4-7:用Redis优化登陆模块

news2024/9/20 1:19:19

相关功能

  • 使用Redis存储验证码
    • 验证码需要频繁的访问与刷新,对性能要求较高。
    • 验证码不需永久保存,通常在很时间后就会失效
      (Redis可以设置有效时间,分布式应用也可以绕过session共享的问题)
    • 分布式部署时,存在Session共享的问题。
  • 使用Redis存储登录凭证
    • 处理每次请求时,都要查询用户的登录凭证,访问的频率非常高
  • 使用Redis缓存用户信息
    • 处理每次请求时,都要根据凭证查询用户信息,访问的频率非常高

redis用户凭证存了,mysql则不用存,但用户需要存储;

RedisKeyUtil.java

添加如下内容

private static final String PREFIX_KAPTCHA = "kaptcha";
 // 登录验证码
    public static String getKaptchaKey(String owner) {
        return PREFIX_KAPTCHA + SPLIT + owner;
        //不同用户的验证码不一样,不能用用户id;因为用户还没登陆
        //所以就要给予一个字符串,零时凭证,一定时间内失效。
    }

打开Lontroller.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);

        // 验证码的归属,随机发布的凭证
        String kaptchaOwner = CommunityUtil.generateUUID();
        Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
        cookie.setMaxAge(60);//生存时间,60s
        cookie.setPath(contextPath);//有效路径,整个项目都有效
        response.addCookie(cookie);//发送给客户端
        // 将验证码存入Redis
        String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
        redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);//有了key,在key里存数据

        // 将突图片输出给浏览器
        response.setContentType("image/png");
        try {
            OutputStream os = response.getOutputStream();
            ImageIO.write(image, "png", os);
        } catch (IOException e) {
            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) {
        // 检查验证码
        // @CookieValue("kaptchaOwner") ,从cookie中取值的一个注解,要想要验证码,就要从redis里面取处key,要想要key,就要知道onwer,要想要owner就要从cookie里面取值.
        // String kaptcha = (String) session.getAttribute("kaptcha");
        String kaptcha = null;
        if (StringUtils.isNotBlank(kaptchaOwner)) {判断key不为空,验证码没有失效才能使用;
            String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);//得到key
            kaptcha = (String) redisTemplate.opsForValue().get(redisKey);//根据key得到验证码
        }

        if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !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 = 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";
        }
    }

    @RequestMapping(path = "/logout", method = RequestMethod.GET)
    public String logout(@CookieValue("ticket") String ticket) {
        userService.logout(ticket);
        return "redirect:/login";
    }

实现登陆凭证的优化操作

RedisKeyUtil.java
    // 登录的凭证
    public static String getTicketKey(String ticket) {
        return PREFIX_TICKET + SPLIT + ticket;//要传进来登陆成功的凭证
        //使用redis存这个凭证,代替那个LoginTicket表
    }
找出需要使用的地方

UserService.java

        // 生成登录凭证
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(user.getId());
        loginTicket.setTicket(CommunityUtil.generateUUID());
        loginTicket.setStatus(0);
        loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));
//        loginTicketMapper.insertLoginTicket(loginTicket);

        String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());//从对象中获取
        redisTemplate.opsForValue().set(redisKey, loginTicket);//将redisTemplate注入,然后将凭证存入到value中,可以以字符串的形式

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

    public void logout(String ticket) {
//        loginTicketMapper.updateStatus(ticket, 1);
        //登出时,把状态改为1,先在redis中存了,再取出来改,再存
        String redisKey = RedisKeyUtil.getTicketKey(ticket);
        LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(redisKey);
        loginTicket.setStatus(1);//改变状态
        redisTemplate.opsForValue().set(redisKey, loginTicket);//改变的值再重新存进去。
    }

    public LoginTicket findLoginTicket(String ticket) {//查询凭证的方法也需要重构
//        return loginTicketMapper.selectByTicket(ticket);
        String redisKey = RedisKeyUtil.getTicketKey(ticket);//取出key
        return (LoginTicket) redisTemplate.opsForValue().get(redisKey);//根据key查询
    }

重启项目,测试登陆
发起任何访问,拦截器都会调用查找凭证的方法,重构函数成功。

使用redis缓存用户信息

一定要注意,信息需要过一段时间删掉,与上一个方法功能不同,凭证不会删除,指挥修改状态。

在RedisUtils.java中添加

 // 用户
    public static String getUserKey(int userId) {
        return PREFIX_USER + SPLIT + userId;
    }

