仿牛客社区——4.23优化登录模块

实现功能

在最初是做项目时一些数据采用的是mysql存储,但一些功能需要频繁进行访问的时候可以将这些数据采用redis存储来提高性能

使用Redis存储验证码

- 验证码需要频繁的访问与刷新,对性能要求较高。

- 验证码不需永久保存,通常在很短的时间后就会失效。

- 分布式部署时,存在Session共享的问题。

用来存储验证码的key

public class RedisKeyUtil {

	private static final String SPLIT=":";
	
	public static final String PREFIX_KAPTCHA="kaptcha"; //验证码
	
	//获取验证码的key:当用户到登录页面时,需要给验证码下发一个临时凭证来进行验证码归属
	public static String getKaptchaKey(String owner){
		return PREFIX_KAPTCHA+SPLIT+owner;
	}

}

重构项目代码1:生成验证码,存入redis

@RequestMapping(path = "/kaptcha",method = RequestMethod.GET)
    public void getKaptcha(HttpServletResponse response/* , HttpSession session */){
        //生成验证码(需注入bean
        String text = defaultKaptcha.createText();
        BufferedImage image = defaultKaptcha.createImage(text);

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

        /**
         * 性能优化
         */
        //现功能:将验证码存入redis
        //验证码的归属:为用户临时生成一个凭证
        String kaptchaOwner=CommunityUtil.generateUUID();
        //将登录凭证存放进cookie中
        Cookie cookie=new Cookie("kaptchaOwner",kaptchaOwner);
        //设置cookie最大生存时间
        cookie.setMaxAge(60);
        //设置cookie有效路径
        cookie.setPath(contextPath);
        response.addCookie(cookie);
        //生成验证码key
        String kaptchaKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
        //将验证码存入redis,并设置过期时间
        redisTemplate.opsForValue().set(kaptchaKey,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());
        }

    }

重构项目代码2:在登陆界面进行验证码验证

/**
     *
     * @param userName 用户名
     * @param password 密码
     * @param code  验证码
     * @param model 封装返回的数据
     * @param rememberMe 是否勾选记住我
     //* @param session 从session中取出生成的验证码
     * @param response 如果登陆成功,将用户登录状态ticket存入客户端(cookie
     */
    @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.isNoneBlank(kaptchaOwner)){ //验证码未过期
            //生成验证码key
            String kaptchaKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
            //从redis中取对应的验证码
            kaptcha = (String) redisTemplate.opsForValue().get(kaptchaKey);
        }


        //得到的验证码和用户传入的验证码进行相比
        if(StringUtils.isBlank(kaptcha)||StringUtils.isBlank(code)||(!kaptcha.equalsIgnoreCase(code))){

            //验证码错误返回提示,将提示封装进model中
            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")){
            //验证成功:将ticket存入cookie中
            Cookie cookie=new Cookie("ticket",map.get("ticket").toString());
            cookie.setPath(contextPath);//设置cookie有效路径
            cookie.setMaxAge(expiredSeconds);//设置cookie有效时间
            response.addCookie(cookie);//将cookie发送给浏览器(将cookie存入到response头部
            return "redirect:/index";  //跳转至首页
        }else{
            //验证失败
            model.addAttribute("userNameMsg",map.get("userNameMsg"));
            model.addAttribute("passwordMsg",map.get("passwordMsg"));
            return "/site/login";//回到登录页面
        }
    }

使用Redis存储登录凭证

- 处理每次请求时,都要查询用户的登录凭证,访问的频率非常高。

用来存储登录凭证的key

public class RedisKeyUtil {

	private static final String SPLIT=":";
	
	public static final String PREFIX_TICKET="ticket"; //登录凭证


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

}

重构项目代码1:将生成的登录凭证存入redis中

