重构

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);
    }