大家好,我是橘长,昨天分享了 「OAuth 2.0 中的授权许可协议 」,大家可以自行回顾一下具体是哪四种,每一种的特点、适用的业务场景,最完备的授权码许可机制的流程是什么。
今天橘长给大家分享「 令牌机制 」。
一、OAuth 2.0 的核心是什么?
回顾之前 OAuth 第一篇文章中用户访问客户的故事,有一个关键名词临时访问二维码
当用户出示一系列信息和操作之后拿到这个二维码,它就可以去见到客户了。
这个二维码回到 OAuth 中,其实就是令牌,那么具体来看令牌到底是什么呢?
其实就是授权之后的结果,前面做的一系列操作都是为了获取最终这个令牌,有了令牌凭据,才能去访问这个令牌能请求的路由、服务、资源等。
可见 OAuth 2.0 的核心就是颁发访问令牌,使用访问令牌。
二、OAuth 2.0 中的令牌
OAuth 官网没有限定令牌的格式,但目前只支持一种,就是 bearer 令牌。
可以是随机字符串,也可以是自定义的数据结构,但都要符合三个条件。
唯一性、不连续、不可猜。
三、JWT
1、是什么
JSON Web Token 是一种基于 RFC7519 协议的开放标准,定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全的传输数据。
简单理解:其实是一组自定义的结构化数据,通过结构化的方式去生成 token
一方面是使得返回给客户端的数据有意义
另外一方面是自编码能力,扩展性强
此外还可以通过签名来保护数据安全,JWT 是需要在 HTML 和 HTTP 等环境下传输的
一方面是需要做 URLEncode(编码)保障不乱码且不丢失数据
另外一方面是做加密,避免失窃
官方文档:jwt.io/introductio…
橘长对其做了翻译:github.com/AFlymamba/t…
2、组成
内部包含了三部分,头部、有效负荷、签名,三部分之间通过 .(点) 连接
如下是一个简单的 jwt 示例:
1 | css复制代码header.payload.signature |
1)header(头部)
JWT 的第一部分是 Header(头部),通常由两部分组成,type 和 sign
type 表示令牌类型,在此自然是”JWT“
此外 sign 表示采用的签名算法,可以是 HMAC SHA256 或 RSA 等
如下是一个简单示例:
1 | json复制代码{ |
最后生成的头部是需要把这一串 JSON 串做 URLEncode 操作,得到一个字符串。
1 | ini复制代码JSONObject headerJson = JSONUtil.createObj(); |
- payload(有效负荷)
JWT 的第二部分是 Payload(有效负荷),这是 JWT 最有价值的一部分
其中包含声明(Claims),声明是关于实体(通常是用户)和其他数据的声明
简单理解就是自定义的那部分数据。
JWT 官方有三种类型的声明,分别是 注册声明、公开声明、私有声明。
注册声明:一组框架预定义的声明,非强制性要求每个都实现,但建议使用,通过这个可以提供一组有用的、可操作的数据。
公开声明:JWT 使用者自定义的,但是需要注意不要和官方提供的数据相冲突。
私有声明:适用场景是数据传输,当通信双方约定好数据结构之后,做共享数据用。
【注意】Payload 的声明只有三个字符长度,符合 紧凑型 特点。
特别要来学习官方推荐的:注册声明
挑选了场景的一些数据定义做示例
iss:issuer(JWT 的发行者)
exp:expression time(JWT 过期时间)
sub:subject(主体)
aud:audience(受众)
如下给出一个 payload 的 json 结构:
1 | json复制代码{ |
最后生成的 payload 是需要把这一串 JSON 串做 URLEncode 操作,得到一个字符串。
1 | ini复制代码 JSONObject payloadJson = JSONUtil.createObj(); |
- signature(签名)
JWT 的第二部分是 Signature(签名),JWT 需要在网络上传输
有了头部和有效负荷携带数据,为了保证安全性,需要做相关加密算法
签名其实就是对 header.payload 数据做加密后的结果。
创建签名前,后续获取到 header、payload、做签名用到的 secret、签名算法,如下是一个生成签名的示例:
1 | ini复制代码String headerStr = base64UrlEncode(header); |
签名作用有哪些?
①保证 header 和 payload 在传输过程中没有被纂改。
②如果签名算法用的是非对称加密(比方 RSA),可以通过验证签名,来确定谁是持有私钥的那方。
整合三部分信息,其实就回到了什么是 JWT,其实就是一个由 header.payload.signature 组成的字符串。
3、如何使用 JWT
假设是用户授权登录场景,当用户登录成功之后服务端会颁发 token 给到客户端
之后用户的每个请求都需要携带 token,如果没携带那么就认为没有凭据,无法访问,如果携带合法合理的,放行。
思考:token 的提交方式?
OAuth 官方有三种方式,如下
方式一:Form-Encoded Body Parameter(表单参数)
方式二:URI Query Parameter(URI查询参数)
方式三:Authorization Request Header(授权请求头部字段)
4、JWT 的优缺点
万物都有两面性。
优点:
1)相比随机字符串,具有含义(自编码能力)
2)加密,保护了 header、payload 传输过程被盗窃
缺点:
最大缺点:覆水难收
JWT 从服务端颁发之后,在有效期内其实是横冲直撞的,它不存储在服务端,很难改变 JWT 的状态。
切忌在代码中不要开 api 提供给测试同事获取token,如果要获取,开发手动生成。
四、开源组件
1、为什么选用开源
JWT 中的 header、payload 都需要做 base64URLEncode 操作,为了保证传输过程的安全,还需要引入加密算法做签名,如果自行手写极容易出错,同时安全风险高。
开源组件有一个特点那就是开箱即用,它屏蔽了底层复杂的具体实现(比方说封装了 Base64 操作、对称/非对称一系列算法实现等),让开发者更专注于上层 api 调用和业务实现。
在 JWT 这块选用开源组件,我们会比较关心两块,一块是 api 层面,另外一块自然就是开源活跃度。
api 层面:是否提供了 生成 JWT 的接口、校验 JWT 的接口等。
开源活跃度:说明出了 bug 有处可寻。
2、JJWT
以下示例说明在 SpringBoot 中如何简单使用 JJWT:
1)引入依赖
1 | xml复制代码<!-- jwt --> |
2)生成 JWT
1 | javascript复制代码# 声明 |
3)使用 JWT
这里用 Authorization Header 头部的方式提交 jwt
4)校验 JWT
①自定义 SpringMVC 拦截器,在 preHandler 方法中做拦截
- 自定义拦截器
1 | java复制代码public class TokenInterceptor implements HandlerInterceptor { |
- 注入 Spring 容器
1 | typescript复制代码@Component |
②校验不通过的时候,后端响应 401
1 | json复制代码{ |
③校验通过,进入业务接口
五、总结
今天橘长带大家分析了 OAuth 2.0 的核心 以及 令牌机制,大家需要记住几点:
1、OAuth 2.0 的核心是颁发令牌,使用访问令牌。
2、令牌组成、payload 中的三种声明,JWT 的优缺点。
3、开源 JWT 组件 JJWT 的使用。
下一篇橘长将给大家带来「 手把手带你落地实现 OAuth 2.0 中的四大角色」的解读,感谢你的关注,如果你觉得有所收益,欢迎点赞、转发、评论,感谢认可!
本文转载自: 掘金