JWT的使用
大约 4 分钟
JWT的使用
为什么使用JWT,而不使用session? 根据我的了解(大白话),传统session是保存在服务器内存中的,而JWT是保存在浏览器本地的,使用JWT的好处就是不占用服务器的内存,使用session的话每一个用户都占用一点服务器的内存,这样显然不好。 使用JWT的话,由于它是保存在浏览器本地的,不会占用服务器的内存,这是一点好处,在前后端分离的系统中对比session,Jwt会更好一点。
header 和 payload 都是用base64来加密的,他们的本体是json数据,咱们看到的只是用base64加密后的header里包含的内容是 jwt的类型和加密算法payload里包含的是 非敏感信息,例如:用户id,账号…等等用户的信息,就是不要放密码,为了安全。
# jwt的结构:
> string ===> header.payload.singnature
### 1.令牌组成
- 1.标头(Header)
- 2.有效载荷(Payload)
- 3.签名 (Signature)
- 因此,JWT通常如下所示 : xxxxxx.yyyyyyy.zzzzzz
1、引入依赖
<!--引入jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
2、生成token
public final static long EXPIRE_TIME=30*60*1000;//放在类中 ,30分钟的毫秒数
//获取时间,用来做token的过期时间,设置30分钟
Date date=new Date(System.currentTimeMillis()+EXPIRE_TIME)
Map<String,Object> map=new HashMap<>();
String sign = JWT.create().withHeader(map) //header 这个不设置也可以
.withClaim("userId", 12)//payload 存储非敏感的信息 例如用户账号,不能存密码,防止被人解析
.withExpiresAt(date)//指定令牌的过期时间
.sign(Algorithm.HMAC256("!we2123")) ;//签名 保密复杂
System.out.println(sign); //输出结果
# 生成结果
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MjY1MzQ3OTIsInVzZXJJZCI6MTIsInVzZXJuYW1lIjoiamZwIn0.GhUCPBptbASHng-8DkEFIw9jYB2IWStUj3SEgm53x84
3、验证token
验证时可以加一个
try {
} catch(){
例如验证出现了异常,就证明验证失败 直接return false;
}
//创建验证对象
//验证时一定要跟生成时的 算法 和 签名 一致
JWTVerifier build = JWT.require(Algorithm.HMAC256("!we2123")).build();
DecodedJWT verify = build.verify(" 这里放token ");
/**
以上两行代码就已经完成验证了,如果没有出异常,就验证成功!
出现了就是验证失败 ,前提要保证算法一定要一致,不要出现算法不一致异常
*/
//获取payload存储的信息
System.out.println(verify.getClaim("userId").asInt()); //存什么类型 就as什么类型,否则为null
System.out.println(verify.getClaim("username").asString());
Date expiresAt = verify.getExpiresAt(); // 查看token过期时间
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(expiresAt);
System.out.println(format);
4、jwt验证出现的异常
验证时会先验证签名,再验证令牌有没有过期
# 签名验证异常 (生成时和验证时的签名不一致,会出现此异常)
- SignatureVerificationException
# 算法不匹配异常 (生成时和验证时的算法不一致,会出现此异常)
- AlgorithmMismatchException
# 令牌过期异常 (生成时设置的时间超时后,再次验证会出现此异常)
- TokenExpiredException
# 失效的payload异常 (出现此异常的原因有:可能有人使用base64解析payload,更改了数据payload里解析的数据,再次传过来,验证时会出现此异常)
- InvalidClaimException
5、封装工具类
com.imooc.utils放在工具文件夹中
package com.imooc.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;
//JWT封装工具类
public class JWTUtil {
//30分钟
public final static long EXPIRE_TIME=30*60*1000;
private static String SECRET = "token!Q@W#E$R";
/**
* 生成token
*/
public static String getToken(Map<String, String> map){
Date date=new Date(System.currentTimeMillis()+EXPIRE_TIME); //默认30分钟过期
//创建JWT builder
JWTCreator.Builder builder = JWT.create();
// payload
map.forEach((k,v)->{
builder.withClaim(k,v); //这里可以存放 用户id,用户名
});
builder.withExpiresAt(date);//指定令牌的过期时间
//实际项目中可以 接收用户的密码来做签名,这样每一个用户对应一个签名
java.lang.String token =builder.sign(Algorithm.HMAC256(SECRET));
return token;
}
/**
* 验证token合法性
*/
public static boolean verify(String token){
try {
//如果抛出异常,证明签名不一致 / token过期
JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
return true;
}catch (Exception e){
return false;
}
}
/**
* 获得token中的信息无需secret解密也能获得
*/
public static String getUserName(String token){
DecodedJWT decode = JWT.decode(token);
//假设存储的是一个phone
String phone = decode.getClaim("phone").asString();
return phone;
}
}
具体使用情况实战教程