一.实现思路:
- 登录成功后,给浏览器响应令牌的同时,把该令牌存储到Redis中
- LoginInterceptor拦截器中,需要验证浏览器携带的令牌,并同时需要获取到Redis中的存储的与之相同的令牌,如果获取到证明令牌有效,如果没有获取到则令牌无效
- 当用户修改密码成功后,删除Redis中存储的旧令牌
二.实现代码解析:
1.首先配置Redis的相关配置文件:
(1)pom.xml:
1 | org.springframework.bootspring-boot-starter-data-redis |
(2)在application.yml中配置相关信息:
1 2 3 4 5 6 7 | spring: data: redis: host: localhost port: 6379 password: ******* #Redis的密码 database: *** #指定使用哪个数据源(可不写,但默认是DB0) |
(3)编写配置类,创建RedisTemplate对象:
我们使用RedisTemplate这个类对象内封装的方法来操作Redis,所以我们在配置类内创建RedisTemplate并用Bean注解将其交给Spring容器管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Configuration @Slf4j public class RedisConfiguration { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){ log.info( "开始创建Redis模板对象...0" ); RedisTemplate redisTemplate = new RedisTemplate(); //设置redis的连接工厂对象 redisTemplate.setConnectionFactory(redisConnectionFactory); //设置redis key的序列化器,这里反应的是Redis图形化工具上value值的不同 redisTemplate.setKeySerializer( new StringRedisSerializer()); return redisTemplate; } } |
2.登录成功后,给浏览器响应令牌的同时,把该令牌存储到Redis中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | @RestController @RequestMapping ( "/user" ) @Validated public class UserController { @Autowired private UserService userService; @Autowired private RedisTemplate redisTemplate; /** * 用户登录 * @param username * @param password * @return */ @PostMapping ( "/login" ) public Result login( @Pattern (regexp = "^\S{5,16}$" ) String username , @Pattern (regexp = "^\S{5,16}$" ) String password){ //根据username查询用户 User loginUser = userService.findByUserName(username); //判断是否存在 if (loginUser == null ){ return Result.error( "用户名错误" ); } //判断密码是否正确 if (Md5Util.getMD5String(password).equals(loginUser.getPassword())){ //登录成功,创建JWT令牌 Map claims = new HashMap(); claims.put( "id" ,loginUser.getId()); claims.put( "username" ,loginUser.getUsername()); String token = JwtUtil.genToken(claims); //将token存储到redis中 redisTemplate.opsForValue().set(token,token, 1 , TimeUnit.HOURS); //key,value,时间值,时间单位 return Result.success(token); } return Result.error( "密码错误" ); } } |
3.LoginInterceptor拦截器中,需要验证浏览器携带的令牌,并同时需要获取到Redis中的存储的与之相同的令牌:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | package com.itheima.bigdealboot.interceptors; import com.itheima.bigdealboot.utils.JwtUtil; import com.itheima.bigdealboot.utils.ThreadLocalUtil; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import java.util.Map; //拦截器 @Component public class LoginInterceptor implements HandlerInterceptor { @Autowired private RedisTemplate redisTemplate; @Override public boolean preHandle(HttpServletRequest request , HttpServletResponse response , Object handle) throws Exception{ String token = request.getHeader( "Authorization" ); //Authorization为请求头的名字 try { //从Redis中国获取相同的token String redisToken = (String) redisTemplate.opsForValue().get(token); if (redisToken == null ){ //token已经失效,抛出异常 throw new RuntimeException(); } //令牌验证 Map claims = JwtUtil.parseToken(token); //线程开辟空间存储 ThreadLocalUtil.set(claims); //放行 return true ; } catch (Exception e){ //http响应状态码为401在· response.setStatus( 401 ); //不放行 return false ; } } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { //清空线程数据 ThreadLocalUtil.remove(); } } |
4.当用户修改密码成功后,删除Redis中存储的旧令牌:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | @RestController @RequestMapping ( "/user" ) @Validated public class UserController { @Autowired private UserService userService; @Autowired private RedisTemplate redisTemplate; @PatchMapping ( "/updatePwd" ) public Result updatePwd( @RequestBody Map params, @RequestHeader ( "Authorization" ) String token){ //校验参数 String oldPwd = params.get( "old_pwd" ); String newPwd = params.get( "new_pwd" ); String rePwd = params.get( "re_pwd" ); if (StringUtils.hasLength(oldPwd) || StringUtils.hasLength(newPwd) || StringUtils.hasLength(rePwd)){ return Result.error( "缺少必要参数" ); } //校验 Map map = ThreadLocalUtil.get(); String username = (String) map.get( "username" ); User loginUser = userService.findByUserName(username); if (!loginUser.getPassword().equals(Md5Util.getMD5String(oldPwd))){ return Result.error( "原密码填写不正确" ); } if (!rePwd.equals(newPwd)){ return Result.error( "两次填写的新密码不一样" ); } //更新密码 userService.updatePwd(newPwd); //删除redis中对应的token redisTemplate.opsForValue().getOperations().delete(token); return Result.success(); } } |
到此这篇关于使用Redis实现JWT令牌主动失效机制的文章就介绍到这了,更多相关Redis JWT主动失效机制内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!