使用JWT结构化令牌

上一篇中提到生成令牌的方式都是默认一个随机字符串。而在结构化令牌这方面,目前用得最多的就是 JWT 令牌了。

JWT结构化令牌

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。

简单理解下,JWT 就是用一种结构化封装的方式来生成 token 的技术。结构化后的 token 可以被赋予非常丰富的含义,这也是它与原先毫无意义的、随机的字符串形式 token 的最大区别。

JWT 这种结构化体可以分为 HEADER(头部)、PAYLOAD(数据体)和 SIGNATURE(签名)三部分。经过签名之后的 JWT 的整体结构,是被句点符号分割的三段内容,结构为 header.payload.signature

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

把这段内容拷贝到 https://jwt.io/ 网站的在线校验工具中去,就可以看到解码之后的数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DYr9ScyT-1616994417493)(http://qiniu.litblue.cn/Snipaste_2020-11-27_14-08-09.png)]

JWT三部分

HEADER

表示装载令牌类型和算法等信息,是 JWT 的头部。其中,typ 表示第二部分 PAYLOAD 是 JWT 类型,alg 表示使用 HS256 对称签名的算法。

PAYLOAD

表示是 JWT 的数据体,代表了一组数据。其中,sub(令牌的主体,一般设为资源拥有者的唯一标识)、exp(令牌的过期时间戳)、iat(令牌颁发的时间戳)是 JWT 规范性的声明,代表的是常规性操作。更多的通用声明,可以参考RFC 7519 开放标准。不过,在一个 JWT 内可以包含一切合法的 JSON 格式的数据,也就是说,PAYLOAD 表示的一组数据允许我们自定义声明。

SIGNATURE

表示对 JWT 信息的签名。我们还需要对其进行加密签名处理,而 SIGNATURE 就是对信息的签名结果,当受保护资源接收到第三方软件的签名后需要验证令牌的签名是否合法。

令牌内检

题外话-“两个一伙”的概念:

OAuth 2.0 中的 4 个角色是 “两两站队” 的:

资源拥有者和第三方软件“站在一起”,因为第三方软件要代表资源拥有者去访问受保护资源;

授权服务和受保护资源“站在一起”,因为授权服务负责颁发访问令牌,受保护资源负责接收并验证访问令牌。

授权服务颁发令牌,受保护资源服务就要验证令牌。同时,授权服务和受保护资源服务通常是“一伙的”。受保护资源来调用授权服务提供的检验令牌的服务,我们把这种校验令牌的方式称为令牌内检。

有时候授权服务依赖一个数据库,然后受保护资源服务也依赖这个数据库,也就是我们说的“共享数据库”。不过,在如今已经成熟的分布式以及微服务的环境下,不同的系统之间是依靠服务而不是数据库来通信了,比如授权服务给受保护资源服务提供一个 RPC 服务。

那么,在有了 JWT 令牌之后,我们就多了一种选择,因为 JWT 令牌本身就包含了之前所要依赖数据库或者依赖 RPC 服务才能拿到的信息。

JWT 是如何被使用的

授权服务“扔出”一个令牌,受保护资源服务“接住”这个令牌,然后自己开始解析令牌本身所包含的信息就可以了,而不需要再去查询数据库或者请求 RPC 服务。

实际上,授权服务颁发了 JWT 令牌后给到了第三方软件,第三方软件拿着 JWT 令牌来请求受保护资源服务。很显然,JWT 令牌需要在公网上做传输。所以在传输过程中,JWT 令牌需要进行 Base64 编码以防止乱码,同时还需要进行签名及加密处理来防止数据信息泄露。

我们可以借助一些开源工具来帮助我们完成处理编码,加密等工作。待会通过案例介绍。

JWT格式令牌的优势

计算代替存储

JWT 的核心思想,就是用计算代替存储。这种经过计算并结构化封装的方式,也减少了“共享数据库” 因远程调用而带来的网络传输消耗,所以也有可能是节省时间的。

加密

JWT 令牌内部已经包含了重要的信息,所以在整个传输过程中都必须被要求是密文传输的,这样被强制要求了加密也就保障了传输过程中的安全性。这里的加密算法,既可以是对称加密,也可以是非对称加密。

增强可用性和可伸缩性

这种 JWT 格式的令牌,通过“自编码”的方式包含了身份验证需要的信息,不再需要服务端进行额外的存储,所以每次的请求都是无状态会话。这就符合了我们尽可能遵循无状态架构设计的原则,也就是增强了系统的可用性和伸缩性。

JWT格式令牌的劣势

JWT 格式令牌的最大问题在于 “覆水难收”,也就是说,没办法在使用过程中修改令牌状态

用户在使用第三方软件的时候,是不是有可能出现修改密码或取消授权的操作?这时候,令牌的状态是不是就要有相应的变更,将原来对应的令牌置为无效。但,使用 JWT 格式令牌时,每次颁发的令牌都不会在服务端存储,这样我们要改变令牌状态的时候,就无能为力了。因为服务端并没有存储这个 JWT 格式的令牌。这就意味着,JWT 令牌在有效期内,是可以“横行无止”的。为了解决这个问题,我们可以把 JWT 令牌存储到远程的分布式内存数据库中吗?显然不能,因为这会违背 JWT 的初衷(将信息通过结构化的方式存入令牌本身)。因此,我们通常会有两种做法:

  • 一是,将每次生成 JWT 令牌时的秘钥粒度缩小到用户级别,也就是一个用户一个秘钥。这样,当用户取消授权或者修改密码后,就可以让这个密钥一起修改。一般情况下,这种方案需要配套一个单独的密钥管理服务;
  • 二是,在不提供用户主动取消授权的环境里面,如果只考虑到修改密码的情况,那么我们就可以把用户密码作为 JWT 的密钥。当然,这也是用户粒度级别的。这样一来,用户修改密码也就相当于修改了密钥。

令牌的生命周期

具体到 OAuth 2.0 的令牌生命周期,通常会有三种情况。

自然过期

这是最常见的情况。这个过程是,从授权服务创建一个令牌开始,到第三方软件使用令牌,再到受保护资源服务验证令牌,最后再到令牌失效。

同时,这个过程也不排除主动销毁令牌的事情发生,比如令牌被泄露,授权服务可以做主让令牌失效。

刷新令牌

访问令牌失效之后可以使用刷新令牌请求新的访问令牌来代替失效的访问令牌,以提升用户使用第三方软件的体验。(上一篇文章中有较详细的说明)

主动发起令牌失效请求

就是让第三方软件主动发起令牌失效的请求,然后授权服务收到请求之后让令牌立即失效。但是这种应用场景并不常见,比如有些时候,用户和第三方软件之间存在一种订购关系。比如用户购买了第三方软件,那么在订购时长到期或者退订,且用户授权的 token 还没有到期的情况下,就需要有这样的一种令牌撤回协议,来支持第三方软件软件主动发起令牌失效的请求。

Spring Boot整合JWT实战

代码实践直接放在了GitHub仓库中:springboot-jwt/

Spring Security(含OAuth2三角色+三模式完整例子)

这个直接看朱晔老师写的文章:朱晔和你聊Spring系列S1E10:强大且复杂的Spring Security(含OAuth2三角色+三模式完整例子)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值