JWT
JSON Web Token簡稱JWT,是用于對應程式上的使用者進行身份驗證的标記。也就是說,使用JWTS的應用程式不再需要儲存有關其使用者的cookie和session資料。此特性便于可伸縮性,同時保證應用程式的安全。
格式:
JWT就是一個字元串,經過加密處理和校驗處理的字元串,形式為:A.B.C
- A由JWT頭部資訊header加密得到
- B由JWT用到的身份驗證資訊json資料加密得到
- C由A和B加密得到,是校驗部分
認證流程:
JWT認證流程
Springboot整合JWT:
依賴:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
JWT工具類:
工具類實作token的生成、從token中擷取claims資訊、驗證token的有效性三個功能。
public class JwtUtils{
private static final int TOKEN_TIME_OUT = 3600; //token的有效期為1小時
private static final String TOKEN_SECRET = "itcast"; //加密key、
/**
* 生成Token
*
* @param params
* @return
*/
public static String getToken(Map params){
long currentTime = System.currentTimeMillis();
return Jwts.builder()
.signWith(SignatureAlgorithm.HS512, TOKEN_SECRET) //加密方式
.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //過期時間戳
.addClaims(params)
.compact();
}
/**
* 擷取token中的claims資訊
*
* @param token
* @return
*/
public static Claims getClaims(String token){
return Jwts.parser()
.setSigningKey(TOKEN_SECRET)
.parseClaimsJws(token)
.getBody();
}
/**
* Token是否有效 true--有效 false--無效
* @param token
* @return
*/
public static boolean verifyToken(String token){
if(StringUtils.isEmpty(token)){
return false;
}
try{
Claims claims = Jwts.parser()
.setSigningKey(TOKEN_SECRET)
.parseClaims(token)
.getBody();
}catch(Exception e){
return false;
}
return true;
}
}
統一token處理:
基于ThreadLocal+攔截器的形式統一處理
ThreadLocal+攔截器處理token
ThreadLocal:
- 線程内部的存儲類,賦予了線程存儲資料的能力;
- 線程内調用的方法都可以從ThreadLocal中擷取同一個對象
- 多線程中ThreadLocal資料互相隔離
定義ThreadLocal工具類,調用set方法将資料存入ThreadLocal中。
/**
* @Author zzw2000
* @Date 2022年04月15日 10:47
* @Description 向ThreadLocal中存入使用者資料
*/
public class UserHodler{
private static ThreadLocal<User> tl = new ThreadLocal<>();
//将使用者對象存入ThreadLocal對象中
public static void set(User user) {
tl.set(user);
}
//從目前線程擷取User對象
public static User getUser() {
return tl.get();
}
//從目前線程中擷取User對象的id
public static Long getId(){
return tl.get().getId();
}
//從目前線程中擷取使用者手機号
public static String getMobile(){
return tl.get().getMobile();
}
//清空線程中記錄的使用者資訊
public static void remove(){
tl.remove();
}
}
攔截器:
- 是一種動态攔截方法調用的機制;
- 類似于Servlet開發中的過濾器Filter,用于對處理器進行前置處理和後置處理。
攔截器工作原理
在前置攔截方法preHandle中解析token并驗證有效性,如果失效傳回狀态碼401(未經授權) ,若有效,解析User,并存入ThreadLocal。
/**
* @Author zzw2000
* @Date 2022年04月15日 10:27
* @Description 登入攔截
*/
public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
//擷取請求頭
String token = request.getHeader("Authorization");
//判斷token是否失效,
if(!JwtUtils.verifyToken(token)){
//token失效,設定狀态碼為401
response.setStatus(401);
//攔截
return false;
}
//token有效,解析token,擷取使用者id和手機号,構造使用者對象,存入ThreadLocal中
Claims claims = JwtUtils.getClaims(token);
Integer id = (Integer) claims.get("id");
String mobile = (String) claims.get("mobile");
User user = new User();
user.setId(Long.valueOf(id)); //字段id為Long類型的
user.setMobile(mobile);
//将user對象儲存到ThreadLocal
UserHolder.set(user);
//放行
retrun true;
}
}
注冊攔截器:
将上面的攔截器注冊到springboot中,交給springboot管理。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptor(InterceptorRegistry registry) {
String[] url = {"/user/login", "/login/loginVerification"};
registry.addInterceptor(new TokenInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(url);
}
}