Springboot项目使用redis实现session共享
1.安装redis,并配置密码
这里就不针对于redis的安装约配置进行说明了,直接在项目中使用。
redis在windows环境下安装:Window下Redis的安装和部署详细图文教程(Redis的安装和可视化工具的使用)_redis安装-CSDN博客
2.pom.xml文件中引入需要的maven
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version></dependency>
3.在项目的配置文件中加入redis的配置
redis:database: 0host: localhostpassword: 123456pool:max-active: 8max-idle: 8max-wait: -1min-idle: 0port: 6379timeout: 3000
4.添加redis的配置文件放在项目的config文件夹下
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;/*** @author kjz*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {@Value("${redis.host}")private String host;@Value("${redis.port}")private int port;@Value("${redis.timeout}")private int timeout;@Value("${redis.pool.max-idle}")private int maxIdle;@Value("${redis.pool.max-wait}")private long maxWaitMillis;@Value("${redis.password}")private String password;@Beanpublic JedisPool redisPoolFactory() {JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxIdle(maxIdle);jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout);return jedisPool;}@Beanpublic RedisConnectionFactory redisConnectionFactory() {JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxIdle(maxIdle);jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(jedisPoolConfig);jedisConnectionFactory.setHostName(host);jedisConnectionFactory.setPort(port);jedisConnectionFactory.setTimeout(timeout);jedisConnectionFactory.setPassword(password);return jedisConnectionFactory;}}
5.具体实现思路(手动实现)
实现思路
创建一个过滤器,拦截除了登录之外的所有请求,判断请求中是否存在cookie,如果存在cookie则判断redis中是否存在以cookie为key的键值对数据,如果有则取出对应的value同时对这个key的过期时间进行续期,如果没有则返回一个响应,说明登录已经过期了,将Value就是session进行Jason反序列化得到session对象,然后把Session绑定到当前的请求中,如果不存在cookie,则直接返回一个响应,说明还未登录。如果是登录请求的话,直接到controller中进行登录校验,让深沉的session通过json序列化放到redis中,并且以uuid为key,同时,返回给前端一个cookie字段,cookie字段的值就是uuid,请求完成之后,在过滤器中将会话数据session更新到redis中。
下面是思路流程图
代码实现
创建过滤器 SessionFilter
import com.fasterxml.jackson.databind.ObjectMapper;
import redis.clients.jedis.Jedis;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;public class SessionFilter implements Filter {private JedisPool jedisPool;private ObjectMapper objectMapper;private static final String LOGIN_PATH = "/login";private static final int SESSION_EXPIRATION_TIME = 30 * 60; // 30 minutes in secondspublic SessionFilter(JedisPool jedisPool) {this.jedisPool = jedisPool;this.objectMapper = new ObjectMapper();}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;HttpServletResponse httpResponse = (HttpServletResponse) response;String requestUri = httpRequest.getRequestURI();if (requestUri.equals(LOGIN_PATH)) {// 直接转发到登录控制器chain.doFilter(request, response);} else {// 检查 CookieCookie[] cookies = httpRequest.getCookies();String sessionId = null;if (cookies != null) {for (Cookie cookie : cookies) {if ("SESSIONID".equals(cookie.getName())) {sessionId = cookie.getValue();break;}}}if (sessionId != null) {try (Jedis jedis = jedisPool.getResource()) {String sessionDataJson = jedis.get(sessionId);if (sessionDataJson != null) {// 续期jedis.expire(sessionId, SESSION_EXPIRATION_TIME);// 反序列化 SessionMap<String, Object> sessionAttributes = objectMapper.readValue(sessionDataJson, Map.class);HttpSessionWrapper wrappedSession = new HttpSessionWrapper(sessionAttributes);request.setAttribute("httpSession", wrappedSession);// 继续执行过滤器链chain.doFilter(request, response);// 更新 Session 到 Redisif (wrappedSession.isDirty()) {jedis.set(sessionId, objectMapper.writeValueAsString(wrappedSession.getSessionData()));}} else {// 登录过期httpResponse.setContentType("application/json");httpResponse.getWriter().write("{\"error\": \"Session expired\"}");}} catch (Exception e) {// 处理异常e.printStackTrace();httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);}} else {// 未登录httpResponse.setContentType("application/json");httpResponse.getWriter().write("{\"error\": \"Not logged in\"}");}}}// ... 其他 Filter 方法 ...
}
注册过滤器
在 Spring 配置中注册过滤器:
@Bean
public FilterRegistrationBean<SessionFilter> sessionFilterRegistration(SessionFilter sessionFilter) {FilterRegistrationBean<SessionFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(sessionFilter);registrationBean.addUrlPatterns("/*");return registrationBean;
}
创建 HttpSessionWrapper 类
将session和sessionid封装到这个类里面
import javax.servlet.http.HttpSession;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;public class HttpSessionWrapper implements HttpSession {private final Map<String, Object> attributes;private boolean dirty;public HttpSessionWrapper(Map<String, Object> attributes) {this.attributes = attributes;this.dirty = false;}// ... 实现 HttpSession 接口的方法 ...public void setAttribute(String name, Object value) {attributes.put(name, value);dirty = true;}public Map<String, Object> getSessionData() {return attributes;}public boolean isDirty() {return dirty;}// ... 其他方法 ...
}
登录控制器
@RestController
public class LoginController {@PostMapping("/login")public HttpServletResponse login(HttpServletRequest request, HttpServletResponse response) {// ... 登录逻辑 ...// 创建新的会话String sessionId = UUID.randomUUID().toString();Map<String, Object> sessionAttributes = new HashMap<>();// 填充会话属性sessionAttributes.put("user", user);// 使用 JsonUtil 序列化会话并存储到 RedisString sessionDataJson = JsonUtil.obj2String(sessionAttributes);try (Jedis jedis = jedisPool.getResource()) {jedis.setex(sessionId, SESSION_EXPIRATION_TIME, sessionDataJson);} catch (Exception e) {// 处理异常e.printStackTrace();return "Error";}// 创建 Cookie 并设置给客户端Cookie sessionCookie = new Cookie("SESSIONID", sessionId);sessionCookie.setPath("/");sessionCookie.setHttpOnly(true); // 确保 Cookie 不会被 JavaScript 访问sessionCookie.setSecure(true); // 确保 Cookie 在 HTTPS 连接中传输response.addCookie(sessionCookie);return HttpServletResponse ;}
}
下面是序列化工具类
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;/***@author kjz*/
@Slf4j
public class JsonUtil {private static ObjectMapper objectMapper = new ObjectMapper();static{//对象的所有字段全部列入objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);}public static <T> String obj2String(T obj){if(obj == null){return null;}try {return obj instanceof String ? (String)obj : objectMapper.writeValueAsString(obj);} catch (Exception e) {log.warn("Parse Object to String error",e);return null;}}/*** 格式化json串,看起来比较好看,但是有换行符等符号,会比没有格式化的大* @param obj* @param <T>* @return*/public static <T> String obj2StringPretty(T obj){if(obj == null){return null;}try {return obj instanceof String ? (String)obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);} catch (Exception e) {log.warn("Parse Object to String error",e);return null;}}public static <T> T string2Obj(String str,Class<T> clazz){if(StringUtils.isEmpty(str) || clazz == null){return null;}try {return clazz.equals(String.class)? (T)str : objectMapper.readValue(str,clazz);} catch (Exception e) {log.warn("Parse String to Object error",e);return null;}}public static <T> T string2Obj(String str, TypeReference<T> typeReference){if(StringUtils.isEmpty(str) || typeReference == null){return null;}try {return (T)(typeReference.getType().equals(String.class)? str : objectMapper.readValue(str,typeReference));} catch (Exception e) {log.warn("Parse String to Object error",e);return null;}}/*** 转换集合* List<User></>* @param str* @param collectionClass* @param elementClasses* @param <T>* @return*/public static <T> T string2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses);try {return objectMapper.readValue(str,javaType);} catch (Exception e) {log.warn("Parse String to Object error",e);return null;}}
}
6.利用Spring Session Data Redis框架实现
引入Spring Session Data Redis 的依赖
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId><version>2.7.0</version></dependency>
创建Spring Session的配置类
创建一个配置类 SessionConfig
,使用 @EnableRedisHttpSession
注解来启用 Spring Session 的 Redis 支持:
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@EnableRedisHttpSession
public class SessionConfig {// 配置会话过期时间(例如,设置为 1800 秒,即 30 分钟)@Beanpublic RedisHttpSessionConfiguration redisHttpSessionConfiguration() {RedisHttpSessionConfiguration configuration = new RedisHttpSessionConfiguration();configuration.setMaxInactiveIntervalInSeconds(1800);return configuration;}// 如果你使用的是 Spring Boot 2.3 或更高版本,你可能需要定义这个 Bean 来避免警告@Beanpublic static ConfigureRedisAction configureRedisAction() {return ConfigureRedisAction.NO_OP;}
}
@EnableRedisHttpSession
是一个方便的注解,它做了以下几件事情:
- 启用 Spring Session 的支持,使得 HttpSession 能够被 Spring Session 管理。
- 配置 Redis 作为会话数据的存储后端。
- 注册一个
SessionRepositoryFilter
的 Bean,这个 Filter 负责拦截请求,并将标准的 HttpSession 替换为 Spring Session 的实现。
Session的创建存储和获取
做完上面的配置之后,你可以像使用常规 HttpSession 一样使用 Spring Session。每次修改会话时,Spring Session 都会自动将这些更改同步到 Redis。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpSession;@RestController
public class SessionController {@GetMapping("/setSession")public String setSession(HttpSession session) {session.setAttribute("message", "Hello, Redis Session!");return "Session set in Redis";}@GetMapping("/getSession")public String getSession(HttpSession session) {return "Session message: " + session.getAttribute("message");}
}
相关文章:
Springboot项目使用redis实现session共享
1.安装redis,并配置密码 这里就不针对于redis的安装约配置进行说明了,直接在项目中使用。 redis在windows环境下安装:Window下Redis的安装和部署详细图文教程(Redis的安装和可视化工具的使用)_redis安装-CSDN博客 2…...
【Linux】Centos7安装部署unimrcp,搭建MRCP服务器
yum install libtool yum install libtool-ltdl-devel yum install libsofia-sip-ua find / -name libsofia-sip-ua.so.0 2>/dev/null # 设置环境变量:如果库文件存在但不在默认搜索路径中,你可以通过设置 LD_LIBRARY_PATH 环境变量来告诉系统在哪…...
什么是Jetpack
Jetpack Jetpack 是一套组件库、工具,可帮助开发人员遵循最佳做法,减少样板代码并编写可在 Android 版本和设备上一致工作的代码,以便开发人员可以专注于他们关心的代码 组成 主要包含四部分:架构(Architecture&…...
macOS sonoma 14.4.1编译JDK 12
macOS sonoma 14.4.1编译JDK 12 环境参考文档开始简述问题心路历程着手解决最终解决(前面有点啰嗦了,可以直接看这里) 记录一次靠自己看代码解决问题的经历(总之就是非常开心)。 首先,先diss一下bing,我差一点就放弃了。 环境 macOS sonom…...
GPU通用计算介绍
谈到 GPU (Graphics Processing Unit,图形显示卡)大多数人想到的是游戏、图形渲染等这些词汇,图形处理确实是 GPU 的一大应用场景。然而人们也早已关注到它在通用计算上的巨大潜力,并提出了 GPGPU (General-purpose co…...
如果数据给的是树形 转好的树形结构并且是有两个二级children的话 该如何写?
第一我们要自己写一个children 并且张数据里面的所要渲染的二级进行赋值 赋给我们新建的children 以下是代码转树形赋值 organ().then(function (res) {console.log(res); // 成功回调// setLists(res.data.data)res.data.data res.data.data.map((obj) > ({...obj, // …...
C++ 函数重载
两个以上的函数,具有相同的函数名,但是形参的个数或者类型不同,编译器会根据实参的类型机个数的最佳来自动调用哪一个函数。 一 带默认形参值的函数 在定义函数时预先声明默认的形参值。调用时如果给出实参,则用实参初始化形…...
5. 分布式链路追踪TracingFilter改造增强设计
前言 在4. 分布式链路追踪客户端工具包Starter设计一文中,我们实现了基础的Starter包,里面提供了我们自己定义的Servlet过滤器和RestTemplate拦截器,其中Servlet过滤器叫做HoneyTracingFilter,仅提供了提取SpanContext࿰…...
C++数据类型与表达式
一 C中的数据类型 二 基本数据类型 三 类型转换 各种类型的高低顺序如下所述; 四 构造数据类型 类类型...
电脑ip地址设置成什么比较好
随着信息技术的快速发展,IP地址已成为电脑在网络世界中的“身份证”。它不仅是电脑在网络中进行通信的基础,也直接关系到网络连接的稳定性、安全性和效率。然而,面对众多IP地址设置选项,许多用户可能会感到困惑。那么,…...
vue-element-template优化升级dart-sass、pnpm
1、替换 node-sass 为 dart-sass - "node-sass": "^4.9.0","sass": "^1.75.0",替换css深度作用域写法 /deep/ >>># 替换为::v-deepVue:Node Sass VS. Dart Sass 2、替换npm为pnpm,需要补充一些依赖…...
Oracle拼接json字符串
在Oracle数据库中,并没有内建的JSON处理函数像其他现代数据库那样直接。但是,你可以使用字符串连接和格式化技巧来拼接JSON字符串。 以下是一个简单的例子,说明如何在Oracle中拼接一个JSON字符串: sql DECLARE v_json_string V…...
如何向Linux内核提交开源补丁?
2021年,我曾经在openEuler社区上看到一项改进Linux内核工具的需求,因此参与过Linux内核社区的开源贡献。贡献开源社区的流程都可以在内核社区文档中找到,但是,单独学习需要一个较长的过程,新手难以入门,因此…...
python数据分析——pandas DataFrame基础知识2
参考资料:活用pandas库 1、分组方式 我们可以把分组计算看作“分割-应用-组合”(split-apply-combine)的过程。首先把数据分割成若干部分,然后把选择的函数(或计算)应用于各部分,最后把所有独立…...
TODESK远程开机的原理
在现代计算机技术飞速发展的背景下,远程控制软件成为我们日常工作中不可或缺的工具。其中,ToDesk作为一款高效且易用的远程控制软件,备受用户青睐。那么,ToDesk远程开机的原理是什么呢?本文将为你揭晓这个秘密。 KKVie…...
【c1】数据类型,运算符/循环,数组/指针,结构体,main参数,static/extern,typedef
文章目录 1.数据类型:编译器(compiler)与解释器(interpreter),中文里的汉字和标点符号是两个字节,不能算一个字符(单引号)2.运算符/循环:sizeof/size_t3.数组…...
word图片水印
一、word中旧水印如何删除 打开word模板,想要删除旧水印,如下图所示操作,但是旧水印删除不掉。 以为上传新水印图片会替换掉旧水印,结果显示了2个水印,要怎么删除呢? 如下截图所示,双击打开页…...
kali安装及替换源
一、安装及简单配置 1.安装:地址就不贴了,自己打一下就好 2.虚拟机中打开kali 3.替换包源 (1)使用指令打开/etc/apt/sources.list mousepad /etc/apt/sources.list (2)将内容替换成阿里云源 deb http://mirrors.aliyun.com/kali kali-rolling main n…...
JSpdf,前端下载大量表格数据pdf文件,不创建dom
数据量太大使用dom》canvas》image》pdf.addimage方法弊端是canvas超出 浏览器承受像素会图片损害,只能将其切割转成小块的canvas,每一次调用html2canvas等待时间都很长累积时间更长,虽然最终可以做到抽取最小dom节点转canvas拼接数据,但是死…...
PHP关联数组[区别,组成,取值,遍历,函数]
关联数组 相较于数值数组,关联数组的索引可以为字符串和数字,关联数组元素也可称为键值对,索引为键,值为值。 源码 <?php echo "<hr>"; //水平线标签//关联数组$arr3 array(); //创建空的数组//关联数…...
JavaWeb--13Mybatis(2)
Mybatis(2) 1 Mybatis基础操作1.1 需求和准备工作1.2 删除员工日志输入参数占位符 1.3 新增员工1.4 修改员工信息1.5 查询员工1.5.1 根据ID查询数据封装 1.5.3 条件查询 2 XML配置文件规范3 MyBatis动态SQL3.1 什么是动态SQL3.2 动态SQL-if更新员工 3.3 …...
如何远程控制另一部手机:远程控制使用方法
在现今高科技的社会中,远程控制手机的需求在某些情境下变得越来越重要。不论是为了协助远在他乡的家人解决问题,还是为了确保孩子的在线安全,了解如何实现这一功能都是有益的。本文将为您简要介绍几种远程控制手机的方法及其使用要点。 KKVi…...
x64dbg中类似于*.exe+地址偏移
在CE和xdb中,形如*.exe数字偏移形式的地址被称为模块地址,CE附加到进程后点击查看内存,显示如下图 这种地址学名叫做模块地址,在x64dbg中显示如下图: CE中可以关闭,从而显示绝对的虚拟地址,如下…...
ICode国际青少年编程竞赛- Python-1级训练场-基础训练1
ICode国际青少年编程竞赛- Python-1级训练场-基础训练1 1、 Dev.step(4)2、 Dev.step(-4) Dev.step(8)3、 Dev.turnLeft() Dev.step(4)4、 Dev.step(3) Dev.turnLeft() Dev.step(-1) Dev.step(4)5、 Dev.step(-1) Dev.step(3) Dev.step(-2) Dev.turnLeft() Dev.step(…...
Baidu Comate智能编码助手
Baidu Comate智能编码助手 🎈1.Baidu Comate的简介🎈2.安装Baidu Comate🎈3.Baidu Comate实现功能🎈4.使用注释进行智能代码提示🎈5.结束语 🎈1.Baidu Comate的简介 根据官网的介绍,我们了解到B…...
nginx自动部署-跨操作系统
项目里面有一个需求,就是需要用让nginx进程提供给系统管理一个start,stop和getPid方法,这样系统管理可以自动拉起来nginx,达到自动部署的目的。离线部署同样适用 这样一来,我就需要提供windows版本linux不同版本的nginx源码包&am…...
组合模式(结构型)
目录 一、前言 二、透明组合模式 三、安全组合模式 四、总结 一、前言 组合模式(Composite Pattern)是一种结构型设计模式,将对象组合成树形结构以表示“部分-整体”得层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 组合模式由以下角色组成…...
Pspice for TI学习
Pspice for TI中PSpice Part Search空白解决方法 配置环境变量 Cad_PSpice_TI_Regr_Srvr https://software-dl.ti.com/pspice/S009 重新安装2023版的Pspice Pspice安装链接 打开新安装的软件即可发现PSpice Part Search可以正常使用了 VSIN各参赛的含义 VOFF直流偏置VAMPL…...
LoRA的原理简介
在文章开始前先澄清一个概念,需要区分形近的单词"LoRa"(long range),这是一项通信技术。熟悉物联网行业的朋友相对会比较熟悉LoRa这项技术,因为有些设备比如电梯的控制就使用了这个技术进行本地数据和命令的…...
安卓使用Fiddler抓包 2024
简介 最近试了一下安卓使用fiddler 抓包,发现https包基本都会丢失。原因是Anandroid 7版本针对ssl安全性做了加强,不认可用户的证书。我们要做的就是把fiddler导出的证书进过处理后放置到系统证书目录下面,这样才能抓包https请求。 这里使用…...
房屋产权地址备案在那个网站做/企业培训公司有哪些
第一次观看我文章的朋友,可以关注、点赞、转发一下,每天分享各种干货技术和程序猿趣事 前言 职场的金三银四跳槽季又来了,不同的是今年比往年「冷」一些,形式更加严峻一些,大家多多少少可能都听到或看到一些信息&…...
专门做儿童的店铺网站/制作一个小型网站
Service(服务)是一个没有用户界面的在后台运行执行耗时操作的应用组件。其他应用组件能够启动Service,并且当用户切换到另外的应用场景,Service将持续在后台运行。另外,一个组件能够绑定到一个service与之交互…...
不断完善政府网站建设/谷歌浏览器搜索入口
android下的单元測试在AndroidManifest.xml文件里配置下面信息:在manifest节点下加入:<!-- 指定測试信息和要測试的包 --><instrumentationandroid:name"android.test.InstrumentationTestRunner"android:targetPackage"com.jxn…...
学做旗袍衣服的网站/指定关键词seo报价
Time Limit: 1 second Memory Limit: 128 MB 【问题描述】 N个数排成一排,你可以任意选择连续的若干个数,算出它们的和。问该如何选择才能使得和的绝对值最小。如:N8时,8个数如下:1 2 3 4 5 6 7 8-20 90 -30 -20 80 -70 -60 125如…...
做网站托管服务器/sem竞价推广是什么
开发和测试环境处于不同的网段,经常需要更改IP,手工修改非常麻烦。后来找到了一个vbs小脚本,实现了自动切换。 假如IP段分别为192.168.30.*和192.168.41.* 分别新添两个文件Turn30.vbs和Turn41.vbs ,内容如下: view plaincopy to clipboar…...
个人网站如何在工信部备案/百度推广业务员电话
某站后端代码被“开源”,同时刷遍全网的,还有代码里的那些神注释。 我们这才知道,原来程序员个个都是段子手;这么多年来,我们也走过了他们的无数套路… 首先,产品经理,是永远永远吐槽不完的&a…...