使用redis模拟cookie-session,例子:实现验证码功能

news2024/9/25 7:24:20

目录

在前后端分离架构中不建议使用cookie-session机制实现端状态识别

所以我们可以使用redis来模拟session-cookie机制

下面我们通过实现验证码的功能来举例 

第一步:了解前端要我们返回的数据变量名字,变量类型

1.封装code,data成一个result类,专门用于返回数据

2.封装data里面的数据

第二步:导入redis依赖

application-cahe.yml没有叶子的解决方法

自定义redis序列化

 测试

第三步:使用雪花算法生成唯一的sessionId,即一个数字

1.导入一个工具类

2.配置机房id和机器id,并存入IoC容器

第四步:导入验证码图片生成包

第五步:定义一个常量类,用于封装我们使用的数据

 第六步:业务逻辑

第七步:测试接口

到时候前端发送的数据中会包含sessionId,我们再根据sessionId去redis取值比对即可


在前后端分离架构中不建议使用cookie-session机制实现端状态识别

原因:
1.前后端分离存在跨域问题,cookie无法共享

2.后台服务器一旦建立集群,可能导致session数据丢失,即·后台有多台服务器,每个服务器存的session不一样,导致访问到不同的服务器时导致找不到对应的session

3.前端浏览器如果禁用session,则该机制无法生效

4.后台服务器需要维护session对象,又内存开销

所以我们可以使用redis来模拟session-cookie机制

我们可以再后端通过雪花算法生成一个独一的sessionid,然后存入redis中,并把这个sessionId值发送给前端,前端再通过发请求数据->里面包含sessionId,后端再通过sessionId去redis取出值进行比较。我们还可以使用redis的数据过期模式来模拟session的过期机制 

下面我们通过实现验证码的功能来举例 

第一步:了解前端要我们返回的数据变量名字,变量类型

{
    "code": 1,
    "data": {
        "imageData": "iVBORw0KGgoAAAANSUh...省略...AAAPoAAAAoCAYAAADX=", //base64格式图片
        "sessionId": "1479063316897845248" //保存在redis中验证码对应的key,模拟sessioinId
    }
}

 我们来封装一个这个json数据->由外到内

1.封装code,data成一个result类,专门用于返回数据

 <!--jackson相关注解,实现日期格式转换和类型格式转换并序列化等-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>
