基于M实现的JWT解决方案

简介: 基于M实现的JWT解决方案

基于 M实现的 JWT解决方案

简介

JWT 英文名是 Json Web Token ,是一种用于通信双方之间传递安全信息的简洁的、URL安全的表述性声明规范,经常用在跨域身份验证。

JWT 以 JSON 对象的形式安全传递信息。因为存在数字签名,因此所传递的信息是安全的。

现状

在讲解 JWT 之前我们先来看一个问题。我们都知道互联网服务的身份验正过程是这样的,客户端向服务器发送登录名和登录密码,服务器验证后将对应的相关信息保存到当前会话中,这些信息包括权限、角色等数据。

服务器向客户端返回 Session ,Session 信息都会写入到客户端的 Cookie 中,后面的请求都会从 Cookie 中读取 Session 发送给服务器,服务器在收到 Session 后会对比保存的数据来确认客户端身份。

但是上述模式存在一个问题,无法横向扩展。在服务器集群或者面向服务且跨域的结构中,需要数据库来保存 Session 会话,实现服务器之间的会话数据共享

在单点登录中我们会遇到上述问题,当有多个网站提供同一拨服务,那么我们该怎么实现在甲网站登陆后其他网站也同时登录呢?

其中一种方法是持久化 Session 数据,也就是上面所说的将 Session 会话存到数据库中。这个方法的优点是架构清晰明了。

但是缺点也非常明显,就是架构修改很困难,验证逻辑需要重写,并且整体依赖于数据库,如果存储 Session 会话的数据库挂掉那么整个身份认证就无法使用,进而导致系统无法登录。要解决这个问题我们就用到了 JWT 。

原理

客户端身份经过服务器验证通过后,会生成带有签名的 JSON 对象并将它返回给客户端。客户端在收到这个 JSON 对象后存储起来。

在以后的请求中客户端将 JSON 对象连同请求内容一起发送给服务器,服务器收到请求后通过 JSON 对象标识用户,如果验证不通过则不返回请求的数据。

验证不通过的情况有很多,比如签名不正确、无权限、过期等。在 JWT 中服务器不保存任何会话数据,使得服务器更加容易扩展。

Base64URL 算法

在讲解 JWT 的组成结构前我们先来讲解一下 Base64URL 算法。这个算法和 Base64 算法类似,但是有一点区别。

我们通过名字可以得知这个算法使用于 URL 的,因此它将 Base64 中的 + 、/、 = 三个字符替换成了 - 、_ , 删除掉了 = 。因为这个三个字符在 URL 中有特殊含义。

基于M实现的方法:

ClassMethod Base64UrlEncryption(str As %String)
{
  s ret = $zcvt(str, "O", "UTF8")
  s ret = ##class(%SYSTEM.Encryption).Base64Encode(ret, 1)
  s ret = ..ConvertUrl(ret)
  q ret
}

ClassMethod ConvertUrl(str)
{
  q $tr(str, "+/=", "-_")
}

JWT 组成结构

JWT 是由三段字符串和两个 . 组成,每个字符串和字符串之间没有换行(类似于这样:abc.def.xyz),每个字符串代表了不同的功能,我们将这三个字符串的功能按顺序列出来并讲解:

头部Header

JWT 头描述了 JWT 元数据,是一个 JSON 对象,它的格式如下:

{
  "alg": "HS256",
  "typ": "JWT"
}

这里的 alg 属性表示签名所使用的算法,JWT 签名默认的算法为 HMAC SHA256 , alg 属性值 HS256 就是 HMAC SHA256 算法。typ 属性表示令牌类型,这里就是 JWT。

基于M实现的方法:

ClassMethod GenerateHeaderPart()
{
  s header = {}
  s header.alg = "HS256"
  s header.typ = "JWT"
  s headerPart = ..Base64UrlEncryption(header.%ToJSON())
  q headerPart
}
USER>w ##class(M.Jwt).GenerateHeaderPart()
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

有效载荷Payload

有效载荷是 JWT 的主体,同样也是个 JSON 对象。

