社区网站项目4.6 优化登录模块

  使用Redis存储验证码
(1)验证码需要频繁的访问与刷新,对性能要求较高。
(2)验证码不需永久保存,通常在很短的时间后就会失效。
(3)分布式部署时,存在Session共享的问题。
  使用Redis存储登录凭证
(1)处理每次请求时,都要查询用户的登录凭证,访问的频率非常高。
  使用Redis缓存用户信息
(1)处理每次请求时,都要根据凭证查询用户信息,访问的频率非常高。
  一、在RedisKeyUtil类里,添加

	private static final String PREFIX_KAPTCHA = "kaptcha";

//登录的凭证
    public static String getTicketKey(String ticket){
        return PREFIX_TICKET + SPLIT + ticket;
    }

  在LoginController类里,重构getKaptcha和login方法

@RequestMapping(path = "/kaptcha",method = RequestMethod.GET)
    public void getKaptcha(HttpServletResponse response/*, HttpSession session*/) { //原来是把验证码放到session里,现在要重构,所以注释掉
        //生成验证码
        String text = kaptchaProducer.createText();
        BufferedImage image = kaptchaProducer.createImage(text);

        //将验证码存入session
//        session.setAttribute("kaptcha",text);

        // 验证码的归属(重构)
        String kaptchaOwner = CommunityUtil.generateUUID();
        Cookie cookie = new Cookie("kaptchaOwner",kaptchaOwner);
        cookie.setMaxAge(60);
        cookie.setPath(contextPath);
        response.addCookie(cookie); //发送给客户端
        //将验证码存入Redis
        String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
        redisTemplate.opsForValue().set(redisKey,text,60, TimeUnit.SECONDS); //设置过期时间为60秒


        //将图片输出给浏览器
        response.setContentType("image/png");
        try {
            OutputStream os = response.getOutputStream();
            ImageIO.write(image,"png",os);
            //不用手动关闭,spring MVC会自动处理
        } catch (IOException e) {
            //e.printStackTrace();
            logger.error("响应验证码失败:" + e.getMessage());
        }

    }

    @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){
        //检查验证码
//        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.equalsIgnoreCase(code)){
            model.addAttribute("codeMsg","验证码不正确!");
            return "/site/login";
        }

  二、在RedisKeyUtil类里添加

	private static final String PREFIX_TICKET = "ticket";

//登录的凭证
    public static String getTicketKey(String ticket){
        return PREFIX_TICKET + SPLIT + ticket;
    }

  把LoginTicketMapper类加上@Deprecated注解。
  在UserService里进行修改login方法、logout方法、findLoginTicket方法

//        loginTicketMapper.insertLoginTicket(loginTicket);

        String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
        redisTemplate.opsForValue().set(redisKey,loginTicket);
//        loginTicketMapper.updateStatus(ticket,1);
        String redisKey = RedisKeyUtil.getTicketKey(ticket);
        LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(redisKey);
        loginTicket.setStatus(1);
        redisTemplate.opsForValue().set(redisKey,loginTicket); //把新改变的值覆盖了原有的值
//        return loginTicketMapper.selectByTicket(ticket);
        String redisKey = RedisKeyUtil.getTicketKey(ticket);
        return (LoginTicket)redisTemplate.opsForValue().get(redisKey);

  三、在RedisKeyUtil类里添加

	private static final String PREFIX_USER = "user";
	
//用户
    public static String getUserKey(int userId){
        return PREFIX_USER + SPLIT + userId;
    }

  在UserService类里,添加

	//1.优先从缓存中取值(先尝试从缓存取用户数据)
    private User getCache(int userId){
        String redisKey = RedisKeyUtil.getUserKey(userId);
        return (User) redisTemplate.opsForValue().get(redisKey);
    }

    //2、取不到时初始化缓存数据(这个数据来源于mysql)
    private 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、数据发生变化时清除缓存数据(避免并发的问题),重查
    private void clearCache(int userId){
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.delete(redisKey);
    }

然后改写findUserById方法

	public User findUserById(int id) {
//        return userMapper.selectById(id);
        User user = getCache(id);   //先尝试从缓存读数据
        if(user==null){
            user = initCache(id);
        }
        return user;
    }

改写activation方法

	public int activation(int userId,String code) {
        User user = userMapper.selectById(userId);
        if (user.getStatus()==1){
            return ACTIVATION_REPEAT;
        }else if(user.getActivationCode().equals(code)){
            userMapper.updateStatus(userId,1);
            clearCache(userId); //加入清除缓存的动作
            return ACTIVATION_SUCCESS;
        }else {
            return ACTIVATION_FAILURE;
        }
    }

改写updateHeader方法

public int updateHeader(int userId,String headerUrl){
//        return userMapper.updateHeader(userId,headerUrl);
        int rows = userMapper.updateHeader(userId,headerUrl);
        clearCache(userId);  //加入清除缓存的动作
        return rows;
    }

  可以用压测工具测一下,优化前后的QPS。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值