/**
 * 返回数据类
 * @JsonInclude 保证序列化json的时候,如果是null的对象,key也会消失
 * @param <T>
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class R<T> implements Serializable {
    private static final long serialVersionUID = 7735505903525411467L;

    // 成功值,默认为1
    private static final int SUCCESS_CODE = 1;
    // 失败值,默认为0
    private static final int ERROR_CODE = 0;

    //状态码
    private int code;
    //消息
    private String msg;
    //返回数据
    private T data;

    private R(int code){
        this.code = code;
    }
    private R(int code, T data){
        this.code = code;
        this.data = data;
    }
    private R(int code, String msg){
        this.code = code;
        this.msg = msg;
    }
    private R(int code, String msg, T data){
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public static <T> R<T> ok(){
        return new R<T>(SUCCESS_CODE,"success");
    }
    public static <T> R<T> ok(String msg){
        return new R<T>(SUCCESS_CODE,msg);
    }
    public static <T> R<T> ok(T data){
        return new R<T>(SUCCESS_CODE,data);
    }
    public static <T> R<T> ok(String msg, T data){
        return new R<T>(SUCCESS_CODE,msg,data);
    }

    public static <T> R<T> error(){
        return new R<T>(ERROR_CODE,"error");
    }
    public static <T> R<T> error(String msg){
        return new R<T>(ERROR_CODE,msg);
    }
    public static <T> R<T> error(int code, String msg){
        return new R<T>(code,msg);
    }
    public static <T> R<T> error(ResponseCode res){
        return new R<T>(res.getCode(),res.getMessage());
    }

    public int getCode(){
        return code;
    }
    public String getMsg(){
        return msg;
    }
    public T getData(){
        return data;
    }
}

2.封装data里面的数据

data里面的数据我们其实可以不用特意封装成一个类,因为只有两个key,且没有什么实际意义,

最重要的是验证码不用与数据库进行交互,只需要访问redis即可,所以我们可以直接使用Map

第二步:导入redis依赖

<!--redis场景依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis创建连接池,默认不会创建连接池 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

 在yml文件中写redis的配置,如果我们把所有配置信息都写在application.yml文件中,就会导致这个文件十分复杂,所以我们额外创建一个application-cache.yml文件,在此文件中写redis配置,再通过application.yml来激活application-cache.yml

spring:
  profiles:
    active: cache #激活其他配置文件
spring:
  # 配置缓存
  redis:
    host: 192.168.230.100
    port: 6379
    database: 0 #Redis数据库索引(默认为0)
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 8 # 连接池中的最大空闲连接
        min-idle: 1  # 连接池中的最小空闲连接
    timeout: PT10S # 连接超时时间

application-cahe.yml没有叶子的解决方法

找到对应的模块添加spring

再点击这个小叶子进行添加 

自定义redis序列化

@Configuration
public class RedisCacheConfig {
    /**
     * 配置redisTemplate bean,自定义数据的序列化的方式,避免使用默认的jdk序列化方式
     * jdk序列化缺点:
     * 1.阅读体验差
     * 2.序列化后内容体积比较大,占用过多内存
     * @param redisConnectionFactory 连接redis的工厂,底层有场景依赖启动时,自动加载
     * @return
     */
    //TODO:方法名必须是redisTemplate,这是bean id 如果自己装配了这个类的bean,SpringBoot就不会自动装配了
    //TODO:而底层又是使用了redisTemplate这个bean id 的,所以方法名必须为redisTemplate
    @Bean
    public RedisTemplate redisTemplate(@Autowired RedisConnectionFactory redisConnectionFactory){//不加@Autowired也行
        //1.构建RedisTemplate模板对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        //2.为不同的数据结构设置不同的序列化方案
        //设置key序列化方式
        template.setKeySerializer(new StringRedisSerializer());
        //设置value序列化方式
        template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
        //设置hash中field字段序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        //设置hash中value的序列化方式
        template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
        //5.初始化参数设置
        template.afterPropertiesSet();
        return template;
    }
}

 测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestRedisCache {
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    public void test(){
        ValueOperations valueOperations = redisTemplate.opsForValue();//取出操作String类型的操作类
        valueOperations.set("name","ajx");
        System.out.println(valueOperations.get("name"));
    }
}

第三步:使用雪花算法生成唯一的sessionId,即一个数字

64位ID (42(时间戳)+5(机房ID)+5(机器ID)+12(序列号-同毫秒内重复累加))

1.导入一个工具类

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;

/**
 * 分布式自增长ID实现,底层基于Twitter的Snowflake
 * 64位ID (42(时间戳)+5(机房ID)+5(机器ID)+12(序列号-同毫秒内重复累加))
 * @author itheima
 */
public class IdWorker {
    // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
    private final static long twepoch = 1288834974657L;
    // 机器标识位数
    private final static long workerIdBits = 5L;
    // 数据中心标识位数
    private final static long datacenterIdBits = 5L;
    // 机器ID最大值
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    // 数据中心ID最大值
    private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    // 毫秒内自增位
    private final static long sequenceBits = 12L;
    // 机器ID偏左移12位
    private final static long workerIdShift = sequenceBits;
    // 数据中心ID左移17位
    private final static long datacenterIdShift = sequenceBits + workerIdBits;
    // 时间毫秒左移22位
    private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
    /* 上次生产id时间戳 */
    private static long lastTimestamp = -1L;
    //同毫秒并发控制
    private long sequence = 0L;
	//机器ID
    private final long workerId;
    //机房ID
    private final long datacenterId;

