Article directory
1. Introduction
JWT itself is a string, which can save some information and set the expiration time. Next, encrypt the user name to generate a token string. The token is used to verify whether the login status has expired.通常使用 Redis + jwt 实现刷新不掉线,小项目可以不使用 Redis
Refresh can not drop the principle, the data format is as follows: take token-1 as an example
- The data format is as follows, set the two-tier data expiration time
- The token passed each time in the front-end header request header is the key of token-1, and the token in the front-end header request header does not need to be changed
- The expiration time of jwt1 is 15 minutes, and the expiration time of token-1 is 30 minutes
- If the interceptor judges that jwt1 has expired but token-1 has not expired, the value of token-1 will be regenerated
- Write a timed task to regularly judge whether token-1 has expired
{
"token-1": {
"value": "jwt1",
"createTime": 2022 - 11 - 09 17: 30: 01,
"saveTime": 30 * 60
},
"token-2": {
"value": "jwt2",
"createTime": 2022 - 11 - 09 18: 30: 01,
"saveTime": 30 * 60
}
}
For detailed usage of JWT, please refer to https://yixiu.blog.csdn.net/article/details/118409124
A simple example of a springboot project is as follows
2. Add token entity class
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TokenEntity {
// 保存 jwt 加密的字符串
private String value;
// 创建时间
private Date createTime;
// 保存时间,单位 s
private int saveTime;
}
3. Add jwt tool class
The jwt tool class includes creating tokens, verifying tokens, and getting usernames from tokens
@Component
@Slf4j
public class TokenUtil {
/* 保存 token */
public Map<String, TokenEntity> map = new HashMap<>();
/* jwt 过期时间 :30*60 s */
@Value("${jwt.expireTime}")
public int expireTime;
/* jwt 加密密码 : 123456 */
@Value("${jwt.password}")
public String password;
@Autowired
UserService userService;
/**
* Description: 根据 userName 生成 token
*/
public String createToken(String userName) {
return JWT.create().withHeader(new HashMap<>())
.withClaim("userName", userName)
.withExpiresAt(Instant.now().plusSeconds(expireTime / 2)) // 设置过期时间
.sign(Algorithm.HMAC512(password));
}
/**
* Description: 从 token 中获取用户名
*/
public String getUserName(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("userName").asString();
} catch (Exception e) {
return null;
}
}
/**
* Description: 校验 token 有效性
*/
public boolean checkToken(String token) {
try {
// 根据密码生成JWT效验器
Algorithm algorithm = Algorithm.HMAC512(password);
JWTVerifier verifier = JWT.require(algorithm).build();
// 效验TOKEN
String userName = verifier.verify(token).getClaim("userName").asString();
if (StringUtils.isEmpty(userName)) return false;
// 查询 userName 是否存在
User user = userService.getUserName(userName);
return null!=user ;
} catch (Exception e) {
log.error("JWT 校验用户token失败");
return false;
}
}
}
4. Check the token in the interceptor
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Autowired
LoginInterceptor loginInterceptor;
/**
* Description: 拦截接口
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**")// 拦截所有请求,包括静态资源
.excludePathPatterns("/user/login"); // 放行登录接口
}
}
@Slf4j
@Component
class LoginInterceptor implements HandlerInterceptor {
@Autowired
TokenUtil tokenUtil;
/**
* Description: 检验 token
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Headers", "*");
String token = request.getHeader("TOKEN");
// 判断前端 token 不为空,并且保存 token 的 map 对象中存在该 key
if (StringUtils.isNotEmpty(token) && null != tokenUtil.map.get(token)) {
// 校验 jwt 是否过期
if (!tokenUtil.checkToken(tokenUtil.map.get(token).getValue())) {
String userName = tokenUtil.getUserName(token);
if (StringUtils.isNotEmpty(userName)) {
String newToken = tokenUtil.createToken(userName);
// System.out.println("newToken--------------->" + newToken);
TokenEntity tokenEntity = new TokenEntity(newToken, new Date(), tokenUtil.expireTime);
tokenUtil.map.put(token, tokenEntity);
}
}
return true;
}
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(JSONObject.toJSONString(new JSONObject().put("error","TOKEN 无效")));
log.error("TOKEN 无效");
return false;
}
}
5. Timed task delete token
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(CadreReserveApplication.class);
ConfigurableApplicationContext applicationContext = springApplication.run(args);
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
TokenUtil tokenUtil = applicationContext.getBean(TokenUtil.class);
Map<String, TokenEntity> map = tokenUtil.map;
for (Map.Entry<String, TokenEntity> entry : map.entrySet()) {
TokenEntity tokenEntity = entry.getValue();
long nowTime = System.currentTimeMillis();
long createTime = tokenEntity.getCreateTime().getTime();
// 判断 token 是否过期,创建时间 + 保存时间小于当前时间则删除
if (nowTime > createTime + tokenEntity.getSaveTime() * 1000L) {
map.remove(entry.getKey());
log.info("-------------- 删除 token 成功 --------------");
}
}
}
};
Timer timer = new Timer();
// 启动延迟 3 s 执行,执行周期间隔 1 s
timer.schedule(timerTask, 3000, 1000L);
}
6. The login interface generates a token
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@Autowired
TokenUtil tokenUtil;
@PostMapping("/login")
public Result<?> login(@RequestBody User user) {
// 如果登陆校验成功,生成 token 返回前端
if (userService.login(user)) {
String token = tokenUtil.createToken(user.getUserName());
TokenEntity tokenEntity = new TokenEntity(token, new Date(), tokenUtil.expireTime / 2);
tokenUtil.map.put(token, tokenEntity);
return Result.OK(user, token);
}
return Result.error("登录失败");
}
}