JWT指定七个默认字段供选择,也可以省略,一般建议使用。包括以下内容:

  • iss:jwt的签发者/发行人
  • sub:主题
  • aud:接收方
  • exp:jwt过期时间
  • nbf:jwt生效时间
  • iat:签发时间
  • jti:jwt唯一身份标识,可以避免重放攻击

也可以自定义字段:

{
  "name": "yx",
  "code": "yaoxin",
  "desc": "engineer",
}

下面这个代码段就是定义了一个有效载荷:

{
  "iss": "yx",
  "jti": "78006423",
  "exp": "2023-01-15 09:30:04",
  "nbf": "2023-01-08 09:30:04",
  "iat": "2023-01-08 09:30:04"
}

基于M实现的方法:

Parameter Sign = "yx";

ClassMethod GeneratePayloadPart()
{
  s payload = {}
  s payload.iss = ..#Sign
  s payload.jti = ##class(%SYSTEM.Encryption).GenCryptToken()
  s payload.exp = $zd(($h + 7), 3) _ " " _$zt($p($h, ",", 2), 1)
  s payload.nbf = $zdt($h, 3)
  s payload.iat = $zdt($h, 3)
  s payloadPart = ..Base64UrlEncryption(payload.%ToJSON())
  q payloadPart
}
USER>w ##class(M.Jwt).GeneratePayloadPart()
eyJpc3MiOiJ5eCIsImp0aSI6IjYzMjg1OTgyIiwiZXhwIjoiMjAyMy0wMS0yNSAwOTozNTozMCIsIm5iZiI6IjIwMjMtMDEtMTggMDk6MzU6MzAiLCJpYXQiOiIyMDIzLTAxLTE4IDA5OjM1OjMwIn0

哈希签名Signature

哈希签名的算法主要是确保数据不会被篡改。它主要是对前面所讲的两个部分进行签名,通过 JWT 头定义的算法生成哈希。哈希签名的过程如下:

  1. 指定密码,密码保存在服务器中,不能向客户端公开。
  2. 使用JWT 头指定的算法进行签名,进行签名前需要对 JWT 头Header和有效载荷payload进行 Base64URL 编码,结果之间需要用 . 来连接。

示例如下:

HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

基于M实现的方法:

Parameter DOT = ".";

Parameter Key = 00000000111111112222222233333333;

