背景:在开始一个项目时,目前生成token的方法主要有两种,一种是spring-security+jwt组合,另一种则是shiro+jwt组合,本文主要阐述spring-security+jwt组合生成token。对这两种框架不熟悉的同学,有兴趣可以点击去官网学习下,不熟悉的也没关系,在生成token的过程中涉及到的知识点,我会一一解答,不熟悉的也不影响阅读本文(建议独自去学习下)。
开始:
废话不多,大家都知道,要使用框架,那必不可少的,首先就是要引入相关依赖啊
//pom.xml
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
除此之外,我们还需要在application.yml文件中,给jwt配置一下信息:
jwt:
# JWT存储请求头
tokenHeader: Authorization
# JWT加密使用的密钥
secret: juejin-secret
# JWT超期限时间(60*60*24)
expiration: 604800
# JWT负载中拿到开头
tokenHead: Bearer
不管你是老司机,还是正在进阶的同学,我还是在这里啰嗦解析一下其中的字段。tokenHeader是存储的一个请求头,在前端后端接口数据交互中一般都会有,以及tokenHead一般都会存在于请求信息里,secret是jwt加密和解密要使用到的一个密钥,expiration是token的一个失效时间设定,我这里设置是24小时失效。
接下来我们可以去实现一个spring-security+jwt组合生成token的一个工具类JwtTokenUtil,这个类一般都放在包的config下,下面先把代码展示出来:
/java/com.hhk,server.config.security(包路径)
package com.hhk.server.config.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtTokenUtil {
private static final String CLAIM_KEY_USERNAME="sub";
private static final String CLAIM_KEY_CREATED="created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
/**
* 根据用户信息生成token
*/
public String generateToken(UserDetails userDetails){
Map<String,Object> claims=new HashMap<>();
claims.put(CLAIM_KEY_USERNAME,userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED,new Date());
return generateToken(claims);
}
// 根据荷载JWT生成token
public String generateToken(Map<String,Object> claims){
// Jwts去生成token
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS256,secret)
.compact();
}
// 生成token失效时间
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis()+expiration*1000);
}
// 从token中获取登录名
public String getUserNameByToken(String token){
String userName;
try{
Claims claims=getClaimsFromToken(token);
userName=claims.getSubject();
}catch (Exception e){
userName=null;
}
return userName;
}
// 从token中获取荷载
private Claims getClaimsFromToken(String token) {
Claims claims=null;
try {
claims=Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
e.printStackTrace();
}
return claims;
}
// 验证token是否有效
public boolean validateToken(String token,UserDetails userDetails){
String username=getUserNameByToken(token);
return username.equals(userDetails.getUsername())&&!isTokenExpired(token);
}
// 验证token是否失效
private boolean isTokenExpired(String token) {
Date expireDate=getExpiredDateFromToken(token);
return expireDate.before(new Date());
}
// 从token中获取失效时间
private Date getExpiredDateFromToken(String token) {
Claims claims=getClaimsFromToken(token);
return claims.getExpiration();
}
// 判断token是否可以被刷新
public boolean canRefresh(String token){
return !isTokenExpired(token);
}
// 刷新token
public String refreshToken(String token){
Claims claims=getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED,new Date());
return generateToken(claims);
}
}
下面主要是解析一下,上面整个JwtTokenUtil的源码,自己能阅读的话,不用看下面的源码,下面主要是以方法为单位进行拆分讲解:
//在类的开始,我们需要定义两个静态字符串,以及获取我们前面在Application.yml中定义好的jwt参数,这里是通过@Value(${""})的方式获取Application.yml文件中的配置信息了
private static final String CLAIM_KEY_USERNAME="sub"; //定义静态用户名(key)CLAIM_KEY_USERNAME
private static final String CLAIM_KEY_CREATED="created";//定义静态生成token的时间
@Value("${jwt.secret}") //获取密钥
private String secret;
@Value("${jwt.expiration}") //获取设定的失效时间
private Long expiration;
到这里在工具类JwtTokenUtil需要使用的静态资源就准备好了,下面我们要开始创建生成token的方法了:
/**
* 根据用户信息生成token
*/
public String generateToken(UserDetails userDetails){
Map<String,Object> claims=new HashMap<>(); //定义一个荷载,用于存储用户ing以及token的生成时间
claims.put(CLAIM_KEY_USERNAME,userDetails.getUsername()); //通过。put方法存储CLAIM_KEY_USERNAME和CLAIM_KEY_CREATED的值
claims.put(CLAIM_KEY_CREATED,new Date());
return generateToken(claims); //generateToken的方法在下面会编写其源码,主要是根据荷载生成token的
}
承上启下,下面是generateToken(claims):
// 根据荷载JWT生成token
public String generateToken(Map<String,Object> claims){
return Jwts.builder() //Jwts去生成token
.setClaims(claims) //存储带有用户信息的荷载
.setExpiration(generateExpirationDate()) //存储失效时间
.signWith(SignatureAlgorithm.HS256,secret) //使用算法,和密钥
.compact();
}
generateToken(claims)中使用的generateExpirationDate(),贴一下代码:
// 生成token失效时间
private Date generateExpirationDate() {
//System.currentTimeMillis()+expiration*1000===当前时间+失效时间
return new Date(System.currentTimeMillis()+expiration*1000);
}
到这里其实已经实现了生成token了,但是,既然作为工具类,那在项目中,有时候要校验token,就是通过token中的用户名去跟数据库中的用户进行对比,这时就要获取token中的用户名了,也就是源码中的getUserNameByToken(String token):
从token中获取登录名
public String getUserNameByToken(String token){
String userName;
try{ //有时候会存在用户名不存在的情况,所以要对这种异常进行捕获
Claims claims=getClaimsFromToken(token); //因为生成token时,用户名是存储在token中的荷载,所以getClaimsFromToken(token)获取荷载
userName=claims.getSubject(); //claims的getSubject()获取用户名
}catch (Exception e){
userName=null;
}
return userName;
}
getUserNameByToken(String token)中使用的getClaimsFromToken(token)是要从token中获取荷载:
// 从token中获取荷载
private Claims getClaimsFromToken(String token) {
Claims claims=null;
try {
//通过密钥和token从jwts中获取荷载,因为荷载有可能为空,所以需要捕获一下异常,防止程序崩溃
claims=Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
e.printStackTrace();
}
return claims;
}
在日常开发中,或者访问网页中时,我们经常会出现需要重新登录的情况,这是,就需要重新登录,以获取新的token继续访问网页,这个过程,需要验证token是否有效、是否可以被刷新。具体方法如下:
// 验证token是否有效UserDetails是spring-security框架里的一个类
public boolean validateToken(String token,UserDetails userDetails){
String username=getUserNameByToken(token); //从token中获取用户名
return username.equals(userDetails.getUsername())&&!isTokenExpired(token);
}
// 验证token是否失效
private boolean isTokenExpired(String token) {
Date expireDate=getExpiredDateFromToken(token); //从token中获取失效时间
return expireDate.before(new Date());
}
// 从token中获取失效时间
private Date getExpiredDateFromToken(String token) {
Claims claims=getClaimsFromToken(token); //从token中获取荷载,荷载中存储了失效时间
return claims.getExpiration();
}
// 判断token是否可以被刷新
public boolean canRefresh(String token){
return !isTokenExpired(token);
}
// 刷新token
public String refreshToken(String token){
Claims claims=getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED,new Date());
return generateToken(claims);
}
到这里,spring-security+jwt生成token,以及获取token中的信息的工具类就写完了,感谢阅读!❤️❤️❤️