    public IdWorker(){
        this.datacenterId = getDatacenterId(maxDatacenterId);
        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
    }
    /**
     * @param workerId
     *            工作机器ID
     * @param datacenterId
     *            序列号
     */
    public IdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    /**
     * 获取下一个ID
     *
     * @return
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        if (lastTimestamp == timestamp) {
            // 当前毫秒内,则+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 当前毫秒内计数满了,则等待下一秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // ID偏移组合生成最终的ID,并返回ID
        long nextId = ((timestamp - twepoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift) | sequence;

        return nextId;
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * <p>
     * 获取 maxWorkerId
     * </p>
     */
    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
        StringBuffer mpid = new StringBuffer();
        mpid.append(datacenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (!name.isEmpty()) {
            /*
             * GET jvmPid
             */
            mpid.append(name.split("@")[0]);
        }
        /*
         * MAC + PID 的 hashcode 获取16个低位
         */
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * <p>
     * 数据标识id部分
     * </p>
     */
    protected static long getDatacenterId(long maxDatacenterId) {
        long id = 0L;
        try {
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            if (network == null) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                id = ((0x000000FF & (long) mac[mac.length - 1])
                        | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                id = id % (maxDatacenterId + 1);
            }
        } catch (Exception e) {
            System.out.println(" getDatacenterId: " + e.getMessage());
        }
        return id;
    }
}

2.配置机房id和机器id,并存入IoC容器

    /**
     * 根据雪花算法保证sessionId的唯一性
     * @return 返回第三方bean,加入到IoC容器
     */
    @Bean
    public IdWorker idWorker(){
        //参数一:机器id
        //参数二:机房id
        return new IdWorker(1l,2l);
    }

第四步:导入验证码图片生成包

<!--hutool万能工具包-->
<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
</dependency>

第五步:定义一个常量类,用于封装我们使用的数据



/**
 * 
 * @Description 常量类信息封装
 */
public class StockConstant {

    /**
     * 定义校验码的前缀
     */
    public static final String CHECK_PREFIX="CK:";

}

 第六步:业务逻辑

@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Autowired
    IdWorker idWorker;//sessionId生成器
    @Autowired
    RedisTemplate redisTemplate;//redis

    @Override
    public R<Map<String, String>> getCaptchaCode() {
        //1.生成图片验证码,使用huTool工具包
        /*
        参数一:生成验证码的宽度
        参数二:生成验证码的长度
        参数三:图片中包含验证码的长度
        参数四:干扰线数量
         */
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(250, 40, 4, 5);
        //设置背景颜色
        captcha.setBackground(Color.LIGHT_GRAY);

        //获取校验码
        String code = captcha.getCode();
        //获取经过base64编码处理的图片数据
        String imageData = captcha.getImageBase64();
        //2.生成sessionId,并转换成String类型,防止发送给前端时数据丢失
        String sessionId = String.valueOf(idWorker.nextId());//把Long类型的数据变成String类型再发送给前端

        log.info("当前生成的验证码为:{},sessionId为:{}",code,sessionId);

        //3.将sessionId作为key,验证码作为value保存到redis,TODO:设置验证码的有效时间,即key(sessionID)的存活时间
        //TODO:这里给sessionID加一个前缀,区别这是用于验证码的,到时候可以在redis使用 keys CK*来查看现在redis到底有多少验证码key
        //redisTemplate.opsForValue().set("CK:"+sessionId,code,5, TimeUnit.MINUTES);
        redisTemplate.opsForValue().set(StockConstant.CHECK_PREFIX +sessionId,code,5, TimeUnit.MINUTES);
        //4.组装数据
        Map<String,String>map=new HashMap<>();
        map.put("sessionId",sessionId);//sessionId key值不能乱写,要和前端的变量名字一样
        map.put("imageData",imageData);
        //5.返回数据
        return R.ok(map);
    }
}

第七步:测试接口

{
    "code": 1,
    "data": {
        "imageData": "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",
        "sessionId": "1826774073703927808"
    }
}

成功

到时候前端发送的数据中会包含sessionId,我们再根据sessionId去redis取值比对即可

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

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

相关文章

多模态技术应用场景探析,景联文科技多模态数据测试平台推动多模态大模型技术突破

