文章目录
- 1、用途✨
- 2、实现流程👀
- 3、JWT介绍
- 3.1、构成
- 3.1.1、header
- 3.1.2、载荷
- 3.1.3、签证
- 4、java实现
- 4.1、引入依赖
- 4.2、测试案例
官网链接:https://github.com/jwtk/jjwt
1、用途✨
JWT主要作用分为以下两种:
- 1:作为在前后端分离项目中的登录策略
- 2:单纯作为客户端的请求身份认证,spring-gateway网关进行判断拦截
这里我仅讲解第二种!第一种见链接:
2、实现流程👀
- 1:服务方编写代码生成token,记录token给消费方
- 2:消费方每次发来的请求,都要携带该token
- 3:服务方部署网关对消费方发送来的token进行解密,判断是否有效:有效放行到服务端,否则进行拦截
注:这种不需要存储在服务端的认证方式时无状态认证方式,不同于session+cookie的有状态认证方式,其中好处主要为以下几点:
- 客户端请求不依赖服务端的信息,多次请求不需要必须访问到同一台服务器
- 减小服务端存储压力
3、JWT介绍
JWT,全称是Json Web Token, 是一种JSON风格的轻量级的授权和身份认证规范,可实现无状态、分布式
的Web应用授权!
3.1、构成
JWT是由三段信息构成的,将这三段信息文本用.
链接一起就构成了Jwt字符串。第一部分我们称它为头部(header)
,第二部分我们称其为载荷(payload)
,第三部分是签证(signature)
.
3.1.1、header
JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示:
{
"alg": "HS256",
}
有的文章说还包含:typ属性表示令牌的类型,JWT令牌统一写为JWT,但是我打印的如上图只有alg。
3.1.2、载荷
一共有7个字段可以设置,如下:
iss: jwt签发者
sub: jwt所面向,使用jwt的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须大于签发时间
nbf: 定义在指定时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
另外,可以自定义一些字段,如下:
{
"admin": true
}
3.1.3、签证
生成语法:
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
注:其中secret是密钥,不可泄漏,并且其生成也用到了头部信息和载荷信息。
JWT解析地址:https://jwt.io/#libraries
4、java实现
4.1、引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
4.2、测试案例
生成代码:
package org.example;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.Base64Codec;
import java.util.Date;
// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
// then press Enter. You can now see whitespace characters in your code.
public class Main {
public static void main(String[] args) {
// Press Alt+Enter with your caret at the highlighted text to see how
// IntelliJ IDEA suggests fixing it.
//testJwt();
generateJwtToken();
testParseToken();
}
/** 测试生成token1 */
public static void testJwt() {
JwtBuilder jwtBuilder = Jwts.builder()
// 唯一ID {"":""}
.setId("888")
// 接受的用户 {"sub":"Rose"}
.setSubject("Rose")
// 签发时间 {"iat":"。。。"}
.setIssuedAt(new Date())
// 签名算法 及秘钥
.signWith(SignatureAlgorithm.HS256, "aVBhbkUxcEEyc1dvMmRCeVpM");
// 签发token
String token = jwtBuilder.compact();
System.out.println(token);
String[] split = token.split("\\.");
// 头部
System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
// 载荷
System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
// 算法及秘钥 这个会乱码
System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
}
/** 测试生成token2 */
public static String generateJwtToken(){
// 头部 map / Jwt的头部承载,第一部分
// 可不设置 默认格式是{"alg":"HS256"}
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
//载荷 map / Jwt的载荷,第二部分
Map<String,Object> claims = new HashMap<String,Object>();
//私有声明 / 自定义数据,根据业务需要添加
claims.put("id","123456");
claims.put("userName", "admin");
//标准中注册的声明 (建议但不强制使用)
//一旦写标准声明赋值之后,就会覆盖了那些标准的声明
claims.put("iss", jwt_iss);
/* iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
*/
//下面就是在为payload添加各种标准声明和私有声明了
return Jwts.builder() // 这里其实就是new一个JwtBuilder,设置jwt的body
.setHeader(map) // 头部信息
.setClaims(claims) // 载荷信息
.setId(UUID.randomUUID().toString()) // 设置jti(JWT ID):是JWT的唯一标识,从而回避重放攻击。
.setIssuedAt(new Date()) // 设置iat: jwt的签发时间
.setExpiration(new Date(System.currentTimeMillis() + access_token_expiration * 1000)) // 设置exp:jwt过期时间
.setSubject(subject) //设置sub:代表这个jwt所面向的用户,所有人
.signWith(SignatureAlgorithm.HS256, "aVBhbkUxcEEyc1dvMmRCeVpM");//设置签名:通过签名算法和秘钥生成签名
.compact(); // 开始压缩为xxxxx.yyyyy.zzzzz 格式的jwt token
}
/** 解析token */
public static void testParseToken() {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiJSb3NlIiwiaWF0IjoxNjkwOTY1ODY3fQ.pT-fzrxquCmB5eLqIiPQfdow92c0ZJBHP79-W3KE898";
//得到的是荷载
Claims claims = (Claims) Jwts.parser()
// 解析时的秘钥一定要和签发时秘钥相同,但是发现这了的秘钥为4-7个x都是可以成功解析的
.setSigningKey("aVBhbkUxcEEyc1dvMmRCeVpM111")
// .parse(token)
.parseClaimsJws(token)
.getBody();
System.out.println(claims);
// 得到的是头部
JwsHeader test = Jwts.parser()
// 解析时的秘钥一定要和签发时秘钥相同,但是发现这了的秘钥为4-7个x都是可以成功解析的
.setSigningKey("aVBhbkUxcEEyc1dvMmRCeVpM111")
// .parse(token)
.parseClaimsJws(token)
.getHeader();
System.out.println(test);
}
}
参考链接:
https://blog.csdn.net/qq_37138756/article/details/103499429