需求:更新后数据存储及作废(时效性)
作用:储存数据(用于比对)
eg:用户修改信息后的旧jwt令牌,(令牌主动失效机制)
内存:
磁盘:
用法:存储、获取、过期时间
用户登录验证
通过令牌自动失效机制,实现用户信息修改的安全性
准备工作
安装redis:(我的资源里面应该是有的)
依赖引入:
资源配置(要先打开redis要不然不成功):
注意!!redis的存储数据需要设置过期时间(一般与jwt过期时间一致)
配置依赖.pml
引入依赖
<!-- redis起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
配置资源
记得要先打开redis,并用自己的修改
spring: data: redis: host: loclocalhost port: 6379
拦截器LoginInterceptor
接口调用前的统一拦截(例如门外大爷!!)
(token中的jwt令牌就是过门令)
不理解也没关系可以直接用
@Component public class LoginInterceptor implements HandlerInterceptor { public static final String AUTHORIZATION_HEADER = "Authorization"; //需要注入资源才能使用redis @Autowired private StringRedisTemplate stringRedisTemplate; @Override public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) { final String token = request.getHeader(AUTHORIZATION_HEADER); try { // Get same token from redis. final ValueOperations<String, String> operations = stringRedisTemplate.opsForValue(); final String redisToken = operations.get(token); if (redisToken == null) { // Token has expired throw new RuntimeException("过期或未登录"); } final Map<String, Object> map = JwtUtil.parseToken(token); //解析token //将解析后信息存储到 本地线程中 ThreadLocalUtil.set(map); return true; } catch (Exception e) { response.setStatus(401); return false; } } @Override public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) throws Exception { //清空ThreadLocal中的数据 防止内存泄漏!!(数据存储时间很长,不要了需要清除) ThreadLocalUtil.remove(); } }
代码实现
controller
将用户的数据保存到redis
@PostMapping("/login") public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") final String username, @Pattern(regexp = "^\\S{5,16}$") final String password) { final User existingUser = userService.findByUsername(username); if (existingUser == null) { return Result.error("用户不存在"); } if (Md5Util.getMD5String(password).equals(existingUser.getPassword())) { final Map<String, Object> map = Map.of("id", existingUser.getId(), "username", existingUser.getUsername()); //封装数据用于jwt令牌生成 final String token = JwtUtil.genToken(map); //生成jwt令牌 // Save token to redis final ValueOperations<String, String> operations = stringRedisTemplate.opsForValue(); // If key is token, and when we validate, and the token doesn't exist, then it has expired. operations.set(token, token, Duration.ofHours(12));//请求头的数据,redis的数据,过期时间三个参数 // Return JWT token return Result.success(token); } else { return Result.error("密码错误"); } }
用户修改数据后时候需要删除redis内数据
@PatchMapping("/updatePwd") public Result<String> updatePwd(@RequestBody Map<String, String> params, @RequestHeader(AUTHORIZATION_HEADER) final String token) { final String oldPwd = params.get("old_pwd"); final String newPwd = params.get("new_pwd"); final String rePwd = params.get("re_pwd"); if (!StringUtils.hasLength(oldPwd) || !StringUtils.hasLength(newPwd) || !StringUtils.hasLength(rePwd)) { return Result.error("缺少必要的参数"); } final Map<String, Object> map = ThreadLocalUtil.get(); final String username = (String) map.get("username"); final User user = userService.findByUsername(username); if (!rePwd.equals(newPwd)) { return Result.error("两次结果不一样"); } if (!user.getPassword().equals(Md5Util.getMD5String(oldPwd))) { return Result.error("密码填写不正确"); } userService.updatePwd(newPwd); // Delete token in redis.!!!! final ValueOperations<String, String> operations = stringRedisTemplate.opsForValue(); //告诉redis删除的数据 @RequestHeader(AUTHORIZATION_HEADER) final String token operations.getOperations().delete(token); return Result.success(); }
修改密码需要和旧密码比对
获取请求头里面的数据
注意!!!
@RequestHeader(AUTHORIZATION_HEADER) final String token
加载和删除都需要先声明操作集合
// Delete token in redis.!!!! final ValueOperations<String, String> operations = stringRedisTemplate.opsForValue(); //告诉redis删除的数据 @RequestHeader(AUTHORIZATION_HEADER) final String token operations.getOperations().delete(token);
其他层代码
省略。