多模态技术应用场景探析&#xff0c;景联文科技多模态数据测试平台推动多模态大模型技术突破 在大语言模型背景下&#xff0c;多模态技术的发展已成为一个重要趋势。 Sora是OpenAI推出的多模态大模型&#xff0c;具备高级视频生成与编辑功能&#xff0c;支持长视频、多视角、多…

使用OpenCV库来捕获摄像头视频流,并按指定格式保存

今天我们来使用OpenCV库来捕获摄像头视频流&#xff0c;并将其保存为AVI格式的视频文件&#xff0c; 代码的主要功能包括&#xff1a; 初始化摄像头捕获对象。设置视频编解码器和输出文件路径。循环读取视频帧&#xff0c;处理并保存到文件中。显示处理后的视频帧。按下q键退…

Python | 处理海洋2C 数据 | 非标准时间格式

写在前面 最近&#xff0c;师弟在用Python读取某海洋2C数据时&#xff0c;突然冒出一个报错&#xff1a;“时间单位的参考日期无效&#xff0c;当前日期 00:00:00.0”。这让我回想起&#xff0c;似乎在很久很久以前&#xff0c;我处理SMAP和Argo数据时也遇到过类似的问题。为了…

JavaScript 模块化开发:ES6 模块与 CommonJS 的对比与应用

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 随着前端项目规模的增长&#xff0c;代码组织和管理变得越来越复杂。模块化开发成为解决这一问题的有效手段&#xff0c;能够帮助开发者将代码进行分割、复用和维护。JavaScript 在发展过程中出现了多种模块化规范&…

【精通SDL之----使用PBO异步传输像素数据】

使用PBO异步传输像素数据 前言一、 一些GPU 分配的常用内存对象介绍二、 PBO 传输原理三、 PBO 使用方法 前言 书接上文【精通SDL之----SDL_RenderReadPixels截屏】&#xff0c;SDL_RenderReadPixels在GLES2上是一个非常耗时的操作&#xff0c;因为   1. OpenGL ES 是一个异步…

.NET Razor类库 - 静态资源组件化

1. 找到Razor类库 打开VS2022 文件 - 新建 - 项目 或者 使用 CtrlShiftN 快捷键 输入Razor 搜索 &#xff0c; 选中Razor类库&#xff0c; 点击 下一步2.创建Razor类库项目 输入项目名称 IX.RCL.Front RCL 是 RazorClassLibrary的简称意思 Front 代表前端静态资源的意思 位…

KEIL Stm32 bin文件生成的两种方法以及报错的处理

Keil里生成bin文件的方法有两种&#xff0c;记录如下&#xff0c;以免忘记~ 首先&#xff0c;在Keil主页面&#xff0c;点击如下按钮&#xff0c;打开Options for Target ‘target 1’对话框&#xff0c;并选择User标签页。 其次&#xff0c;通过在 User标签页 设置 “After B…

一种基于 JavaEE 的合同管理系统,用于存储、管理和跟踪合同的软件工具,功能很完善(附源码)

前言 在现代企业管理中&#xff0c;合同管理是一项至关重要的任务。传统的手动管理合同不仅耗时耗力&#xff0c;而且容易出现错误和遗漏&#xff0c;导致合同执行效率低下&#xff0c;甚至面临法律风险。随着业务的不断扩展&#xff0c;越来越多的企业开始寻求一种更为高效、…

作为HR,如何解决简历筛选难度大的问题

简历筛选需要耗费大量的时间和精力&#xff0c;尤其是当应聘者过多的情况下&#xff0c;往往让HR精疲力竭&#xff0c;而且效率低下。当下也有很多这方面的工具可以辅助的&#xff0c;如&#xff1a;招聘管理&#xff0c;简历解析类等。 借助软件&#xff0c;可以更好的获取简…

GLM-4-Flash 大模型API免费了,手把手构建“儿童绘本”应用实战(附源码)

老牛同学刚刷到了一条劲爆的消息&#xff0c;GLM-4-Flash大模型推理 API 免费了&#xff1a;https://bigmodel.cn/pricing 老牛同学一直觉得上次阿里云百炼平台为期 1 个月免费额度的“羊毛”已经够大了&#xff08;太卷了&#xff0c;阿里云免费 1 个月大模型算力额度&#xf…

