【Java Web】用Redis优化登陆模块

  • 使用Redis存储验证码
    • 验证码需要频繁访问和封信,对性能要求高;
    • 验证码不需要永久保存,通常在很短时间内失效;
    • 分布式部署,存在Session共享问题;
  • 使用Redis存储登陆凭证
    • 处理每次请求时,都要查询用户登陆凭证,访问频率非常高;
  • 使用Redis缓存用户信息
    • 处理每次请求时,都要根据凭证查询用户信息,访问频率非常高。
      • 查询缓存中是否有user;
      • 如果没有就初始化缓存;
      • 如果user的信息更新,将清除缓存;

1. 使用Redis存储验证码

  • 在生成验证码的逻辑中,owner由UUID生成,并交给Cookie;然后将验证码text存到kaptcha:owner键中,并设置有效时间为60秒
java 复制代码
@RequestMapping(path="/kaptcha", method = RequestMethod.GET)
    public void getKaptcha(HttpServletResponse response, HttpSession session){
        // 生成验证码
        String text = kaptchaProducer.createText();
        BufferedImage image = kaptchaProducer.createImage(text);

        // 将验证码存入session
        // session.setAttribute("kaptcha", text);
        // 优化:存到redis里
        // 验证码的归属owner
        String kaptchaOwner = CommunityUtil.generateUUID();
        Cookie cookie = new Cookie("kaptchaOwner",kaptchaOwner);
        cookie.setMaxAge(60);
        cookie.setPath(context_path);
        response.addCookie(cookie);

        String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
        redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);

        // 将图片输出给浏览器
        response.setContentType("image/png");
        try{
            OutputStream os = response.getOutputStream();
            ImageIO.write(image, "png", os);
        } catch (IOException e) {
            logger.error("响应验证码失败:"+e.getMessage());
        }
    }
  • 在登陆功能中,键从cookie中去哪出,然后从redis中获取kaptcha:kaptchaOwner键对应的值(验证码),判断验证码是否正确;
java 复制代码
@RequestMapping(path = "/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){
        // 从Session中取并检查验证码 --> 优化:从Redis中取
        // String kaptcha = (String) session.getAttribute("kaptcha");
        String kaptcha = null;
        if(StringUtils.isNotBlank(kaptchaOwner)){
            String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
            kaptcha = (String) redisTemplate.opsForValue().get(redisKey);
        }

        if(StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equals(code)){
            model.addAttribute("codeMsg","验证码不正确");
            return "/site/login";
        }
        ......
        ......
        ......
        ......

2. 使用Redis存储登陆凭证

各个逻辑验证通过后,将生成一个用户凭证(UUID生成),存到Redis中,并设置生存周期。在后续需要验证登陆逻辑时,从Redis中get即可。

java 复制代码
 public Map<String,Object> login(String username, String password, int expiredSeconds){
        Map<String,Object> map = new HashMap<>();
        // 空值判断
        ....
        ....
        
        // 账号密码都不为空,验证合法性
        // 验证账号合法性
        .....
        .....
        
        // 验证密码
        ....
        ....
        
        // 生成登陆凭证
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(user.getId());
        loginTicket.setTicket(CommunityUtil.generateUUID());
        loginTicket.setStatus(0);
        loginTicket.setExpired(new Date(System.currentTimeMillis() + 1000 * 60 * 10));
        loginTicketMapper.insertLoginTicket(loginTicket);

        String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
        redisTemplate.opsForValue().set(redisKey,loginTicket);

        map.put("ticket", loginTicket.getTicket());
        return map;
    }

3. 使用Redis缓存用户信息

java 复制代码
// 1. 优先从缓存中取值
    public User getCache(int userId){
        String redisKey = RedisKeyUtil.getUserKey(userId);
        return (User) redisTemplate.opsForValue().get(redisKey);
    }
    // 2. 如果取不到就初始化缓存
    public User initCache(int userId){
        User user = userMapper.selectById(userId);
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS);
        return user;
    }

    // 3. 数据变更时清除缓存数据
    public void clearCache(int userId){
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.delete(redisKey);
    }
  • 当获取user的时候,例如根据userId获取用户信息,先判断缓存中查有没有user:userId这个键。如果有,就从缓存中返回User;如果没有,就初始化缓存,将User信息写入user:userId。
  • 每次用户信息修改后,例如更改密码后、退出登录修改ticket后、修改用户激活状态后等,多需要做一次clearCache。
相关推荐
豪宇刘1 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意1 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
m0_748247551 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
Ren_xixi1 小时前
redis和mysql的区别
数据库·redis·mysql
m0_748255022 小时前
前端常用算法集合
前端·算法
FF在路上2 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203982 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2342 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
众拾达人2 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言