1、SpringBoot集成redis
<!--redis坐标-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@SpringBootTest//如果在测试类上添加了这个注解,那么将来单元测试方法执行之前,会先初始化Spring容器
public class RedisTest {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void testSet(){
//往redis中存储一个键值对 StringRedisTemplate
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.set("username","zhangsan");
operations.set("id","1",15, TimeUnit.SECONDS);
}
@Test
public void testGet(){
//从redis中获取一个键值对
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
System.out.println(operations.get("id"));
}
}
2、令牌主动失效机制
- 登录成功后,给浏览器响应令牌的同时,把该令牌存储到redis中
- LoginInterceptor拦截器中,需要验证浏览器携带的令牌,并同时需要获取到redis中存储的与之相同的令牌
- 当用户修改密码成功后,删除redis中存储的旧令牌
controller->UserController
登录
@PostMapping("/login")
public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password) {
//根据用户名查询用户
User loginUser = userService.findByUserName(username);
//判断该用户是否存在
if (loginUser == null) {
return Result.error("用户名错误");
}
//判断密码是否正确 loginUser对象中的password是密文
if (Md5Util.getMD5String(password).equals(loginUser.getPassword())) {
//登录成功
Map<String, Object> claims = new HashMap<>();
claims.put("id", loginUser.getId());
claims.put("username", loginUser.getUsername());
String token = JwtUtil.genToken(claims);
//把token存储到redis中
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.set(token,token,1, TimeUnit.HOURS);
return Result.success(token);
}
return Result.error("密码错误");
}
注册:
@PatchMapping("/updatePwd") public Result updatePwd(@RequestBody Map<String, String> params,@RequestHeader("Authorization") String token) { //1.校验参数 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("缺少必要的参数"); } //原密码是否正确 //调用userService根据用户名拿到原密码,再和old_pwd比对 Map<String,Object> map = ThreadLocalUtil.get(); String username = (String) map.get("username"); User loginUser = userService.findByUserName(username); if (!loginUser.getPassword().equals(Md5Util.getMD5String(oldPwd))){ return Result.error("原密码填写不正确"); } //newPwd和rePwd是否一样 if (!rePwd.equals(newPwd)){ return Result.error("两次填写的新密码不一样"); } //2.调用service完成密码更新 userService.updatePwd(newPwd); //删除redis中对应的token ValueOperations<String, String> operations = stringRedisTemplate.opsForValue(); operations.getOperations().delete(token); return Result.success(); }
interceptors->LoginInterceptor
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//令牌验证
String token = request.getHeader("Authorization");
//验证token
try {
//从redis中获取相同的token
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
String redisToken = operations.get(token);
if (redisToken==null){
//token已经失效了
throw new RuntimeException();
}
Map<String, Object> claims = JwtUtil.parseToken(token);
//把业务数据存储到ThreadLocal中
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 {
//清空ThreadLocal中的数据
ThreadLocalUtil.remove();
}
}
config->WebConfig
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//登录接口和注册接口不拦截
registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");
}
}