概念
JWT(JSON Web Token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。
组成
jwt由三部分组成,分别是头部,载荷以及签证。
然后这三部分进行加密,然后用“.”连接在一起
如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
分别为:header(加密).playload(加密).signature(加密)
1、header
头部,由json组成
具体参数如下:
{
'typ': 'JWT',
'alg': 'HS256'
}
- typ:类型
- alg:加密算法
2、playload
载荷,一般用于存放有效信息,也是以json的格式进行存储
有效信息分为三部分:
- 标准中注册的声明;
其中,标准中的注册声明分为以下几部分
iss: jwt签发者;
sub: jwt所面向的用户;
aud: 接收jwt的一方;
exp: jwt的过期时间,这个过期时间必须要大于签发时间;
nbf: 定义在什么时间之前,该jwt都是不可用的;
iat: jwt的签发时间;
jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击;
- 公共的声明;
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密。
- 私有的声明;
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
3、signature
签证,由三部分组成,分别是:header (base64后的);payload (base64后的);secret (密钥);
所以我们在加解密的时候,需要秘钥,一定要保存好
使用
简单使用
1、导入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
2、工具类编写
主要有两种方式,一种是存储自定义类,一种是存储hashMap
自定义类
1、构建类
package com.walker.third.jwt;
import lombok.Data;
/**
* 这里的参数可以自己定义,例如我们可以存储用户的id,或者部门id等等
* 之后我们就可以在token进行获取
*/
@Data
public class TokenData {
private String name;
private Integer age;
}
2、工具类
package com.walker.third.jwt;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClaims;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
//jwt标识,这里自己可以进行修改
static String JWT_ID="tokenId";
//秘钥,也可以将其放在配置文件中
static String secretKey="fsfdhufwjnqfjnfafa";
/**
* 生成token,claims中存放一个类,方便我们在直接获取类进行处理
*/
public static String genToken(TokenData tokenData,Integer expiredMinute){
DateTime date = DateUtil.date();
HashMap<String, Object> claims = new HashMap<>();
claims.put("data", JSONUtil.toJsonStr(tokenData));
//构建JwtBuilder
JwtBuilder builder = Jwts.builder()
.setClaims(claims) //私有声明,用于存放有效信息
.setId(JWT_ID) //是JWT的唯一标识
.setIssuedAt(date) //签发时间
.signWith(SignatureAlgorithm.HS256, secretKey) //加密算法
.setExpiration(DateUtil.offsetMinute(date, expiredMinute));//过期时间
/**
* compact:压缩,将header,playload,signature压缩成token字符串
*/
String compact = builder.compact();
return compact;
}
/**
* 获取tokenData
*/
public static TokenData getTokenData(String token){
Claims body;
try {
//使用jwt解析器
body = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
return null;
}
//获取到放入的的data
String data = body.get("data", String.class);
//将获取到的json转成对应的类
return JSONObject.parseObject(data,TokenData.class);
}
}
3、测试
@Test
public void genToken2(){
TokenData tokenData = new TokenData();
tokenData.setName("waler");
tokenData.setAge(18);
String token = JwtUtil.genToken(tokenData, 10);
System.out.println(token);
TokenData res = JwtUtil.getTokenData(token);
System.out.println(res);
}
返回结果:
eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoie1wibmFtZVwiOlwid2FsZXJcIixcImFnZVwiOjE4fSIsImV4cCI6MTY1Mzk2OTI2OCwiaWF0IjoxNjUzOTY4NjY4LCJqdGkiOiJ0b2tlbklkIn0.i9ioJYoe8k6X4KlXZu2tU0P0Pfl5D0FH-CApWMf3QOg
TokenData(name=waler, age=18)
HashMap存储
1、工具类方法
package com.walker.third.jwt;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClaims;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
//jwt标识,这里自己可以进行修改
static String JWT_ID="tokenId";
//秘钥,也可以将其放在配置文件中
static String secretKey="fsfdhufwjnqfjnfafa";
/**
* 生成token
*/
public static String genToken(Map<String,Object> claims,Integer expiredMinute){
DateTime date = DateUtil.date();
JwtBuilder builder = Jwts.builder()
.setClaims(claims) //私有声明,用于存放有效信息
.setId(JWT_ID) //是JWT的唯一标识
.setIssuedAt(date) //签发时间
.signWith(SignatureAlgorithm.HS256, secretKey) //加密算法
.setExpiration(DateUtil.offsetMinute(date, expiredMinute));//过期时间
/**
* compact:压缩,将header,playload,signature压缩成token字符串
*/
String compact = builder.compact();
return compact;
}
/**
* 获取claims
*/
public static Claims getClaims(String token){
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
claims=null;
}
return claims;
}
}
2、测试
@Test
public void genToken(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","walker");
map.put("age",18);
String token = JwtUtil.genToken(map, 10);
System.out.println(token);
Claims claims = JwtUtil.getClaims(token);
System.out.println(claims);
}
返回结果:
eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoid2Fsa2VyIiwiZXhwIjoxNjUzOTcwNzAzLCJpYXQiOjE2NTM5NzAxMDMsImFnZSI6MTgsImp0aSI6InRva2VuSWQifQ.08V-cVMsXxFiJW7l9KhM3968Ke7uKeP7jz3fNRG_Ii0
{name=walker, exp=1653970703, iat=1653970103, age=18, jti=tokenId}
如果使用的是返回Claims的,会返回对应的过期时间等其他的信息,所以对于返回,还是可以使用Claims,当然也可以自己进行一定的整理
相关知识
JwtBuilder
Header 头部
claims:私有声明
sign 签名
claims,包含两种
JwtBuilder setClaims(Claims var1);
JwtBuilder setClaims(Map<String, Object> var1);
加密算法
一般有下面这里几种,枚举
public enum SignatureAlgorithm {
NONE("none", "No digital signature or MAC performed", "None", (String)null, false),
HS256("HS256", "HMAC using SHA-256", "HMAC", "HmacSHA256", true),
HS384("HS384", "HMAC using SHA-384", "HMAC", "HmacSHA384", true),
HS512("HS512", "HMAC using SHA-512", "HMAC", "HmacSHA512", true),
RS256("RS256", "RSASSA-PKCS-v1_5 using SHA-256", "RSA", "SHA256withRSA", true),
RS384("RS384", "RSASSA-PKCS-v1_5 using SHA-384", "RSA", "SHA384withRSA", true),
RS512("RS512", "RSASSA-PKCS-v1_5 using SHA-512", "RSA", "SHA512withRSA", true),
ES256("ES256", "ECDSA using P-256 and SHA-256", "Elliptic Curve", "SHA256withECDSA", false),
ES384("ES384", "ECDSA using P-384 and SHA-384", "Elliptic Curve", "SHA384withECDSA", false),
ES512("ES512", "ECDSA using P-512 and SHA-512", "Elliptic Curve", "SHA512withECDSA", false),
PS256("PS256", "RSASSA-PSS using SHA-256 and MGF1 with SHA-256", "RSA", "SHA256withRSAandMGF1", false),
PS384("PS384", "RSASSA-PSS using SHA-384 and MGF1 with SHA-384", "RSA", "SHA384withRSAandMGF1", false),
PS512("PS512", "RSASSA-PSS using SHA-512 and MGF1 with SHA-512", "RSA", "SHA512withRSAandMGF1", false);
//..省略其他代码
}
JwtParser jwt解析器
一般用来处理token的解析,能够获取放在playload中的有效信息