//用户登录验证(用户登录凭证
    public Map<String,Object> login(String userName,String password,int expiredSeconds){
        Map<String,Object> map=new HashMap<>();//将验证信息封装进map中
        //账号不能为空
        if(StringUtils.isBlank(userName)){
            map.put("userNameMsg","账号不能为空!");
            return map;
        }

        //密码不能为空
        if(StringUtils.isBlank(password)){
            map.put("passwordMsg","密码不能为空!");
            return map;
        }

        User user = userMapper.selectByName(userName);
        //账号验证
        if(user==null){
            map.put("userNameMsg","账号不存在!");
            return map;
        }
        //账号是否激活
        if(user.getStatus()==0){
            map.put("userNameMsg","账号未激活!");
            return map;
        }
        //密码验证
        password=CommunityUtil.md5(password + user.getSalt());
        if(!password.equals(user.getPassword())){
            map.put("passwordMsg","密码错误!");
            return map;
        }

        //验证通过,生成用户凭证
        LoginTicket loginTicket=new LoginTicket();
        loginTicket.setUserId(user.getId());
        loginTicket.setTicket(CommunityUtil.generateUUID());//生成随机字符串
        loginTicket.setStatus(0);//账号登录状态0:未过期
        loginTicket.setExpired(new Date(System.currentTimeMillis()+expiredSeconds*1000));
        //原来:loginTicketMapper.insertLoginTicket(loginTicket);//存入用户登录凭证
        //现在
        //生成key
        String ticketKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
        //将登录凭证存入redis
        redisTemplate.opsForValue().set(ticketKey,loginTicket);
        map.put("ticket",loginTicket.getTicket());//将ticket存入,方便传给浏览器
        return map;
    }

重构项目代码2:更改登录凭证状态

public void logout(String ticket){

        //原来:loginTicketMapper.updateStatus(ticket,1);

        //现在:更改redis中登录凭证状态 :先取、在更改、再存入
        //生成key
        String ticketKey = RedisKeyUtil.getTicketKey(ticket);
        LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(ticketKey);
        loginTicket.setStatus(1);
        redisTemplate.opsForValue().set(ticketKey,loginTicket);

    }

重构项目代码3:获取登录凭证

//获取登录凭证ticket
    public LoginTicket findLoginTicket(String ticket){

        //原来:return loginTicketMapper.selectByTicket(ticket);

        //现在
        //生成key
        String ticketKey = RedisKeyUtil.getTicketKey(ticket);
        return (LoginTicket) redisTemplate.opsForValue().get(ticketKey);
    }

使用Redis缓存用户信息

- 处理每次请求时,都要根据凭证查询用户信息,访问的频率非常高

重构userService

 //1、优先从缓存中取值
    public User getCache(int userId){
        //构建用户key
        String userKey=RedisKeyUtil.getUserKey(userId);
        return (User) redisTemplate.opsForValue().get(userKey);
        
    }
    
    //2、若缓存中无值,则从mysql中查找,后在写入缓存中
    public User initCache(int userId){
        User user = userMapper.selectById(userId);
        //构建用户key
        String userKey=RedisKeyUtil.getUserKey(userId);
        redisTemplate.opsForValue().set(userKey,user,3600, TimeUnit.SECONDS);
        return user;
    }
    
    //3、当用户信息发生更改时清除缓存
    public void clearCache(int userId){
        //构建用户key
        String userKey=RedisKeyUtil.getUserKey(userId);
        redisTemplate.delete(userKey);
    }

重构项目代码1:从缓存中查找用户

 public User findUserById(int id){

        // 之前:return userMapper.selectById(id);
        User user = getCache(id);  //先从缓存中查
        if(user==null){
            user = initCache(id); //缓存中没有
        }
        return user;
    }

重构项目代码2:修改数据后清除缓存


    //编写激活业务
    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; //激活码不等
        }
    }


    //更改头像信息(headerUrL
    public int updateHeaderUrl(int userId,String headerUrl){

        //之前:return userMapper.updateHeader(userId,headerUrl);

        int rows = userMapper.updateHeader(userId, headerUrl);
        clearCache(userId);
        return rows;
    }

    //修改密码
    public int updatePassword(int userId,String password){

        //之前:return userMapper.updatePassword(userId,password);
        int rows = userMapper.updatePassword(userId, password);
        clearCache(userId);
        return rows;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值