JWT验证
Jwt结构
json web token 其实就是一个字符串 string令牌
header.payload.singnature
1、标头(Header)
令牌类型(即jwt) + 使用的签名算法
{
“alg”:"HS256", //签名算法,还有HMAC,RSA,SHA256
"typ":"jwt" //令牌类型,JWT
}
最后通过Base64编码加密组成字符串变成JWT的一部分。
2、有效载荷(Payload)
令牌的第二部分,有效负载,包含有关实体和其他数据的声明,除敏感信息,同样使用Base64组码加密组成JWT第二部 分
{
“sub”:"123456",
"name":"John Doe",
"admin":true
}
3、签名(Signature)
Signature需要使用编码后的header和payload以及我们提供的密钥,再使用header指定的算法(HS256)进行签名。
**签名的作用保证被篡改的数据不会通过 **
4、总的jwt结构就是
header.payload.Signature(header+payload+私钥)
签名的目的:最后一步签名的过程中,实际是对头部以及负载内容进行的签名,防止内容被串改,如果有人对头部以及负载的内容解码之后进行修改,再进行修改加上签名组成新的jwt后,服务端会判断除新的头部和负载形成签名和JWT附带的签名不一样的。如果对新的头部和负载进行签名时候,在不知道服务器加密时用的公钥的话,得出来的签名也是不一样的。
最后Base64编码后
header:红色 payload:紫色 signature:蓝色
JWT使用
第一步:引入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.0</version>
</dependency>
第二步:生成token
//生成token
@Test
public void getToken(){
long EXPIRE_TIME =30*60*1000; //30分钟过期时间
//获取时间,作为token的过期时间,设置为30分钟
Date deadline = new Date(System.currentTimeMillis() + EXPIRE_TIME);
HashMap<String, Object> map = new HashMap<>();
String sign = JWT.create().withHeader(map) //header,暂不设置。
.withClaim("userId", 12) //payload 存储非敏感的信息 例如用户账号,不能存密码,防止被人解析
.withClaim("username","bindada")
.withExpiresAt(deadline) //指定令牌的过期时间
.sign(Algorithm.HMAC256("!we2123")); //签名 保密复杂
System.out.println(sign); //输出token
}
第三步:verify验证token
@Test
public void verify(){
//创建验证对象 注意:验证时候一定要和生成时的 算法 和 签名 一致。
JWTVerifier build = JWT.require(Algorithm.HMAC256("!we2123")).build();
DecodedJWT verify = build.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTc0MzkxNTcsInVzZXJJZCI6MTIsInVzZXJuYW1lIjoiYmluZGFkYSJ9.Eb4VKwitXlXq0qvZW3fOWXTY-69ZfzrzqAbacSaMdUA");
//以上两行已经完成验证了,若没异常就验证成功!有误就验证失败,前提保证算法一定的一致。
//获取payload存储信息
System.out.println(verify.getClaims().get("userId").asInt()); //存什么类型,就用什么as方法,否则会null。
System.out.println(verify.getClaims().get("username").asString());
//查看token过期时间
Date expiresAt = verify.getExpiresAt();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(expiresAt);
System.out.println(format);
}
异常类型
# 签名验证异常 (生成时和验证时的签名不一致,会出现此异常)
- SignatureVerificationException
# 算法不匹配异常 (生成时和验证时的算法不一致,会出现此异常)
- AlgorithmMismatchException
# 令牌过期异常 (生成时设置的时间超时后,再次验证会出现此异常)
- TokenExpiredException
# 失效的payload异常 (出现此异常的原因有:可能有人使用base64解析payload,更改了数据payload里解析的数据,再次传过来,验证时会出现此异常)
- InvalidClaimException
JwtUtil工具类
package com.bindada.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Date;
import java.util.Map;
/*
* @Description JwtUtil的工具类
* @Author bindada 2022/7/10 21:12
*/
public class Jwtutil {
private static final String SING = "!AFGYO8*$@%"; //服务器签名
private static final long EXPIRE_TIME = 120*60*1000; //毫秒级,2个小时
/*
* 生成token 格式:header.payload.sing
*/
public static String getToken(Map<String,String> map){
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); //过期时间
JWTCreator.Builder builder = JWT.create();
//payload
map.forEach((k,v)->{
builder.withClaim(k,v); //遍历存储payload信息
});
String token = builder.withExpiresAt(date).sign(Algorithm.HMAC256(SING)); //设置令牌时间和签名加密
return token;
}
/*
* 验证token合法性
*/
public static DecodedJWT verify(String token){
//注意:验证时候一定要和生成时的 算法 和 签名SING 一致。
return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
}
/*
* 获得token中的信息
*/
public static DecodedJWT getTokenInfo(String token){
DecodedJWT decode = JWT.decode(token);
//decode.getClaims().get("username");
return decode;
}
}