重构UserService.java
通过用户id查找用户,这个方法非常常见,于是将其信息存到redis中。

/**
     * 在查询user时,先查询缓存数据,
     * 如查不到,则需要重新初始化;更改用户后(用户数据变化后),
     * 也要更新缓存或删除缓存;一般用删除操作
     * 都是私有的,给userService自己调用
     * **/
    // 1.优先从缓存中取值
    private User getCache(int userId) {
        String redisKey = RedisKeyUtil.getUserKey(userId);
        return (User) redisTemplate.opsForValue().get(redisKey);
    }

    // 2.取不到时初始化缓存数据
    private User initCache(int userId) {
        User user = userMapper.selectById(userId);//从mysql中查数据
        String redisKey = RedisKeyUtil.getUserKey(userId);
        //3600s,一个小时
        redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS);
        return user;//得到一个user
    }

    // 3.数据变更时清除缓存数据
    private void clearCache(int userId) {
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.delete(redisKey);//将用户信息删除;
    }

再重构调用查询用户的方法

public User findUserById(int id) {
//        return userMapper.selectById(id);
        User user = getCache(id);
        if (user == null) {
            user = initCache(id);
        }
        return user;
    }

紧接着,再在需要修改用户的地方设置,将缓存清除;

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;
        }
    }
    public int updateHeader(int userId, String headerUrl) {
//        return userMapper.updateHeader(userId, headerUrl);
        int rows = userMapper.updateHeader(userId, headerUrl);
        clearCache(userId);//清除用户缓存
        return rows;
    }

再重启服务器,检查即可。
在这里插入图片描述

首页查询的每个帖子,都用到了缓存机制,虽然页面上没有变化,但内部的存储机制过程已经改变。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/60762.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022物联卡排行榜公司有哪些?

科技的发展日新月异&#xff0c;我们国家的发展战略也是支持高新科技公司的发展&#xff0c;所以越来越多的高新科技公司出现&#xff0c;但凡是高新科技公司&#xff0c;在设备的联网中&#xff0c;都会用到物联卡&#xff0c;所以物联卡的市场也愈发火爆&#xff0c;那么今天…

Zygote在Framework中起什么作用?

前言 提到Zygote可能了解一些的小伙伴会说&#xff0c;它是分裂进程用的。没错它最大的作用的确是分裂进程&#xff0c;但是它除了分裂进程外还做了什么呢。还是老规矩&#xff0c;让我们抱着几个问题来看文章。最后在结尾&#xff0c;再对问题进行思考回复。 你能大概描述一…

【springboot进阶】使用aop + 注解方式,简单实现spring cache redis 功能

目录 一、实现思路 二、定义缓存注解 三、aop 切面处理 四、使用方式 五、灵活的运用 六、总结 前几天有同学看了 SpringBoot整合RedisTemplate配置多个redis库 这篇文章&#xff0c;提问spring cache 能不能也动态配置多个redis库。介于笔者没怎么接触过&#xff0c;所以…

【Graph】NetworkX官方基础教程

NetworkX官方基础教程图的基础知识1.1 图&#xff08;graph&#xff09;及其分类1.2 节点的度&#xff08;degree&#xff09;1.3 子图&#xff08;subgraph&#xff09;1.4 连通图1.5 图的矩阵表示NetworkX概述NetworkX基础教程1. 创建图2. 节点3. 边4. 清空图5. 图可视化6. 访…

基于javaweb框架的springboot mybatis宠物商城源码含论文设计文档

在互联网高速发展、信息技术步入人类生活的情况下&#xff0c;电子贸易也得到了空前发展。网购几乎成为了人人都会进行的活动。近几年来&#xff0c;养宠物更是成为人们生活中重要的娱乐内容之一&#xff0c; 人们越来越多的讲感情也寄托给了宠物&#xff0c;以给自己另一个感情…

自动驾驶--预测技术

