一、token定时刷新的意义
因为登录后获得的token是有时间限制的,也即是有效期,刷新token一是可以延续token,二是可以保证安全,即使token被人截获 刷新token后也还是很安全的。
二、大致实现步骤
在前端登录后的主页 的公共模块或公共组件里面设置一个Interval,也即是每隔一定周期 去请求后台,通过先前的token得到一个新token,然后前端获取。
三、代码实现:
1.后台:
/**
* @Description: 刷新token
* @Author: tony
* @Date: 2020/1/22 15:40
**/
@RestController
@RequestMapping("/")
public class TokenController {
private final Logger logger= LoggerFactory.getLogger(TokenController.class);
/**
* 刷新用户token
* @param request
* @return
*/
@GetMapping(value = "/refreshToken")
public R refreshToken(HttpServletRequest request){
//这里生成claims,下面就可以根据claims得到id和用户名 从而索取到新的token
Claims claims = JwtUtils.validateJWT(request.getHeader("token")).getClaims();
String newToken = JwtUtils.createJWT(claims.getId(), claims.getSubject(), SystemConstant.JWT_TTL);
R r = new R();
r.put("token",newToken);
logger.info("新token: "+newToken);
return r;
}
}
2.Jwt工具类:
/**
* jwt加密和解密的工具类
*/
public class JwtUtils {
/** 比如用户输入用户名和密码 若登录合法就生成jwt的一个token 发送给前端 前端存储到localStorage
* 然后用户每次发起请求都从本地获取 携带着一个token送至后台 去验证 从而完成身份的验证
* 签发JWT
* @param id 一般是用户id
* @param subject 一般是用户名 可以是JSON数据 尽可能少
* @param ttlMillis 有效期 这里是1小时
* @return
*/
public static String createJWT(String id, String subject, long ttlMillis) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
SecretKey secretKey = generalKey();
JwtBuilder builder = Jwts.builder()
.setId(id)
.setSubject(subject) // 主题
.setIssuer("Java1234") // 签发者
.setIssuedAt(now) // 签发时间
.signWith(signatureAlgorithm, secretKey); // 签名算法以及密匙
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
builder.setExpiration(expDate); // 过期时间
}
return builder.compact();
}
/**
* 验证JWT 需要把jwt的token传过来
* @param jwtStr
* @return
*/
public static CheckResult validateJWT(String jwtStr) {
CheckResult checkResult = new CheckResult();
Claims claims = null;
try {
claims = parseJWT(jwtStr); // 里面包含有id,subject(用户名),有效期等信息
checkResult.setSuccess(true); //封装进checkResult
checkResult.setClaims(claims);
} catch (ExpiredJwtException e) {
checkResult.setErrCode(SystemConstant.JWT_ERRCODE_EXPIRE);
checkResult.setSuccess(false);
} catch (SignatureException e) {
checkResult.setErrCode(SystemConstant.JWT_ERRCODE_FAIL);
checkResult.setSuccess(false);
} catch (Exception e) {
checkResult.setErrCode(SystemConstant.JWT_ERRCODE_FAIL);
checkResult.setSuccess(false);
}
return checkResult;
}
/**
* 生成加密Key
* 解密时也要用到 因为在刷新token时要通过原来生成的token解密获取用户id 和用户名(subject) 重新生成token
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.decode(SystemConstant.JWT_SECERT);
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
/**
* 解析JWT字符串
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
}
public static void main(String[] args) throws InterruptedException {
// 后端生成token
String sc=createJWT("1","jack",SystemConstant.JWT_TTL);
System.out.println(sc);
// 后端验证token
CheckResult checkResult = validateJWT(sc);
System.out.println(checkResult.isSuccess()); //true为验证成功
System.out.println(checkResult.getErrCode());// code为0 表示无异常
Claims claims=checkResult.getClaims();
System.out.println(claims);
System.out.println(claims.getId());
System.out.println(claims.getSubject());
// 刷新token 重新生成token 和之前的token不同
//刷新token时要通过原来生成的token解密获取用户id 和用户名(subject) 重新生成token
Claims claims2=validateJWT(sc).getClaims();
String sc2=createJWT(claims2.getId(),claims2.getSubject(),SystemConstant.JWT_TTL);
System.out.println(sc2);
}
}
3.前端:
export default {
name: "PhoneBook",
methods: {
refreshToken(){
let token = window.localStorage.getItem("token");
axios.defaults.headers.common['token'] = token
let url = getServerUrl("refreshToken");
axios.get(url)
.then(response=>{
console.log("token刷新: "+response.data.token)
window.localStorage.setItem("token",response.data.token);
}).catch(error=>{
console.log(error)
})
},
getPhoneBooks() {
let token = localStorage.getItem("token");
alert(token)
axios.defaults.headers.common['token']=token;
let url = getServerUrl("phoneBook/loadAll");
axios.get(url)
.then(response=>{
console.log(response)
}).catch(error=>{
console.log(error)
})
}
},
mounted() {
setInterval(this.refreshToken,1000*60*10);//每10分钟刷新一次token
this.getPhoneBooks(); //加载所有电话簿信息
}
}