无状态认证神器:JWT

说起身份认证,可能大家的第一反应就是Session认证,其通过在服务端保存一份用户登录信息,并返回一个SessionId保存在Cookie中,这样在下次请求时就可以通过SessionId来识别用户。

我们通常会采用Spring-Session-Data-Redis组件来实现,正常使用情况下不会有任何问题,但是如果服务达到每秒万级别的调用量时,如何解决应用系统与Redis的远程调用开销导致的响应能力下降?如果Redis发生异常中断服务时,如何来保证认证服务?这些都值得我们去思考,是不是可以去中心化呢,是不是可以将认证无状态化呢?

答案肯定是可以的,那么就有请今天的主角,一款无状态认证神器:JWT。

==========================================================

什么是JWT:(WHAT)

JSON Web Token(以下简称 JWT)是一套开放的标准(RFC 7519),它定义了一套简洁(compact)且 URL 安全(URL-safe)的方案,以安全地在客户端和服务器之间传输 JSON 格式的信息。

什么场景用JWT:(WHEN)

最常见的场景为单点认证场景,即用户认证后会获取专属的JWT,那么在后续的每个请求中都将会携带JWT,然后服务端可通过JWT来识别用户身份,同时也可根据JWT中的信息来控制路由、服务和资源。

为什么用JWT:(WHY)

JWT 的设计契合无状态原则,无状态是 RESTful 架构设计的一个非常重要的原则,每一个请求都是独立的,用户获取的JWT将会被保存在本地,因此可有效降低服务端性能压力以及健壮性,同时还可以支持跨域验证;

==========================================================

JWT的工作原理:

无状态认证神器:JWT

  1. 用户端提交账号/密码请求登录
  2. 服务端验证通过后生成JWT
  3. 返回JWT到浏览器并保存
  4. 用户端携带JWT请求服务资源
  5. 服务端验证JWT获取服务资源
  6. 返回服务资源信息到用户端

==========================================================

JWT的结构组成:

JWT由三部分信息组成,它们之间用“.”连接,第一部分为头部(Header),第二部分为载荷(Payload),第三部分为签名(Signature)

// Header:对象里有一个值为 “JWT” 的 typ 属性,以及 alg 属性,值为 HS256,表明最终使用的HASH算法是SHA256

{

  "alg": "HS256",

  "typ": "JWT"

}

// Payload,附加元数据,包含我们想要传输的信息,以及用于服务器验证的信息,信息一般分为标准、公共、私有类型

{

  "jti": "123456789",

  "iat": "1595300394"

  "nbf": "1595300394",

  "exp": "1595386793"

  "sub": "test"

}

Payload声明可参考如下

https://www.iana.org/assignments/jwt/jwt.xhtml

// Signature,Header指定的算法HS256产生,该算法有两个参数,第一个参数是经过base64UrlEncode分别编码的Header及Payload并通过“.”连接组成的字符串,第二个参数是**或者可以叫做“盐”,由服务器保存

Signature=(HS256(base64UrlEncode(Header) + "." + base64UrlEncode(Payload), secretKey))

 

//JWT,最终由以上三个部分进行组合

JWT=base64UrlEncode(Header) + "." + base64UrlEncode(Payload) + "."+Signature

==========================================================

JWT的升级加固:

JWT只是提供了一种身份认证的思路,但是不代表JWT没有缺点或问题,所以使用JWT前我们应该结合实际的业务场景以及系统设计来综合考虑,并在原有的JWT上进行一定的升级加固后再运用到实际生产上,一般我们需要考虑如下几个情况:

  1. JWT是否包含敏感信息,默认情况下我们仅仅只会对Payload进行base64UrlEncode,但信息一旦被拦截后仍然可以进行Decode,导致信息泄漏;
  2. JWT是否需要续签,用户在长时间使用后出现JWT过期则需要重新再进行身份认证,会降低客户体验;
  3. JWT是否需要注销,用户进行登出或者签退后,其实JWT将仍然有效,可能会存在JWT泄漏的安全问题;

 

那么针对以上这几个情况,我们需要对JWT进行升级加固来提升JWT的服务能力:

  1. 选择SM国密算法对JWT进行签名/加密保护,将敏感信息通过随机生成SM4进行加密,同时对SM4进行SM2公钥进行加密保护植入Payload,而Signature则通过SM2私钥进行签名;
  2. 在Payload中增加刷新时间并结合过期时间实现JWT续签能力,每次请求会检查过期时间和当前时间,若超过过期时间则自动进行JWT续签,若超过刷新时间则提示重新登录;
  3. 注销场景增加JTW黑名单机制,一旦注销时会将用户标识和注销时间记录在注销缓存中,每次请求会检查是否命中注销缓存,若命中则再判断签发时间是否小于等于缓存的注销时间,若符合则提示重新登录;

==========================================================

总结:JWT虽然是一款解决身份识别的无状态认证神器,但不合理的使用反而会将应用架构变得更复杂,并引入新的安全问题,其实在绝大多数情况下,传统的Session机制完全可以满足实际需要。因此,在应用架构上不要过分设计,更不要滥用技术,它是一种反模式,我们需要在实际的工作中及时辨认以及预防它的出现,这样才能码出高效。

 

注:文章均为原创,翻版必究,图片来源于网络。