SpringBoot实战4
一、优化登录机制通过Redis
在我们使用JWT令牌进行登录验证的时候,不可避免的也会出现一些问题。比如:当用户修改密码后,旧的令牌仍然生效,任然可以用于进行登录操作。因此,我们可以采用**Redis
**进行登录的优化。
SpringBoot集成Redis
1、导入Redis起步依赖
2、在yml配置文件中配置Redis的连接信息
3、调用API完成字符串的存取操作
<!--导入Redis的起步依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
yml
spring:data:redis:host: localhostport: 6379
@SpringBootTest//如果说在测试类上面添加了这个注解,那么在单元测试方法之前会先初始化Spring容器,这样就可以获取到IOC容器中的bean对象
public class RedisTest {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Testpublic void testSet(){//网Redis中存储一个键值对ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();operations.set("name","zhangsan");operations.set("id","1",15, TimeUnit.SECONDS);}@Testpublic void testGet(){//从Redis中获取一个键值对ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();String name = operations.get("name");System.out.println(name);}
}
登录优化
实现方法步骤
-
登录成功的时候,给浏览器响应令牌的同时,将对应的用户信息的令牌存入Redis中
// 登录成功 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,12, TimeUnit.HOURS);
//TODO: 登录 @PostMapping("/login") public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username,@Pattern(regexp = "^\\S{5,16}$") String password) {// TODO: 查询用户是否存在User loginUser = userService.findByUserName(username);// TODO: 判断用户是否存在if(loginUser==null){return Result.error("用户不存在");}// TODO: 判断密码是否正确else{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,12, TimeUnit.HOURS);return Result.success(token);}else {return Result.error("密码错误");}} }
-
LoginInterceptor拦截器中,需要验证浏览器携带的令牌,同时需要获取到Redis中存储与之对应的相同的令牌
//从Redis中获取相同的token ValueOperations<String, String> operations = stringRedisTemplate.opsForValue(); String redisToken = operations.get(token); if (redisToken == null){ //redis中的token已经失效 throw new RuntimeException("token已失效"); }
@Autowired private StringRedisTemplate stringRedisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//在这个拦截器中验证Token---》通过请求头中携带的的tokenString token = request.getHeader("Authorization");//解析Tokentry {//从Redis中获取相同的tokenValueOperations<String, String> operations = stringRedisTemplate.opsForValue();String redisToken = operations.get(token);if (redisToken == null){//redis中的token已经失效throw new RuntimeException("token已失效");}Map<String, Object> claims = JwtUtil.parseToken(token);//TODO:将相关的业务数据存到ThreadLocal中-->方便后续业务逻辑中获取ThreadLocalUtil.set(claims);//TODO:如果解析成功,则放行return true;} catch (Exception e) {response.setStatus(401);//TODO:如果解析失败,则返回错误信息,并且拦截return false;} }
-
修改密码的时候就删除旧的令牌
//删除Redis中对应的token ValueOperations<String, String> operations = stringRedisTemplate.opsForValue(); RedisOperations<String, String> redisOperations = operations.getOperations(); redisOperations.delete(token);
// TODO: 更新用户的密码 @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(oldPwd==null||newPwd==null||rePwd==null){return Result.error("参数不能为空");}//判断原密码是否正确---》调用Service根据用户名获取原密码,和oldPwd进行比较//用户名通过ThreadLocalUtil存储获取Map<String,Object> map = ThreadLocalUtil.get();String username = (String) map.get("username");User user = userService.findByUserName(username);String pwd = user.getPassword();//原有的密码是进行了加密的,所以需要先对用户输入的密码进行加密再进行比较if(!Md5Util.getMD5String(oldPwd).equals(pwd)){return Result.error("原密码错误");}//判断新密码和确认密码是否一致if(!newPwd.equals(rePwd)){return Result.error("新密码和确认密码不一致");}//2、调用Service完成更新密码userService.updatePwd(newPwd);//删除Redis中对应的tokenValueOperations<String, String> operations = stringRedisTemplate.opsForValue();RedisOperations<String, String> redisOperations = operations.getOperations();redisOperations.delete(token);return Result.success(); }
-
拦截器中进行再次校验
项目部署
打成jar包,引入依赖,并通过Maven的package进行打包
<build><plugins><!--配置打包插件--><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>3.4.4</version></plugin></plugins>
</build>
得到的jar包
在对应的目录下打开cmd命令行窗口
执行java -jar big-event-1.0-SNAPSHOT.jar(输入了java -jar之后按
Tab键自动补全)
SpringBoot属性配置(三种方式的优先级从低到高)
1、通过properties或者yml
2、命令行中进行修改(–键=值)
java -jar big-event-1.0-SNAPSHOT.jar --server.port=9090
3、通过在jar包目录下加上一个application.yml的配置文件指定对应的配置信息