什么是BI?BI系统的功能有哪些?哪些人需要BI工具支持?

什么是BI&#xff1f; BI是商业智能&#xff08;Business Intelligence&#xff09;的缩写。它是指通过收集、整理、分析和可视化企业内部和外部数据&#xff0c;从中获得洞察信息和决策支持的技术和流程。BI利用数据分析工具和技术&#xff0c;帮助企业管理者和决策者更好地理…

Cesium 展示——获取指定区域地形的最大最小高程

文章目录 需求分析方法一:方法二:需求 在地图上勾选某一处的区域,分析获取区域内最大最小高程 分析 方法一: function getAreaHeight(viewer, positions) {const startP = positions[0

备考AMC10美国数学竞赛2024:吃透1250道真题和知识点(持续)

有什么含金量比较高的初中生数学竞赛吗&#xff1f;美国数学竞赛AMC10是个不错的选择。那么&#xff0c;如何备考AMC10美国数学竞赛呢&#xff1f;做真题&#xff0c;吃透真题和背后的知识点是备考AMC8、AMC10有效的方法之一。 通过做真题&#xff0c;可以帮助孩子找到真实竞赛…

解决tensorflow模型加载时把GPU显存占满的问题(亲测效果显著)

第一次用tensorflow模型进行推理。 初始状态下&#xff0c;显存的占用情况 模型加载后&#xff0c;电脑显存直接快拉满了&#xff0c;但是模型参数量并不大&#xff0c;这就很奇怪,究竟这是一股什么神秘的力量&#xff0c;竟要试图侵占我可怜的显卡宝贝 原因分析:安装了tensor…

fastadmin表单中二维数组组件+图片上传

记录&#xff1a;fastadmin表单中二维数组组件图片上传 add.html代码 <div class"form-group"><label class"control-label col-xs-12 col-sm-2">{:__(Quanyi)}:</label><div class"col-xs-12 col-sm-10"><style>…

多个FeignClient使用相同服务名报错问题:使用contextId区分不同FeignClient

一、概述 在项目开发中&#xff0c;由于多个 FeignClient 使用了相同的 name &#xff0c;导致SpringBoot启动时出现 BeanDefinitionOverrideException 异常。 解决方案是在每个 FeignClient 上添加唯一的 contextId&#xff0c;如 userFeignClient 和 helloWorldFeignClient…

低功耗高效率同步降压变换器SiLM6880/SiLM6881 为工业应用领域电源提供强力支撑

为了满足现代电子设备对性能和能耗的更高要求&#xff0c;并不断推动电源技术的优化与创新&#xff0c;深力科推荐一款——低功耗、高效率的同步降压变换器SiLM6880/SiLM6881。 SiLM6880/SiLM6881具有宽泛的输入电压范围&#xff08;6V~80V&#xff09;&#xff0c;能适应各种电…

LLM分布式预训练浅析

随着深度学习的不断进步&#xff0c;语言模型的规模越来越大&#xff0c;参数量级已经达到了数千亿甚至数万亿&#xff0c;参数规模的指数增长带来了两个巨大的挑战 1&#xff09;模型参数过大&#xff0c;如GLM 130B模型参数需要520GB&#xff08;130B*4bytes&#xff09;的显…

【Latex】Latex 简介和安装

Latex LaTeX 是一种基于排版的文档准备系统&#xff0c;广泛用于创建高质量的科学和数学文档。它的核心是 TeX 语言&#xff0c;由美国计算机科学家 Donald Knuth 在 1978 年开发。LaTeX 简化了文档的格式化过程&#xff0c;尤其是在处理复杂的公式、引用、表格、图表等方面&a…

Java IO精髓:高效块读写入技术深入解析

文件的复制 首先我们先用昨天学习的知识来完成文件的复制,使用read和write方法来完成。 public static void main(String[] args) throws IOException {FileInputStream fis new FileInputStream("./image.png");FileOutputStream fos new FileOutputStream("…