jwt guarantees that the front-end refresh will not drop

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("登录失败");
    }
}    

Guess you like

Origin blog.csdn.net/qq_41538097/article/details/127773921