轻松学会使用JWT,让你的OAuth2.0实现更加安全高效!
授权服务的核心:颁发访问令牌,而OAuth 2.0规范没有约束访问令牌内容的生成规则,只要符合:
- 唯一性
- 不连续性
- 不可猜性
可灵活选择令牌形式:
- 既可是没有内部结构 && 不包含任何信息含义的随机字符串
- 也可是具有内部结构 && 包含有信息含义的字符串
以前生成令牌的方式都是默认一个随机字符串。而结构化令牌,目前用得最多的就是JWT令牌。
1 简介
JSON Web Token(JWT)是一个开放标准(RFC 7519),定义了一种紧凑、自包含的方式,作为JSON对象在各方之间安全地传输信息,结构化封装的方式生成token。结构化后的token可被赋予丰富含义,这是与无意义的随机字符串形式token的最大区别。
2 JWT结构
例如:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
在https://jwt.io/网站中解密得到
2.1 HEADER(头部)
装载令牌类型和算法等信息:
- typ 表示第二部分PAYLOAD是JWT类型
- alg 表示使用HS256对称签名的算法
2.2 PAYLOAD(数据体)
代表一组数据:
- sub 令牌的主体,一般设为资源拥有者的唯一标识
- exp 令牌的过期时间戳
- iat 令牌颁发的时间戳
是JWT规范性的声明,PAYLOAD表示的一组数据允许我们自定义声明。
2.3 SIGNATURE(签名)
签名后的JWT整体结构,是被.
分割的三段内容:header.payload.signature
。JWT令牌直接用肉眼,看起来还是毫无意义,拷贝到 https://jwt.io/ 在线校验,即可看到解码后的有意义数据。
SIGNATURE
表示对JWT信息的签名。
作用
有了HEADER
和PAYLOAD
就可让令牌携带信息在网络中传输,但网络中传输这样的信息体不安全。须加密签名,SIGNATURE
就是对信息的签名结果,当受保护资源接收到三方软件的签名后需要验证令牌的签名是否合法。
3 令牌内检
3.1 定义
既然授权服务颁发令牌,受保护资源服务就要验证令牌。而受保护资源调用授权服务提供的检验令牌的服务的这种校验令牌方式就叫令牌内检。
3.2 特点
有时授权服务依赖DB,然后受保护资源服务也依赖该DB,即“共享DB”。
微服务架构下,不同系统间依靠服务而非DB通信,如【授权服务】给【受保护资源服务】提供一个RPC服务:
JWT令牌本身包含了之前所要依赖DB或依赖RPC服务才能拿到的信息,如某用户为某软件进行授权等信息。
4 JWT实现方案
有JWT令牌后的通信方式:
授权服务发个令牌,受保护资源服务接令牌,然后开始解析令牌所含信息,无需再去查询DB或RPC调用。即实现了令牌内检。
4.1 HMAC 流程
Hash-based Message Authentication Code,基于哈希函数的消息认证码。验证数据完整性和真实性,通常使用一个共享密钥来计算并验证消息认证码。
- 使用Base64算法将header和payload进行编码,并用"."串联成一个字符串
- 使用secret key对上一步得到的字符串进行HMAC签名操作,生成一个签名值
- 将签名值也进行Base64编码,与JWT的header和payload一起组成最终的JWT。
接收方在验证JWT时需按照相同的流程计算签名值并将其与JWT中的签名值进行比较,如果相同则表明JWT是有效的。由于签名值的生成过程需要使用密钥,因此只有持有密钥的人才能正确地计算签名值,从而保证JWT安全性。
4.2 RSA 流程
Rivest-Shamir-Adleman,一种公钥加密算法,也可用于数字签名。
基于两个大质数的乘积难以分解这一数学难题,利用公钥和私钥配对实现信息的加密和解密,广泛应用于网络安全、数字签名、电子商务等领域。
- 使用Base64算法将header和payload进行编码,并用"."串联成字符串
- 使用private key对上一步得到的字符串进行RSA签名操作,生成一个签名值
- 将签名值进行Base64编码,与JWT的header和payload一起组成最终的JWT。
接收方验证JWT时:
- 从JWT解析出签名值
- 使用public key对JWT的header和payload进行RSA验签操作,得到一个验证结果
- 将该结果与JWT中的签名值进行比较,如果相同则表明JWT是有效的
由于私钥只有签发者拥有,因此只有签发者才能正确地给JWT进行签名,而任何人都可以使用公钥进行验签,从而保证了JWT的安全性和可信度。
5 为什么令牌要编码且签名?
授权服务颁发JWT后给到xx软件,xx拿着令牌请求受保护资源服务,即我在公众号里的文章。显然令牌要在公网传输。所以传输过程令牌还要做到:
- 编码,防乱码
- 签名及加密,以防数据信息泄露。
jjwt 开源的JWT工具,封装了Base64URL编码和对称HMAC、非对称RSA的一系列签名算法。使用它可方便生成一个经过签名的JWT令牌及解析一个JWT令牌。
// 密钥
String sharedTokenSecret="hellooauthhellooauthhellooauthhellooauth";
Key key = new SecretKeySpec(sharedTokenSecret.getBytes(),
SignatureAlgorithm.HS256.getJcaName());
// 生成JWT令牌
String jwts=
Jwts.builder()
.setHeaderParams(headerMap)
.setClaims(payloadMap)
.signWith(key,SignatureAlgorithm.HS256)
.compact()
// 解析JWT令牌
Jws<Claims> claimsJws =Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwts);
JwsHeader header = claimsJws.getHeader();
Claims body = claimsJws.getBody();
6 优点
6.1 计算代替存储
时间换空间。这种计算并结构化封装,减少了“共享DB” 因远程调用而带来的网络传输性能损耗,所以可能节省时间。
6.2 加密
因JWT令牌内部已包含重要信息,所以传输过程都必须被要求密文传输,被强制要求加密也保障了传输安全性。
6.3 增强系统可用性和可伸缩性
JWT令牌通过“自编码”包含身份验证所需信息,无需服务端额外存储,所以每次的请求都是无状态会话。符合尽可能遵循无状态架构设计原则,增强了系统可用性和伸缩性。
6.4 降低 AuthServer 压力
客户端获取令牌后,后续资源服务器可做自校验,无需到AuthServer校验。
6.5 简化AuthServer实现
无需对用户状态会话进行维护和管理
7 缺点
7.1 无状态和吊销无法两全
无法在使用过程中修改令牌状态。比如我在使用xx时,可能莫须有原因修改了在公众号平台的密码或突然取消了给xx的授权。这时,令牌状态就该有变更,将原来对应令牌置无效。但使用JWT时,每次颁发的令牌都不会存在服务端,无法改变令牌状态。这表示JWT令牌在有效期内都会畅通无阻。
那可以把JWT令牌存储在一个分布式内存数据库,如Redis吗?NO!这违背JWT意义 - 将信息结构化存入令牌本身。通常有两种方案:
- 将每次生成JWT令牌时的密钥粒度缩小到用户级别,即一个用户一个密钥 如此,当用户取消授权或修改密码,可让该密钥一起修改。这种方案一般还需配套单独密钥管理服务
- 在不提供用户主动取消授权的环境里面,若只考虑修改密码场景,即可把用户密码作为JWT的密钥。这也是用户粒度。这样用户修改密码也就相当于修改了密钥。
7.2 网络传输开销
随 claims 增多而增大。
8 令牌的生命周期
令牌都有【有效期】,只是JWT可将有效期的信息存在自身结构中。
OAuth 2.0的令牌生命周期:
- 令牌自然过期
- 该过程不排除主动销毁令牌的可能,比如令牌被泄露,授权服务可让令牌失效。
- 访问令牌失效后可使用刷新令牌请求新令牌,提高用户使用三方软件的体验。
- 让三方软件比如xx,主动发起令牌失效请求,然后授权服务收到请求后让令牌立即失效。何时需要该机制? 比如用户和三方软件间存在一种订购关系:我购买了xx软件,那么到期或退订时且我授权的token还未到期时,就需这样一种令牌撤回协议,支持xx主动发起令牌失效请求。作为开放平台,有责任的三方软件也应该遵守这样的令牌撤回协议。
9 总结
OAuth 2.0 的核心是授权服务,没有令牌就没有OAuth,令牌表示授权后的结果。令牌在OAuth 2.0系统中对于第三方软件都是不透明的。需要关心令牌的,是授权服务和受保护资源服务。
- JWT默认不加密,但也可加密。生成原始 Token 后,可用密钥再加密一次
- JWT不加密时,不能将秘密数据写入JWT
- JWT不仅可用于认证,也可以用于交换信息。有效使用 JWT,可降低服务器查询数据库的次数
- JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。即一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外逻辑
- JWT本身包含认证信息,一旦泄露,任何人都能获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证
- 为减少盗用,JWT 不应使用 HTTP 协议明码传输,要使用 HTTPS 协议传输
参考
- JSON Web Token 入门教程
- 在OAuth 2.0中,如何使用JWT结构化令牌?
- https://tools.ietf.org/html/rfc6749#section-4.4
文章转载自公众号: JavaEdge