Java生成token的工具类
简单了解Token与JWT
什么是Token
Token是什么?某度里这么说:
Token在计算机身份认证中是令牌(临时)的意思。一般作为邀请、登录系统使用。
也就是用户请求资源的的时候不需要每次都去验证账号密码等信息,而是只需要验证令牌是否正确即可。
打个简单的比方:假如你要进一个小区拜访一个富婆,如果是陌生人,小区的保安会询问你相关的信息,比如你叫什么,身份证多少,电话多少,要去拜访哪个住户,几栋几单元几号的用户。然后跟住户确定没问题之后才放你进去小区。
如果你要多次出入,保安大爷记性不好,记不住你的脸,但每次问这问那又很麻烦。于是大爷就给你发了一个“拜访证”,类似于以下这样:

这样的话,大爷只要看拜访证,就可以放你过去了。也就避免了每次都要把你当成陌生人来盘问的环节。
那么登录就可以看作这样子:
第一次登录

后续进入系统进行操作:

这样的话,就不必每次都去让用户输入账号和密码进行验证了,而只要在第一次登录验证通过的时候给用户一个携带有用户信息的token,后续只要携带token请求资源,就只需要校验token是否正确即可。
什么是JWT
JWT的全称是Json Web Token,直译过来就是Json格式的互联网令牌。其作用是为多种终端设备,提供统一的、安全的令牌格式。所以JWT是一种令牌的格式,大家约定好的一种令牌的格式。
为什么要用Token认证
早期我们使用session(客户端使用cookie)来存储登录信息来实现验证的,但相比于session,基于token的认证有以下优点
- 支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题
- 无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力
- 更适用CDN:可以通过内容分发网络请求服务端的所有资料
- 更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多
- 无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御
以上信息来源于以下大佬的文章:JWT详解
token与session的区别及优势请戳上面大佬的文章。
使用token验证记住这一点:token中不存放敏感信息。
了解使用到的类
引入的依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
简单了解需要使用的类
Claims
先看注释
This is ultimately a JSON map and any values can be added
to it, but JWT standard names are provided as
type-safe getters and setters for convenience.
Because this interface extends Map<String, Object>,
if you would like to add your own properties,
you simply use map methods, for example:
claims.put("someKey", "someValue");
简而言之这个类可以看成一个Json的Map映射集合,因为其继承了Map接口。
SecretKey
这个之前讲AES算法的时候就说过了,是一个密钥的接口。
SecretKeySpec
密钥(SecretKey)的实现类
UUID
产生一个随机36字符的长字符串
JwtBuilder
A builder for constructing JWTs.
用于构造jwt的生成器,用于设置JWT的各种基本信息以及产生token字符。
token工具类
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClaims;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* @author 三文鱼先生
* @title
* @description 生成token和解析token的工具类
* @date 2022/10/13
**/
public class TokenUtil {
//密钥明文I guess PeterPan was right,growing up's a waste of time.
private static final String SECRET_CLEARTEXT = "PeterPan";
//过期时间默认为半小时后过期
private static final long EXPIRATION_TIME = 1000 * 60 * 30L;
/**
* @description 以Base64编码获取一个AES算法密钥
* @author 三文鱼先生
* @date 16:22 2022/10/14
* @return javax.crypto.SecretKey
**/
public static SecretKey getSecretKey() {
//以Base64编码获取到明文密钥的字节 以该数组生成一个AES算法的的密钥
return new SecretKeySpec(Base64.getDecoder().decode(SECRET_CLEARTEXT) , "AES");
}
/***
* @description 获取一个无 - 作为token的唯一ID
* @author 三文鱼先生
* @date 16:07 2022/10/14
* @return java.lang.String
**/
public static String getUUID() {
return UUID.randomUUID().toString().replaceAll("-" , "");
}
/***
* @description 以默认的过期时间构造token
* @author 三文鱼先生
* @date 16:07 2022/10/14
* @param str 存放在token里的信息
* @return java.lang.String
**/
public static String getToken(String str) {
return getJwtBuilder(str).compact();
}
/***
* @description 以指定的过期时间构造token
* @author 三文鱼先生
* @date 16:07 2022/10/14
* @param str 存放在token里的信息
* @param mills 指定多少毫秒后过期
* @return java.lang.String
**/
public static String getToken(String str , long mills) {
return getJwtBuilder(str , mills).compact();
}
/**
* @description 获取一个JWT的构造器
* @author 三文鱼先生
* @date 16:09 2022/10/14
* @param str
* @return io.jsonwebtoken.JwtBuilder
**/
public static JwtBuilder getJwtBuilder(String str) {
if(str == null)
throw new RuntimeException("实体数据为空");
//获取到AES算法的密钥
long nowMills = System.currentTimeMillis();
DefaultClaims defaultClaims = new DefaultClaims();
defaultClaims.put("msg" , str);
return Jwts.builder()
.setId(getUUID()) //唯一的ID
.setSubject("token") // 数据
.setIssuer("PeterPan") // 签发者
.setClaims(defaultClaims) //数据
.setIssuedAt(new Date(nowMills)) // 签发时间设置为当前
.signWith(SignatureAlgorithm.HS256, getSecretKey()) //使用HS256对称加密算法签名, 第二个参数为秘钥
.setExpiration(new Date(nowMills + EXPIRATION_TIME));//设置为超时时间
}
/***
* @description 获取一个token的构造器
* @author 三文鱼先生
* @date 15:51 2022/10/14
* @param str 数据
* @param mills 过期时间
* @return io.jsonwebtoken.JwtBuilder
**/
public static JwtBuilder getJwtBuilder(String str , long mills) {
if(str == null)
throw new RuntimeException("实体数据为空");
//获取到AES算法的密钥
long nowMills = System.currentTimeMillis();
DefaultClaims defaultClaims = new DefaultClaims();
defaultClaims.put("msg" , str);
return Jwts.builder()//一个构造器 下面为必要属性的设置
.setId(getUUID()) //唯一的ID
.setSubject("token") //主题为token
.setIssuer("PeterPan") // 签发者
.setClaims(defaultClaims) //数据存放
.setIssuedAt(new Date(nowMills)) // 签发时间设置为当前
.signWith(SignatureAlgorithm.HS256, getSecretKey()) //使用HS256对称加密算法签名, 第二个参数为秘钥
.setExpiration(new Date(nowMills + mills));//设置过期时间
}
/**
* @description 从给定的token中获取msg信息
* @author 三文鱼先生
* @date 16:09 2022/10/14
* @param token token
* @return java.lang.String
**/
public static String getMsgFromToken(String token)
throws UnsupportedJwtException ,//不支持的格式异常
MalformedJwtException ,//平台jwt异常
SignatureException,//签名异常
ExpiredJwtException,//超时异常
IllegalArgumentException { //非法参数异常
return Jwts.parser()//token的语法分析器
.setSigningKey(getSecretKey())//设置签名验证所用的密钥
.parseClaimsJws(token)//处理token
.getBody()//获取存入的token里的所有信息
.get("msg").toString();//获取claims里面存放的msg数据
}
}
测试
public class Test {
public static void main(String[] args) {
String token = TokenUtil.getToken("放入token中的msg");
System.out.println(token);
System.out.println("msg: " + TokenUtil.getMsgFromToken(token));
}
}
测试结果
eyJhbGciOiJIUzI1NiJ9.eyJtc2ciOiLmlL7lhaV0b2tlbuS4reeahG1zZyIsImlhdCI6MTY2NTczNjE2NCwiZXhwIjoxNjY1NzM3OTY0fQ.CW3yZ7DP4fXHQTnJVSIQZwvQcabzkTd7FTNl3sIf9a4
msg: 放入token中的msg
3827

被折叠的 条评论
为什么被折叠?



