模仿某B长期登录有效之双Token机制
提示:需要有一定了解token机制的同学进行学习
对于某B如何长时间处于登录状态发起思考
大家应该都有用过某B吧,不知道使用某B的同学有没有发现一个问题,别人的网站,登录过后,一段时间不使用就自动退出了,而某B,就算你长时间不使用还是处于登录状态。并且所有的网站也都是你用多久就是多久,不会出现你用到一半就突然退出的情况。那么对于这种情况,本人对此做出了深深的思考,偷偷的查询了资料过后,发现大伙提到了一种名叫双token的东西。
在我简单的demo使用过后呢,发现,确实不错。本文思路有借鉴都绝无抄袭,大部分想法和处理都为自己独立思考而成。不喜勿喷。谢谢。
提示:以下案例可供参考
一、何为双Token?
顾名思义:两个token,在基础idToken之上,添加一个新的refreshToken,
在jwt的官网解释为:Refresh Tokens
二、使用步骤
1.实现思路
思路真的非常非常的重要,get不到双token的那个点,就会想不通这和普通的单token的区别在哪里,本文仅展示自己对于jwt工具双token的处理方式,如有雷同,纯属巧合。
在登录请求后发放双token返回给前端,原因:主要进行认证的token其实还是idToken,只有在短期token过期时,jwt工具对token进行解析失败的情况下,才会进行对refreshToken进行处理,在实际上,refreshToken与idToken在生产时的参数可以是相同的,也可以是不同的,具体逻辑具体分析即可。
双token传输到后端后,会先校验idToken,idToken如果没有过期直接成功登录,不会去刷新token令牌(这里也可以选择刷新,我觉得是没有必要的),
如果idToken过期,那么在解析时就会出现超时异常,捕获异常继续解析refreshToken即可,如若refreshToken也出现超时异常,则拦截器进行拦截,让用户进行重新登录即可。在这里可对token异常单独做出处理。如果refreshToken持续有效的情况下,则操作成功,并让前端请求token刷新接口即可。
这时可能会出现,前一秒refreshToken未过期,但请求刷新接口时过期的可能性,那么此时jwt解析body用户信息时也会出现超时异常,此时接口直接返回相应跳转code或者直接进行页面跳转即可。系统无法再为用户刷新token,只能让用户重新登录。
拦截器处理代码(示例):
以下shortToken代表idToken,longToken代表refrshToken
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
if (!(object instanceof HandlerMethod)) {
return true;
}
String shortToken = request.getHeader("authorization");// 从 http 请求头中取出 token
String longToken = request.getHeader("authorization-long");
log.info("短期token:{}", shortToken);
log.info("长期token:{}", longToken);
// 执行认证
if (null == shortToken && null == longToken) {
throw new RuntimeException("无token,请重新登录");
}
String phone = null;
Claims shortTokenClaim = null;
Claims longTokenClaim = null;
if (null != shortToken) {
try {
// 先验证短期token
shortTokenClaim = tokenObj.getTokenClaim(shortToken);
phone = shortTokenClaim.getSubject();
if (null == phone) {
throw new RuntimeException("用户不存在,请重新登录");
}
log.info("通过短期token登录成功");
return true;
} catch (Exception j) {
log.debug("短期token过期,继续检测长期token");
}
}
try {
log.info("检查长期令牌加长期令牌时间");
longTokenClaim = tokenObj.getTokenClaim(longToken);
phone = longTokenClaim.getSubject();
if (null == phone) {
throw new RuntimeException("用户不存在,请重新登录");
}
log.info("通过长期token登录成功,需要刷新token");
} catch (Exception e) {
log.error("长期令牌已失效请重新登录");
return false;
}
return true;
}
工具类(示例):
package com.mrone.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Service
public class JwtUtil {
private String secret= "NjYwQ0Y0N0M3ODhFODYwOUU2RDM1RTNDRkNENjQ1REE=";
private long expire= 3600;
private String header="token";
public String getLongTimeToken (String phone){
Date nowDate = new Date();
//过期时间 两天后的这个点
Date expireDate = new Date(nowDate.getTime() + expire * 1000 * 47);
return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(phone)
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
//一分钟后过期
public String getShortTimeToken (String phone){
Date nowDate = new Date();
Date expireDate = new Date(nowDate.getTime() + expire*100 );
return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(phone)
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
//刷新Token
public Map refresh(String auth){
Map<String,String> map = new HashMap<>();
try{
String phone = getTokenClaim(auth).getSubject();
String shortTimeToken = getShortTimeToken(phone);
String longTimeToken = getLongTimeToken(phone);
map.put("short",shortTimeToken);
map.put("long",longTimeToken);
}catch (Exception e){
System.out.println("长期token过期,请重新登录");
map.put("202","请重新登录");
}
return map;
}
/*
* 获取 Token 中注册信息
*/
public Claims getTokenClaim (String token) {
try {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
刷新接口(示例):
@ApiOperation(value = "admin",tags = "token刷新")
@PostMapping("/refresh")
public Map refresh(HttpServletRequest request){
String authorization = request.getHeader("authorization-long");
return jwtUtil.refresh(authorization);
}
总结
提示:以上内容仅供参考:
如需demo案例请私信我。
小程序地址:https://github.com/CaseOfShe/school
演示地址:https://www.bilibili.com/video/BV1q3411g71P