当前位置 : 首页 » 文章分类 :  开发  »  JWT(JSON Web Token)

JWT(JSON Web Token)

JWT笔记

JWT(JSON Web Tokens), 是一个开放的标准, 规定了一种 Token 实现方式, 以 JSON 为格式, 相应的 RFC 文件为: JSON Web Token (JWT)

使用JWT保护你的Spring Boot应用 - Spring Security实战
https://segmentfault.com/a/1190000009231329


传统session/cookie认证的缺点

用户认证一般流程是下面这样:
1、用户向服务器发送用户名和密码。
2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
3、服务器向用户返回一个 session_id,写入用户的 Cookie。
4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。

如何 session 共享?
1、一种解决方案是 session 数据持久化,写入数据库、redis或别的持久层。各种服务收到请求后,都向持久层请求数据。
2、另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。


JWT 的构成

一个 JWT 实际上就是一个字符串,它由三部分组成,头部、载荷与签名。

JWT 的结构分为三个部分:
头部Header: 存放Token类型和加密的方法
载荷Payload: 包含一些用户身份信息.
签名Signature: 签名是将前面的Header、Payload信息以及一个密钥组合起来并使用Header中的算法进行加密

最终生成的是一个有两个 . 号连接的字符串, 前两个部分是 Header 和 Payload 的 Base64URL 编码, 最后一个是签名, 例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzZXNzaW9uIjoiOWQ2NDg4M2VhYjgzNDMyYWFiMGVhMThlOGFiMjZhYzgiLCJleHAiOjE1NTc0NzE2NjcsImFwcF9pZCI6IjEwMDA0MSJ9.PokfAbXvl6xToWLU5IzfBaa7Yb1n0VVz1aqnMkC6NHY

这个例子中,头部经 base64URL 解码后是:

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

载荷经 base64URL 解码后是:

{"session":"9d64883eab83432aab0ea18e8ab26ac8","exp":1557471667,"app_id":"100041"}

头部 Header

jwt 的头部承载两部分信息:
1、声明类型, 这里是jwt
2、声明加密的算法, 通常直接使用 HMAC SHA256 HS256
例如:

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

然后将头部进行 base64URL 编码, 构成了第一部分

载荷 Payload

载荷就是存放有效信息的地方。
有效信息可能包含三个部分:
1、标准中注册的声明
标准中注册的声明包括如下,建议但不强制使用

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,是一个Unix时间戳, 这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

2、公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密

3、私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为 base64URL 是对称解密的,意味着该部分信息可以归类为明文信息

例如上例中的:

{"session":"9d64883eab83432aab0ea18e8ab26ac8","exp":1557471667,"app_id":"100041"}

然后将载荷进行 base64URL 加密(该加密是可以对称解密的),构成了第一部分。

注意:由于头部和载荷数据可以 base64URL 解码看到,其实相当于是明文,所以,在JWT中,不应该在头部和载荷里加入任何敏感数据

签名 Signature

jwt 的第三部分是一个签名信息,签名的计算方式为:
将 base64URL 加密后的 header 和 base64URL 加密后的 payload 用.连接组成字符串,然后通过 header 中声明的加密算法进行加盐 secret 组合加密,就生成了 JWT 的第三部分签名。
伪码如下:

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret');

注意:secret 是保存在服务器端的,jwt 的签发生成也是在服务器端的,secret 用来进行 jwt 的签发和验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个 secret, 那就意味着客户端是可以自我签发jwt了。

将 头部、载荷、签名三部分用 . 连接成字符串,就构成了完整的JWT。

认证中心有 secret,签发 jwt 给客户端
服务端也有 secret,收到客户端请求后,再用前两部分计算一遍签名和第三部分比较,相同才继续解析 payload 并处理用户请求,不同就认为 jwt 非法,是伪造的


Base64URL 编码

前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符 +, /=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-/替换成_ 。这就是 Base64URL 算法。


JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

Authorization: Bearer <token>

另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

JWT的Java使用 (JJWT)
https://blog.csdn.net/qq_37636695/article/details/79265711

OAuth,Token和JWT
https://www.jianshu.com/p/9f80be6ba2e9

JSON Web Token - 在Web应用间安全地传递信息
https://blog.leapoahead.com/2015/09/06/understanding-jwt/

JSON Web Token 入门教程 - 阮一峰
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

讲真,别再使用JWT了!
https://juejin.im/entry/5993a030f265da24941202c2


上一篇 SLF4J

下一篇 Spring-Web

阅读
评论
1.6k
阅读预计6分钟
创建日期 2019-01-11
修改日期 2019-05-16
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论