根据百度技术培训中心课程整理( https://bit.baidu.com/productsBuy?id72) 背景简介 无人车系统从算法模块可分为三个部分&#xff0c;首先是感知通过对传感器数据和环境信息进行计算来解决周围有什么的问题&#xff0c;其次是预测&#xff0c;根据感知信息预测环境下一步将…

Java单元测试

1. 序言 1.1 工作中要求进行单元测试 毕业进入公司时&#xff0c;为了锻炼笔者的Java基础&#xff0c;老大给笔者分配了平台化开发的工作&#xff0c;基于Spring Boot Mybatis的Java Web后端开发一个人干后端开发&#xff0c;且以前也没有后端开发的经验&#xff0c;所以只是…

CTF之序列化__toString

序列化简介 本质上serialize()和unserialize&#xff08;&#xff09;在php内部的实现上是没有漏洞的&#xff0c;漏洞的主要产生是由于应用程序在处理对象&#xff0c;魔术函数以及序列化相关问题时导致的。 当传给unserialize()的参数可控时&#xff0c;那么用户就可以注入精…

【应用】Docker Swarm

Docker SwarmDocker Swarm 集群配置配置前准备初始化 SwarmSwarm 常用命令Portainer 集群管理Docker Swarm 集群配置 masternode1node2192.168.86.133192.168.86.131192.168.86.139 配置前准备 关闭各个节点服务器的防火墙 systemctl stop firewalld systemctl disable fire…

ATF问题二则:EL3可能没有实现吗? aarch32中的S-EL1是什么?

最近两个问题&#xff0c;戳到了我的知识盲点&#xff0c;当然我这个菜鸡ATF哪里都是盲点。 问题一&#xff1a;EL3可能没有实现吗&#xff1f; 问题二&#xff1a;bl2是aarch32, 那么bl2是S-EL1&#xff0c;bl31也是S-EL1? 1、EL3可能没有实现吗&#xff1f; The Armv8-A …

基于MATLAB的一级倒立摆控制仿真,带GUI界面操作显示倒立摆动画,控制器控制输出

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 一个可以活动的小车上立着一根不稳定随时会倒下的杆。小车的轮子由电机控制&#xff0c;可以控制小车电机的转动力矩M。同时&#xff0c;也可以获取小车轮子转动的圈数N&#xff08;可以精确到小…

java计算机毕业设计ssm实验室设备管理系统5k648(附源码、数据库)

java计算机毕业设计ssm实验室设备管理系统5k648&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xf…

162.基于Django-rest_framework的身份验证和权限

1. 概述 到目前为止&#xff0c;程序的API对任何人都可以编辑或删除&#xff0c;没有任何限制。我们希望有一些更高级的行为&#xff0c;进行身份验证和权限分配&#xff0c;以确保&#xff1a; 数据始终与创建者相关联只有经过身份验证的用户才能创建数据只有数据的创建者可…

嵌入式Linux上ifpulgd的使用配置与qemu模拟验证

问题引入 最近在项目开发中收到了一个非常简单的需求&#xff0c;我们的嵌入式Linux板卡需要在检测到网口插拔后重新配置网络&#xff0c;这在pc环境中非常常见。但是在这个项目的默认SDK中并没有相关配置&#xff0c;稍微查询了一下&#xff0c;在一般pc上通常使用Ifpulgd,并…

[附源码]Python计算机毕业设计Django企业售后服务管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【数据结构】树的概念与堆的实现

树的概念与堆的实现1、什么是树1.1 树的概念1.2 树的相关概念1.3 树的表示2、二叉树概念及结构2.1 概念2.2 特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储结构3、二叉树的顺序结构及实现3.1 二叉树的顺序结构3.2 堆的概念及结构3.3 堆的实现3.3.1 创建一个堆3.3.2 初始化堆3.3…

【计算机毕业设计】基于JSP的网上购物系统的设计与实现

分类号&#xff1a;TP315 U D C&#xff1a;D10621-408-(2007)5883-0 密 级&#xff1a;公 开 编 号&#xff1a;2003214012 学位论文 基于JSP的网上购物系统的设计与实现 基于JSP的网上购物系统的设计与实现 摘 要 近年来&#xff0c;随着Internet的迅速崛起&#xff0c…

内存 分页、交换空间

目录 1. 分页 1.1 地址转换 1.2 页表存在哪里 1.3 列表中究竟有什么 1.4 分页的优缺点 2. 快速地址转换&#xff08;TLB&#xff09; 2.1 TLB 的基本算法 2.2 谁来处理 TLB 未命中 2.2.1 硬件处理 2.2.2 软件&#xff08;操作系统&#xff09;处理 2.3 TLB 的内容 …

[附源码]Python计算机毕业设计SSM精准扶贫系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

(免费分享)基于springboot,vue公司财务系统

该系统是一个简单的公司财务管理系统&#xff0c;包含用户基本信息管理&#xff08;员工管理&#xff0c;管理员管理&#xff09;&#xff0c;工资管理&#xff08;员工工资管理&#xff0c;管理员工资管理&#xff09;&#xff0c;业务管理&#xff08;员工业务管理&#xff0…