目录
一、什么是 JWT ?
二、什么时候使用 JWT ?
三、JWT 格式
1、Header
2、Payload
3、Signature
4、 JWT实现:
官网
- 官网JSON Web Tokens – jwt.io
- RFC 7519文档RFC 7519: JSON Web Token (JWT)
一、什么是 JWT ?
JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。
JWT可以使用密码(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
加签后的token 能够使用 JWT 里的算法验证 json 的完整性.
二、什么时候使用 JWT ?
- 授权
- 信息交换
- 使用方式:服务端根据规范生成一个令牌(token),并且发放给客户端(保存在客户端)。此时客户端请求服务端的时候就可以携带者令牌,以令牌来证明自己的身份信息。
- 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
JWT优势
简洁:可以通过URL、POST参数或者在HTTP Header发送,因为数据量小,传输速度也很快
自包含:负载中包含了所有用户所需要的信息,避免了多次查询数据库
因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式 都支持
不需要在服务端保存会话信息,特别适合用于分布式微服务。
更适合用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时使用token认证方 式会简单很多
单点登录友好:由于cookie无法跨域,难以实现单点登录。但是,使用token进行认证的话,token可以被保存在客户端的任意位置的内存中,不一定是cookie,所以不依赖cookie,不会 存在这些问题
三、JWT 格式
使用逗号分隔的三部分 :
- Header
- Payload
- Signature
token 格式:xxxxx.yyyyy.zzzzz
1、Header
Header 通常由 token 类型和签名算法名两部分组成.是token的第1部分
例如:
{ | |
"alg": "HS256", // 签名算法 | |
"typ": "JWT" // token类型 | |
} |
然后, 这个JSON 使用 Base64Url 编码后放到 JWT 的第1部分.
2、Payload
Payload 是token的第2部分.包含了一些声明(claims).声明的名字必须是唯一的.
claims 是包含了 用户数据和其他数据的陈述,
有三种类型的声明:
- Registered Claim Name
预定义好的一些声明(如果有需要就使用,没需要可不使用):
- “iss”
- “sub”
- “aud”
- “exp”
- “nbf”
- “iat”
- “jti”
更多参见JSON Web Token (JWT)
Public Claim Names
公共的声明,可以预先定义在IANA JSON Web Token Registry中,或者定义在1个能解决名字冲突的地方.Private Claim Names
双方共享数据使用的私有名字.既不在 Registered Claim Name 也不在 Public Claim Names 中.
payload 示例
{ | |
"sub": "1234567890", | |
"name": "John Doe", | |
"admin": true | |
} |
JSON
然后, 这个JSON 使用 Base64Url 编码后放到 JWT 的第2部分.
3、Signature
拿到编码后的 header 和 编码后的 payload 使用 密码进行签名.
使用 HMAC SHA256 加签示例:
HMACSHA256( |
base64UrlEncode(header) + "." + |
base64UrlEncode(payload), |
secret) |
Signature需要使用编码后的header和payload以及我们提供的一个秘钥,然后使用header中指定的签名算法进行签名,签名的作用是保证JWT没有被篡改过
HMACSHA256(base64UrlEncode(header)+“.”+base64UrlEncode(payload),secret)
实际上是对头部信息和负载内容进行签名,防止内容被篡改,如果有人对头部以及负载内容解码后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT上附带的签名是不一样的。如果要对新的头部和负载进行签名,由于不知道服务器加密时使用的秘钥,得出来的结果也是不一样的
注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。
4、 JWT实现:
1、依赖引入
io.jsonwebtokenjjwt${jwt-jsonwebtoken.version}com.alibabafastjsonRELEASE
2、生成Token
// 签名密钥 private static final String SECRET = "!Doker$";public String createToken(Map claims, String subject) {final Date createdDate = clock.now();final Date expirationDate = calculateExpirationDate(createdDate);return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(createdDate).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, Algorithm.HMAC256(SECRET)).compact();}private Date calculateExpirationDate(Date createdDate) {return new Date(createdDate.getTime() + expiration * 1000);}
3、刷新Token
public String RefreshToken(String token) {final Date createdDate = clock.now();final Date expirationDate = calculateExpirationDate(createdDate);final Claims claims = getAllClaimsFromToken(token);claims.setIssuedAt(createdDate);claims.setExpiration(expirationDate);return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, Secret).compact();}
4、token发送给前端
传入当前用户的功能与用户信息,登录名生成token,写入response的返回头中,前端获取后保存在前端的本地缓存中,后续前端请求要把token放在头header里。
//登录成功之后List
5、验证用户请求携带token
String header = request.getHeader(JwtUtil.TOKEN_HEADER); if (null == header || !header.toLowerCase().startsWith(JwtUtil.TOKEN_PREFIX)) { // 如果头部 Authorization 未设置或者不是 basic 认证头部,则当前 // 请求不是该过滤器关注的对象,直接放行,继续filter chain 的执行 chain.doFilter(request, response); return;} try {String token = header.replace(JwtUtil.TOKEN_PREFIX, ""); // 验证token是否过期if (JwtUtil.isExpiration(token)) { throw new javax.security.sasl.AuthenticationException("token 验证不通过");} //檢查token是否能解析Users user = (Users) JwtUtil.getUser(token); if (null == user) { throw new javax.security.sasl.AuthenticationException("token 验证不通过");} //验证成功