ClassMethod GenerateSignaturePart(headerPart, payloadPart)
{
  s content = headerPart _ ..#DOT _ payloadPart
  s signaturePart = ..HMACSHABase64Encode(content, ..#Key)
  q signaturePart
}

JWT完整结果

JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串。

Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

基于M实现的方法:

ClassMethod GenerateJwt()
{
  s headerPart = ..GenerateHeaderPart()
  s payloadPart = ..GeneratePayloadPart()
  s signaturePart = ..GenerateSignaturePart(headerPart, payloadPart)
  s jwt = headerPart _ ..#DOT _ payloadPart _ ..#DOT _ signaturePart
  q jwt
}
USER>w ##class(M.Jwt).GenerateJwt()
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ5eCIsImp0aSI6IjA0MzYzNzA1IiwiZXhwIjoiMjAyMy0wMS0yNSAwOTo0ODowMSIsIm5iZiI6IjIwMjMtMDEtMTggMDk6NDg6MDEiLCJpYXQiOiIyMDIzLTAxLTE4IDA5OjQ4OjAxIn0.XxgI9PG8AznJIyS6neze2UC4XNG-V0dP46pkp_JNhBY

可以在jwt.io(地址:https://jwt.io/)上验证一下:

JWT基于M的使用流程

  1. 首先创建后端门面接口,用户处理通用权限校验和接收反射需要调用的方法:
Class M.Broker Extends (%CSP.Page, %CSP.REST)
{
/// desc:返回页面数据方法
ClassMethod OnPage() As %Status
{
  s pClassName = $g(%request.Data("ClassName",1))
  s pMethodName = $g(%request.Data("MethodName",1)) 
  
  /* 输出返回值到页面上 */
  w ..RunMethod(pClassName, pMethodName)
  q $$$OK
}

/// desc:运行通用方法
ClassMethod RunMethod(pClassName, pMethodName) [ ProcedureBlock = 0 ]
{
  s $zt = "Error"
  d ..GetInfo()
  
  q:(pMethodName'="Login")&&('$d(%request.CgiEnvs("HTTP_AUTHORIZATION"))) ..Failure2Json("请求头中AUTHORIZATION不存在!")
  
  q:(pClassName = "") ..Failure2Json("类不能为空!")
  q:(pMethodName = "") ..Failure2Json("方法不能为空!")
  
  d:(pMethodName'="Login") ##class(M.Jwt).VerifyJwt()
  
  
  s requestMethod = $lb("POST", "GET")
  q:('$lf(requestMethod, %request.CgiEnvs("REQUEST_METHOD")))
  
  try {
    
    s result = $classmethod(pClassName, pMethodName)
    
  } catch e {
    
    s msg = e.Name _ e.Location _ " *" _ data _ $ze
    ret ..Failure2Json(msg)
  }
  
    q ..Success2Json(result)
Error 
  s $zt = ""
  ret ..Failure2Json("意外没有捕捉错误的错误:" _ $ze)
}
}
  1. 模拟客户端使用用户名和密码请求登录返回Jwt。服务端收到请求,验证用户名和密码。验证成功后,服务端会签发一个 JWT token,再把这个JWT token返回给客户端。

后端登录方法:

Class M.Jwt Extends %RegisteredObject
{

ClassMethod Login()
{
  #; todo 验证用户密码信息
  #; 返回JWT
  q ..GenerateJwt()
}

}

使用PostMan模拟接口登录:

{
    "code": 200,
    "msg": "",
    "data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ5eCIsImp0aSI6IjQ3MDY0MDQ3IiwiZXhwIjoiMjAyMy0wMS0yNSAxMDoyMzo1NCIsIm5iZiI6IjIwMjMtMDEtMTggMTA6MjM6NTQiLCJpYXQiOiIyMDIzLTAxLTE4IDEwOjIzOjU0In0.IcYRp5BqGtslB94lNYmgIrvIwuyne347HjmHkNmAceo"
}
  1. 客户端收到token后可以把它存储起来,比如放到sessionStorage中。客户端每次向服务端请求资源时需要携带服务端签发的JWT token,可以在header中携带。

使用PostMan模拟请求数据:

  • 把请求到的JWT信息放到 PostMan -> Authorization -> TYPE = Bear Token -> 设置Token 中。
  • 前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)。

  1. 服务端收到请求,然后去验证客户端请求里面带着的JWT token,如果验证成功,就向客户端返回请求数据。

解析验证JWT后端方法:

ClassMethod VerifyJwt()
{
  #; 验证是否携带JWT
  q:'$d(%request) ""
  q:'$d(%request.CgiEnvs("HTTP_AUTHORIZATION")) $$$JwtException("请求头中AUTHORIZATION不存在!")
  
  #; 获取JWT
  s token = %request.CgiEnvs("HTTP_AUTHORIZATION")
  s jwt = $p(token," " ,2)
  s headerPart = $p(jwt, ".", 1)
  s payloadPart = $p(jwt, ".", 2)
  s signaturePart = $p(jwt, ".", 3)
  
  #; 解析header部分
  s header = ..Base64Decryption(headerPart)
  s headerObj = {}.%FromJSON(header)
  
  #; 解析payload部分
  s payload = ..Base64Decryption(payloadPart)
  s payloadObj = {}.%FromJSON(payload)
  
  #; 解析重新验签判断数据是否被修改过
  s content = headerPart _ ..#DOT _ payloadPart
  s signatureConetent = ..HMACSHABase64Encode(content, ..#Key)
  q:(signatureConetent '= signaturePart) $$$JwtException("签名校验异常")
  
  #; 其他验证信息,根据需求自行判断
  q:(payloadObj.iss '= ..#Sign) $$$JwtException("IIS发行人校验异常")
  s exp = payloadObj.exp
  #; 这里只计算了日期,需要对比时间根据需要自行判断
  q:(+$zdth(exp, 3) < +$h) $$$JwtException("JWT已经过期")
   
  q $$$OK
}

后端数据方法:

ClassMethod Data()
{
  q $$$OK
}

总结

使用场景:

  • 支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题。
  • 无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力。
  • 更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多。
  • 无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御。
  • 单点登录。

在使用 JWT 时需要注意以下事项:

  1. JWT 默认不加密,如果要写入敏感信息必须加密,可以用生成的原始令牌再次对内容进行加密。
  2. JWT无法使服务器保存会话状态,当令牌生成后在有效期内无法取消也不能更改。
  1. JWT 包含认证信息,如果泄露了,任何人都可以获得令牌所有的权限;因此 JWT 有效期不能太长,对于重要操作每次请求都必须进行身份验证。

完整代码

  • M.Broker.cls
Class M.Broker Extends (%CSP.Page, %CSP.REST)
{

/// 设置为"utf-8",否则中文乱码
Parameter CHARSET = "utf-8";

ClassMethod OnPreHTTP() As %Boolean
{
  #dim %response as %CSP.Response
  #dim %session as %CSP.Session
  #; js是否可读cookie
  #; s %response.UseHttpOnly = 1
  
  
  /* 星号表示所有的域都可以接受 */
  d %response.SetHeader("Access-Control-Allow-Origin", ..GetOrigin())
  
  #; 允许请求方式,例如get。post
  d %response.SetHeader("Access-Control-Allow-Methods", "*")
  
  #; 允许头信息
  d %response.SetHeader("Access-Control-Allow-Headers", "x-requested-with,content-type")
  
  #; 允许验证
  d %response.SetHeader("Access-Control-Allow-Credentials", "true")
  
  #; 额外暴露头信息
  #;d %response.SetHeader("Access-Control-Expose-Headers", "cookie")
  
  #; 超时时间
  d %response.SetHeader("Access-Control-Max-Age", 3600)
  
  /* 设置返回结构为Json */
  d %response.SetHeader("Content-Type", "application/json")
  
  #; 每次请求将Session超时时间重新设置
  s %session.AppTimeout = 900

  if ($zv [ "IRIS") {
    #; 确定是否应该使用 sessionId cookie 发送 "Secure" 标志的内部属性
    s %session.SecureSessionCookie = 1
    
    #; 确定如何严格限制 sessionId cookie 域的属性。选项有 None (0)、Lax (1) 和 Strict (2),其中 Strict 表示 cookie 只能在当前应用程序中使用。默认为 CSP 应用程序的相应设置。除非另有配置,否则应用程序默认为 Strict。请注意,None 对于不安全 (HTTP) 连接无效
    s %session.SessionScope = 0
    
    #; 用于确定要与用户创建的 cookie 一起发送的 SameSite 属性的属性。选项有 None (0)、Lax (1) 和 Strict (2)。默认为 CSP 应用程序的相应设置。除非另有配置,否则应用程序默认为 Strict。请注意,None 对于不安全 (HTTP) 连接无效。
    s %session.UserCookieScope = 0
  }


  q 1
}

/// desc:返回页面数据方法
ClassMethod OnPage() As %Status
{
  s pClassName = $g(%request.Data("ClassName",1))
  s pMethodName = $g(%request.Data("MethodName",1)) 
  
  /* 输出返回值到页面上 */
  w ..RunMethod(pClassName, pMethodName)
  q $$$OK
}

/// desc:运行通用方法
ClassMethod RunMethod(pClassName, pMethodName) [ ProcedureBlock = 0 ]
{
  s $zt = "Error"
  d ..GetInfo()
  
  q:(pMethodName'="Login")&&('$d(%request.CgiEnvs("HTTP_AUTHORIZATION"))) ..Failure2Json("请求头中AUTHORIZATION不存在!")
  
  q:(pClassName = "") ..Failure2Json("类不能为空!")
  q:(pMethodName = "") ..Failure2Json("方法不能为空!")
  
  d:(pMethodName'="Login") ##class(M.Jwt).VerifyJwt()
  
  
  s requestMethod = $lb("POST", "GET")
  q:('$lf(requestMethod, %request.CgiEnvs("REQUEST_METHOD")))
  
  try {
    
    s result = $classmethod(pClassName, pMethodName)
    
  } catch e {
    
    s msg = e.Name _ e.Location _ " *" _ data _ $ze
    ret ..Failure2Json(msg)
  }
  
    q ..Success2Json(result)
Error 
  s $zt = ""
  ret ..Failure2Json("意外没有捕捉错误的错误:" _ $ze)
}

/// 返回消息Json
ClassMethod Msg(code, msg = "", data = "") As %DynamicObject
{
    s ret = {}
    s ret.code = code
    s ret.msg = msg
    s ret.data = data
    q ret
}

/// 无Session判断
ClassMethod Session() As %DynamicObject
{
    q ..Msg(-2, "", "")
}

/// 返回前台的成功消息转Json
ClassMethod Session2Json() As %String
{
    q ..Session().%ToJSON()
}

/// 返回前台的成功消息
ClassMethod Success(data) As %DynamicObject
{
    q ..Msg(200, "", data)
}

/// 返回前台的成功消息转Json
ClassMethod Success2Json(data) As %String
{
    q ..Success(data).%ToJSON()
}

/// 返回前台分页数据的成功消息转Json
ClassMethod Success2Json4Page(data, total) As %String
{
    s ret = ..Success(data)
    d ret.%Set("size" ,$g(%request.Data("size", 1)) ,"number")
    d ret.%Set("current" ,$g(%request.Data("current", 1)) ,"number")
    d ret.%Set("total" ,total ,"number")
    q ret.%ToJSON()
}

/// 返回前台的失败消息
ClassMethod Failure(msg, data = "") As %DynamicObject
{
    q ..Msg(-1, msg, data)
}

/// 返回前台的失败消息转Json
ClassMethod Failure2Json(msg, data = "") As %String
{
    q ..Failure(msg, data).%ToJSON()
}

/// 返回前台的警告消息
ClassMethod Warning(msg, data = "") As %DynamicObject
{
    q ..Msg(0, msg, data)
}

/// 返回前台的警告消息转Json
ClassMethod Warning2Json(msg, data = "") As %String
{
    q ..Warning(msg, data).%ToJSON()
}

ClassMethod GetOrigin()
{
  q:'$d(%request) ""
  q:'$d(%request.CgiEnvs("HTTP_ORIGIN")) ""
  s origin = %request.CgiEnvs("HTTP_ORIGIN")
  q origin
}

ClassMethod GetAuthorization()
{
  q:'$d(%request) ""
  q:'$d(%request.CgiEnvs("HTTP_AUTHORIZATION")) ""
  s token = %request.CgiEnvs("HTTP_AUTHORIZATION")
  q token
}

}

  • M.Jwt.cls
Include M.Jwt

Class M.Jwt Extends %RegisteredObject
{

ClassMethod Login()
{
  #; todo 验证用户密码信息
  #; 返回JWT
  q ..GenerateJwt()
}

ClassMethod Data()
{
  q $$$OK
}

Parameter DOT = ".";

Parameter Sign = "yx";

Parameter Key = 00000000111111112222222233333333;

/// w ##class(M.Jwt).GetJwt()
ClassMethod VerifyJwt()
{
  #; 验证是否携带JWT
  q:'$d(%request) ""
  q:'$d(%request.CgiEnvs("HTTP_AUTHORIZATION")) $$$JwtException("请求头中AUTHORIZATION不存在!")
  
  #; 获取JWT
  s token = %request.CgiEnvs("HTTP_AUTHORIZATION")
  s jwt = $p(token," " ,2)
  s headerPart = $p(jwt, ".", 1)
  s payloadPart = $p(jwt, ".", 2)
  s signaturePart = $p(jwt, ".", 3)
  
  #; 解析header部分
  s header = ..Base64Decryption(headerPart)
  s headerObj = {}.%FromJSON(header)
  
  #; 解析payload部分
  s payload = ..Base64Decryption(payloadPart)
  s payloadObj = {}.%FromJSON(payload)
  
  #; 解析重新验签判断数据是否被修改过
  s content = headerPart _ ..#DOT _ payloadPart
  s signatureConetent = ..HMACSHABase64Encode(content, ..#Key)
  q:(signatureConetent '= signaturePart) $$$JwtException("签名校验异常")
  
  #; 其他验证信息,根据需求自行判断
  q:(payloadObj.iss '= ..#Sign) $$$JwtException("IIS发行人校验异常")
  s exp = payloadObj.exp
  #; 这里只计算了日期,需要对比时间根据需要自行判断
  q:(+$zdth(exp, 3) < +$h) $$$JwtException("JWT已经过期")
   
  q $$$OK
}

/// w ##class(M.Jwt).SetJwt()
ClassMethod GenerateJwt()
{
  s headerPart = ..GenerateHeaderPart()
  s payloadPart = ..GeneratePayloadPart()
  s signaturePart = ..GenerateSignaturePart(headerPart, payloadPart)
  s jwt = headerPart _ ..#DOT _ payloadPart _ ..#DOT _ signaturePart
  q jwt
}

/// w ##class(M.Jwt).GenerateHeaderPart()
/// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0aHJvd3giLCJqaWQiOjEwMDg3LCJleHAiOjE2MTMyMjc0NjgxNjh9.7skduDGxV-BP2p_CXyr3Na7WBvENNl--Pm4HQ8cJuEs
/// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0aHJvd3giLCJqaWQiOjEwMDg3LCJleHAiOjE2MTMyMjc0NjgxNjh9.RkVBMjAwOEYwNzIwRDdENzI0MURCMzUxRjhGMzcyOUI5Mjc3MEYzOENFMjgxQjcyNTdDRTU3QTBEQTJDNTlCMw==
ClassMethod GenerateHeaderPart()
{
  s header = {}
  s header.alg = "HS256"
  s header.typ = "JWT"
  s headerPart = ..Base64UrlEncryption(header.%ToJSON())
  q headerPart
}

/// w ##class(M.Jwt).GeneratePayloadPart()
ClassMethod GeneratePayloadPart()
{
  s payload = {}
  s payload.iss = ..#Sign
  s payload.jti = ##class(%SYSTEM.Encryption).GenCryptToken()
  s payload.exp = $zd(($h + 7), 3) _ " " _$zt($p($h, ",", 2), 1)
  s payload.nbf = $zdt($h, 3)
  s payload.iat = $zdt($h, 3)
  s payloadPart = ..Base64UrlEncryption(payload.%ToJSON())
  q payloadPart
}

/// w ##class(M.Jwt).GenerateSignaturePart()
/// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0aHJvd3giLCJqaWQiOjEwMDg3LCJleHAiOjE2MTMyMjc0NjgxNjh9._qIAjwcg19ckHbNR-PNym5J3DzjOKBtyV85XoNosWbM
ClassMethod GenerateSignaturePart(headerPart, payloadPart)
{
  s content = headerPart _ ..#DOT _ payloadPart
  s signaturePart = ..HMACSHABase64Encode(content, ..#Key)
  q signaturePart
}

ClassMethod Base64UrlEncryption(str As %String)
{
  s ret = $zcvt(str, "O", "UTF8")
  s ret = ##class(%SYSTEM.Encryption).Base64Encode(ret, 1)
  s ret = ..ConvertUrl(ret)
  q ret
}

ClassMethod ConvertUrl(str)
{
  q $tr(str, "+/=", "-_")
}

ClassMethod Base64Decryption(str As %String)
{
  s ret = ##class(%SYSTEM.Encryption).Base64Decode(str)
  q ret
}

/// w ##class(M.Jwt).HMACSHABase64Encode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ5eCIsImp0aSI6IjY2MzkyMTU5IiwiZXhwIjoiMjAyMy0wMS0xOSAxMjoxNDozNCIsIm5iZiI6IjIwMjMtMDEtMTIgMTI6MTQ6MzQiLCJpYXQiOiIyMDIzLTAxLTEyIDEyOjE0OjM0In0","00000000111111112222222233333333")
ClassMethod HMACSHABase64Encode(str As %Text, key As %String) As %String
{
  s str = $zcvt(str, "O", "UTF8")
  s key = $zcvt(key, "O", "UTF8")
  s ret = ##class(%SYSTEM.Encryption).HMACSHA("256", str, key)
  
  s ret = ##class(%SYSTEM.Encryption).Base64Encode(ret, 1)
  s ret = ..ConvertUrl(ret)
  q ret
}

}
  • M.Jwt.inc
#define JwtException(%msg) ##class(M.JwtException).%New("",-100, %msg)

相关文章
|
存储 NoSQL 算法
搞懂单点登录SSO,基于SpringBoot+JWT实现单点登录解决方案
单点登录是目前比较流行的企业业务整合的解决方案之一。单点登录是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。例如:百度旗下有很多的产品,比如百度贴吧、百度知道、百度文库等,只要登录百度账号,在任何一个地方都是已登录状态,不需要重新登录。 单点登录是互联网应用和企业级平台中的基础组件服务。接下来就介绍单点登录的原理,并基于SpringBoot +JWT实现单点登录解决方案。
22215 2
搞懂单点登录SSO,基于SpringBoot+JWT实现单点登录解决方案
|
存储 缓存 前端开发
关于JWT Token 自动续期的解决方案
关于JWT Token 自动续期的解决方案
2386 1
|
JSON 安全 算法
JSON Web Token(缩写 JWT) 目前最流行、最常见的跨域认证解决方案
JSON Web Token(缩写 JWT) 目前最流行、最常见的跨域认证解决方案
1309 0
|
存储 SQL 算法
【案例实战】分布式应用下登录检验解决方案(JWT)
【案例实战】分布式应用下登录检验解决方案(JWT)
【案例实战】分布式应用下登录检验解决方案(JWT)
|
存储 JSON 安全
JSON Web Token(缩写 JWT) 目前最流行、最常见的跨域认证解决方案
JSON Web Token(缩写 JWT) 目前最流行、最常见的跨域认证解决方案
1153 0
JSON Web Token(缩写 JWT) 目前最流行、最常见的跨域认证解决方案
|
算法 安全 Java
【方向盘】通俗易懂版讲解JWT和OAuth2,以及他俩的区别和联系(Token鉴权解决方案)(中)
【方向盘】通俗易懂版讲解JWT和OAuth2,以及他俩的区别和联系(Token鉴权解决方案)(中)
【方向盘】通俗易懂版讲解JWT和OAuth2,以及他俩的区别和联系(Token鉴权解决方案)(中)
|
XML 存储 JSON
JSON Web Token (JWT),服务端信息传输安全解决方案。
JWT介绍 JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑独立的基于JSON对象在各方之间安全地传输信息的方式。这些信息可以被验证和信任,因为它是数字签名的。JWTs可以使用一个密钥(HMAC算法),或使用RSA的公钥/私钥密钥对对信息进行签名。 让我们进一步解释这个定义的一些概念。
1329 0
JSON Web Token (JWT),服务端信息传输安全解决方案。
|
JSON 安全 算法
单点登录SSO解决方案之SpringSecurity+JWT实现
通过前面几天文章我们详细的介绍了SpringSecurity的使用,本文我们来看下,结合JWT来实现单点登录操作。
单点登录SSO解决方案之SpringSecurity+JWT实现
|
JSON 安全 API
【方向盘】通俗易懂版讲解JWT和OAuth2,以及他俩的区别和联系(Token鉴权解决方案)(下)
【方向盘】通俗易懂版讲解JWT和OAuth2,以及他俩的区别和联系(Token鉴权解决方案)(下)
|
JSON 安全 算法
【方向盘】通俗易懂版讲解JWT和OAuth2,以及他俩的区别和联系(Token鉴权解决方案)(上)
【方向盘】通俗易懂版讲解JWT和OAuth2,以及他俩的区别和联系(Token鉴权解决方案)(上)