SpringBoot實(shí)現(xiàn)JWT token自動(dòng)續(xù)期的示例代碼
為什么要 token自動(dòng)續(xù)期
token中一般會(huì)包含用戶的基本信息,為了保證token的安全性,一般會(huì)將token的過期時(shí)間設(shè)置的比較短,但是這樣會(huì)導(dǎo)致用戶因?yàn)閠oken過期需要頻繁登錄,因此需要token自動(dòng)續(xù)期。
//創(chuàng)建token String token = JwtUtil.createToken(sysUser.getId(), user.getUserName()); //將token放入redis中,key為用戶的手機(jī)號(hào)+"token" redisUtil.set(sysUser.getPhone() + GlobalConstant.TOKEN, token, JwtUtil.EXPIRE_TIME*2);

在攔截器中重寫public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler)方法
/**
* token自動(dòng)續(xù)期
*
* @param sysUser 用戶實(shí)體
* @return 是否刷新成功
*/
private boolean refreshToken(SysUser sysUser) {
String token = request.getHeader(GlobalConstant.TOKEN);
String cacheToken = (String) (redisUtil.get(sysUser.getPhone() + GlobalConstant.TOKEN));
//請(qǐng)求頭中不存在token,返回false
if (StringUtil.isEmpty(token)) {
logger.error("請(qǐng)求頭中token不存在");
return false;
}
//用戶是否登錄只根據(jù)redis中token是否存在決定,redis中不存在token,返回false
if (StringUtil.isEmpty(cacheToken)) {
logger.error("用戶未登錄");
return false;
}
try {
//驗(yàn)證請(qǐng)求頭中的token是否合法
JwtUtil.verify(token);
} catch (TokenExpiredException tokenExpiredException) {
/*若拋出token過期異常,檢查請(qǐng)求頭中的token與redis中的token是否相同
如果相同,說明用戶仍在操作,只是請(qǐng)求頭中的token已經(jīng)過期,此時(shí)需要對(duì)token進(jìn)行續(xù)期*/
if (cacheToken.equals(token)) {
//重新刷新redis中的token的過期時(shí)間
redisUtil.set(sysUser.getPhone() + GlobalConstant.TOKEN, token, JwtUtil.EXPIRE_TIME * 2);
return true;
} else {
return false;
}
} catch (Exception e) {
//若拋出除token過期異常之外的其他異常,說明該token不合法
logger.error("token不合法");
return false;
}
return true;
}
攔截器所有代碼如下
@Component
public class LoginInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private SysUserDao sysUserDao;
@Resource
private RedisUtil redisUtil;
@Resource
private HttpServletRequest request;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
logger.info("進(jìn)入攔截器 uri:" + request.getRequestURI());
// 不是controller的方法不攔截
if (!(handler instanceof HandlerMethod)) {
return true;
}
//檢查方法上是否有@PreAuth注解,沒有則不攔截
HandlerMethod handlerMethod = (HandlerMethod) handler;
PreAuth preAuth = handlerMethod.getMethodAnnotation(PreAuth.class);
if (preAuth == null) {
return true;
}
Long userId = JwtUtil.getUserIdByToken(request);
SysUser sysUser = sysUserDao.selectById(userId);
//用戶不存在,進(jìn)行攔截
if (sysUser == null) {
logger.error("用戶不存在");
return false;
}
if (!refreshToken(sysUser)) {
return false;
}
//判斷用戶是否有對(duì)應(yīng)權(quán)限
Set<String> authList = this.sysUserDao.queryAuthList(userId);
if (!authList.contains(preAuth.value())) {
logger.error("無權(quán)限");
return false;
}
return true;
}
/**
* token自動(dòng)續(xù)期
*
* @param sysUser 用戶實(shí)體
* @return 是否刷新成功
*/
private boolean refreshToken(SysUser sysUser) {
String token = request.getHeader(GlobalConstant.TOKEN);
String cacheToken = (String) (redisUtil.get(sysUser.getPhone() + GlobalConstant.TOKEN));
//請(qǐng)求頭中不存在token,返回false
if (StringUtil.isEmpty(token)) {
logger.error("請(qǐng)求頭中token不存在");
return false;
}
//用戶是否登錄只根據(jù)redis中token是否存在決定,redis中不存在token,返回false
if (StringUtil.isEmpty(cacheToken)) {
logger.error("用戶未登錄");
return false;
}
try {
//驗(yàn)證請(qǐng)求頭中的token是否合法
JwtUtil.verify(token);
} catch (TokenExpiredException tokenExpiredException) {
/*若拋出token過期異常,檢查redis中的是否存在token以及請(qǐng)求頭中的token與redis中的token是否相同
如果相同,說明用戶仍在操作,只是請(qǐng)求頭中的token已經(jīng)過期,此時(shí)需要對(duì)token進(jìn)行續(xù)期*/
if (cacheToken.equals(token)) {
//重新刷新redis中的token的過期時(shí)間
redisUtil.set(sysUser.getPhone() + GlobalConstant.TOKEN, token, JwtUtil.EXPIRE_TIME * 60 * 2);
return true;
} else {
return false;
}
} catch (Exception e) {
//若拋出除token過期異常之外的其他異常,說明該token不合法
logger.error("token不合法");
return false;
}
return true;
}
}JwtUtil工具類如下
import com.admin.common.constant.GlobalConstant;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import javax.servlet.http.HttpServletRequest;
import java.util.Calendar;
import java.util.Date;
public class JwtUtil {
/**
* token私鑰,不可以暴露
*/
public static final String TOKEN_SECRET_KEY = "tokenSecretKey";
/**
* token過期時(shí)間(秒)
*/
public static final int EXPIRE_TIME = 60;
/**
* 創(chuàng)建token
*
* @param userId 用戶ID
* @param userName 用戶名
* @return token
*/
public static String createToken(Long userId, String userName) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, EXPIRE_TIME);
return JWT.create()
//簽發(fā)對(duì)象
.withAudience(userId + "")
//載荷
.withClaim("userName", userName)
//簽發(fā)時(shí)間
.withIssuedAt(new Date())
//有效時(shí)間
.withExpiresAt(calendar.getTime())
//加密
.sign(Algorithm.HMAC256(TOKEN_SECRET_KEY));
}
/**
* 驗(yàn)證token合法性
*
* @param token token
* @return token是否合法
*/
public static void verify(String token) {
JWT.require(Algorithm.HMAC256(TOKEN_SECRET_KEY)).build().verify(token);
}
/**
* 通過token獲取userId
*
* @return userId
*/
public static Long getUserIdByToken(HttpServletRequest request) {
String token = request.getHeader(GlobalConstant.TOKEN);
String userId = JWT.decode(token).getAudience().get(0);
return Long.valueOf(userId);
}
}到此這篇關(guān)于SpringBoot實(shí)現(xiàn)JWT token自動(dòng)續(xù)期的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot JWT token自動(dòng)續(xù)期內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Vue如何優(yōu)雅處理Token過期并自動(dòng)續(xù)期
- SpringBoot基于Redis實(shí)現(xiàn)token的在線續(xù)期的實(shí)踐
- springboot+vue實(shí)現(xiàn)Token自動(dòng)續(xù)期(雙Token方案)
- SpringBoot中Token登錄授權(quán)、續(xù)期和主動(dòng)終止的方案流程分析
- SpringBoot中基于JWT的單token授權(quán)和續(xù)期方案步驟詳解
- Spring?Boot實(shí)現(xiàn)JWT?token自動(dòng)續(xù)期的實(shí)現(xiàn)
- JAVA實(shí)現(xiàn)Token自動(dòng)續(xù)期機(jī)制的示例代碼
相關(guān)文章
SpringBoot使用Validation進(jìn)行參數(shù)校驗(yàn)的示例詳解
在 SpringBoot項(xiàng)目開發(fā)中,有一個(gè)觀點(diǎn)是不要相信前端傳入的參數(shù),因?yàn)槟悴恢烙脩羰窃趺床僮魑覀兘涌诘?,所以在后端也需要?duì)參數(shù)進(jìn)行校驗(yàn),這篇文章主要講講我們項(xiàng)目中最常使用的驗(yàn)證方案2023-05-05
Java Swing JProgressBar進(jìn)度條的實(shí)現(xiàn)示例
這篇文章主要介紹了Java Swing JProgressBar進(jìn)度條的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
SpringBoot 增量部署發(fā)布的實(shí)現(xiàn)步驟
本文介紹了通過拆分項(xiàng)目jar包和使用類加載器實(shí)現(xiàn)Spring Boot的增量部署,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
如何利用JAVA正則表達(dá)式輕松替換JSON中的大字段
這篇文章主要給大家介紹了關(guān)于如何利用JAVA正則表達(dá)式輕松替換JSON中大字段的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Springboot集成kafka高級(jí)應(yīng)用實(shí)戰(zhàn)分享
這篇文章主要介紹了Springboot集成kafka高級(jí)應(yīng)用實(shí)戰(zhàn)分享,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08
詳解Java中方法重寫與重載的區(qū)別(面試高頻問點(diǎn))
這篇文章主要介紹了Java中方法重寫與重載的區(qū)別(面試高頻問點(diǎn)),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03

