重构
1、使用Redis存储验证码
- 验证码需要频繁的访问和刷新,对性能的要求较高
- 验证码无需永久保存,通常在很短的时间后就失效
- 分布式部署时,存在session共享的问题
2、使用Redis存储登录凭证 - 处理每次请求时,都要查询用户的登录凭证,访问的频率非常高
3、使用Redis缓存用户信息 - 处理每次请求时,都要根据凭证查询用户信息,访问的频率非常高
1、Redis存储验证码
1.新增rediskey
/** * 登录验证码 * @param owner 临时登录凭证 * @return */ public static String getKaptchaKey(String owner){ return PREFIX_KAPTCHA + SPLIT + owner; }
2.controller中将原先从session中获取验证码,改为从redis中获取
当用户没登陆的时候,向客户端发放一个cookie,再将这个cookie的值存入到数据库中,
当点击登录的时候,通过cooike从redis中获取获取验证码
@RequestMapping(value = "/kaptcha", method = RequestMethod.GET) public void getKaptcha(HttpServletResponse response /*HttpSession session*/) { //生成验证码 //此前已经将Producer注入到Spring容器里了 String text = kaptchaProducer.createText(); BufferedImage image = kaptchaProducer.createImage(text); //Ed---0.2 //设置验证码的归属 String kaptchaOwner = CommunityUtil.generateUUID(); Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner); cookie.setMaxAge(60); cookie.setPath(contextPath); response.addCookie(cookie); //将验证码存入Redis String kaptchaKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner); redisTemplate.opsForValue().set(kaptchaKey, text, 60, TimeUnit.SECONDS); //将图片输出给浏览器 response.setContentType("image/png"); ServletOutputStream os = null; try { os = response.getOutputStream(); ImageIO.write(image, "png", os); } catch (IOException e) { logger.error("相应验证码失败" + e.getMessage()); } } @RequestMapping(value = "/login", method = RequestMethod.POST) public String login(String username, String password, String code, boolean rememberme, Model model, HttpSession session, HttpServletResponse response, @CookieValue("kaptchaOwner") String kaptchaOwner) { String kaptcha = null; if (StringUtils.isNotBlank(kaptchaOwner)) { String kaptchaKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner); kaptcha = (String) redisTemplate.opsForValue().get(kaptchaKey); } if (StringUtils.isBlank(code) || StringUtils.isBlank(kaptcha) || !kaptcha.equalsIgnoreCase(code)) { model.addAttribute("codeMsg", "验证码不正确"); return "/site/login"; } //检查账号,密码 int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS; Map<String, Object> map = userService.login(username, password, expiredSeconds); if (map.containsKey("ticket")) { //登录成功,发放cookie Cookie cookie = new Cookie("ticket", map.get("ticket").toString()); cookie.setPath(contextPath); cookie.setMaxAge(expiredSeconds); response.addCookie(cookie); return "redirect:/index"; } else { model.addAttribute("usernameMsg", map.get("usernameMsg")); model.addAttribute("passwordMsg", map.get("passwordMsg")); return "/site/login"; } }
2、Redis存储登录凭证
/** * 登录的凭证 * @param ticket * @return */ public static String getTicketKey(String ticket){ return PREFIX_TICKET + SPLIT + ticket; }
将原来的插入到MySQL中更改为插入到Redis中,提高效率
登出时,将状态设置为1
要获取ticket时,直接从Redis中获取
![图片说明]
(https://uploadfiles.nowcoder.com/images/20200426/725155193_1587884300913_240F9BF75D686739BC3D04DE4F9BCC23 "图片标题")
3.使用Redis缓存用户信息
缓存策略:
优先从缓存中取值,取不到就初始化,数据变更时清楚缓存
//1.优先从缓存中取值 public User getCache(int userId) { String userKey = RedisKeyUtil.getUserKey(userId); User user = (User) redisTemplate.opsForValue().get(userKey); return user; } //2.取不到时初始化缓存数据 public User initCache(int userId) { String userKey = RedisKeyUtil.getUserKey(userId); User user = userMapper.selectById(userId); redisTemplate.opsForValue().set(userKey, user, 3600, TimeUnit.SECONDS); return user; } //3.数据变更时清楚缓存数据 public void clearCache(int userId) { String userKey = RedisKeyUtil.getUserKey(userId); redisTemplate.delete(userKey); }