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 是一个方便的注解,它做了以下几件事情:

  1. 启用 Spring Session 的支持,使得 HttpSession 能够被 Spring Session 管理。
  2. 配置 Redis 作为会话数据的存储后端。
  3. 注册一个 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");}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++青少年简明教程:C++程序结构

C青少年简明教程&#xff1a;C程序结构 一个简单的C程序源码如下&#xff1a; #include <iostream> using namespace std;int main() {cout << "Hello World" << endl;return 0; }下面解析一下。 1. #include <iostream> 这是一条预处理…

Request请求数据 (** kwargs参数)

目录 &#x1f31f;前言&#x1f349;request入门1. params2. data3. json4. headers5. cookies6. auth7. files8. timeout9. proxies10. allow_redirects11. stream12. verify13. cert &#x1f31f;总结 &#x1f31f;前言 在Python中&#xff0c;发送网络请求是一项常见的任…

xCode升级后: Library ‘iconv2.4.0’ not found

报错信息&#xff1a; targets 选中 xxxNotification: Build Phases ——> Link Binary With Libraries 中&#xff0c;移除 libiconv.2.4.0.tbd libiconv.2.4.0.dylib 这两个库&#xff08;只有一个的移除一个就好&#xff09;。 然后重新添加 libiconv.tbd 修改完…

日本率先研发成功6G设备,刺痛了谁?为何日本能率先突破?

日本率先研发成功6G设备&#xff0c;无线数据速率是5G的百倍&#xff0c;这让日本方面兴奋莫名&#xff0c;毕竟日本在科技方面从1990年代以来太缺少突破的创新了&#xff0c;那么日本为何如今在6G技术上能率先突破呢&#xff1f; 日本在1980年代末期达到顶峰&#xff0c;它的科…

基于springboot+mybatis+vue的项目实战之(后端+前后端联调)

步骤&#xff1a; 1、项目准备&#xff1a;创建数据库&#xff08;之前已经创建则忽略&#xff09;&#xff0c;以及数据库连接 2、建立项目结构文件夹 3、编写pojo文件 4、编写mapper文件&#xff0c;并测试sql语句是否正确 5、编写service文件 6、编写controller文件 …

机器学习1——线性回归、误差推导

有监督——分类、回归 一、线性回归 对于一个线性方程&#xff0c;没办法拟合所有的数据点&#xff0c;但是要尽可能的覆盖尽可能多的点。 在下面的图中&#xff0c;x01。添加这一项的目的是&#xff1a;将数据矩阵补全&#xff08;比如年龄是x1、工资是x2&#xff0c;那么x0手…

JS解密之新js加密实战(二)

前言 上次发了一篇关于新加密的&#xff0c;只解了前边两层&#xff0c;这中间家里各种事情因素影响&#xff0c;没有继续进一步研究&#xff0c;今天百忙之中抽空发布第二篇&#xff0c;关于其中的一小段加密片段&#xff0c;我认为分割成多个小片段是更容易被理解的。逻辑相…

大规模 RGB LED灯控系统 Lumos:创新与智能化的融合

灯控系统&#xff1a;创新与智能化的融合 在现代照明技术不断进步的背景下&#xff0c;灯控系统的应用已经从简单的开关控制&#xff0c;发展到能够进行复杂程控操作的智能化管理。我们推出的新一代灯控解决方案&#xff0c;凭借其高度的可配置性和跨平台兼容性&#xff0c;已…

LVDS 源同步接口

传统数据传输通常采用系统同步传输方式&#xff0c;多个器件基于同一时钟源进行系统同步&#xff0c;器件之间的数据传输时序关系以系统时钟为参考&#xff0c;如图1所示。系统同步传输方式使各器件处于同步工作模式&#xff0c;但器件之间传输数据的传输时延难以确定&#xff…

大语言模型的数据预处理

文章目录 质量过滤敏感内容过滤数据去重 当收集了丰富的文本数据之后&#xff0c;为了确保数据的质量和效用&#xff0c;还需要对数据进行预处理&#xff0c;从而消除低质量、冗余、无关甚可能有害的数据。一般来说&#xff0c;需要构建并使用系统化的数据处理框架&#xff08;…

Find My腰包|苹果Find My技术与腰包结合,智能防丢,全球定位

腰包具有显瘦和显高的双重功效&#xff0c;它不仅能提高腰线、拉长腿部线条&#xff0c;还能遮住腹部多余的赘肉&#xff0c;从而在视觉上达到变高的效果&#xff0c;使整体看起来更加显瘦。除了时尚功能&#xff0c;腰包在运动中也有其独特的用途。例如&#xff0c;在跑步时&a…

大数据项目中的拉链表(hadoop,hive)

缓慢渐变维 拉链表 拉链表&#xff0c;可实现数据快照&#xff0c;可以将历史和最新数据保存在一起 如何实现: 在原始数据增加两个新字段 起始时间&#xff08;有效时间&#xff1a;什么时候导入的数据的时间&#xff09;&#xff0c;结束时间&#xff08;默认的结束时间为99…

day-35 二叉树的右视图

思路 根据层序遍历的思路。将每一层的最右边元素加入返回序列即可 解题方法 注意&#xff1a;链表删除一个数据后会立即重排&#xff0c;所以删除同一层的节点时&#xff0c;每次都删除第一个节点。 Code /*** Definition for a binary tree node.* public class TreeNode {…

企业智能照明控制系统 为企业实现智能化照明管理

工厂车间传统照明的问题及智能照明系统的优势 谢继东15821713522 一、工厂传统照明存在的问题&#xff1a; 1、工业厂房一般建筑结构高&#xff0c;跨距大。灯具安装悬挂高&#xff0c;照明空间大&#xff0c;灯具回路多&#xff0c;而车间是厂区对照明要求较高的区域&#xf…

Linux学习笔记4---点亮LED灯(汇编裸机)

本系统学习利用的是正点原子的阿尔法mini开发板&#xff0c;本系列的学习笔记也是按照正点原子的教程进行学习&#xff0c;但并不是利用虚拟机进行开发&#xff0c;而是使用Windows下的子系统WSL进行学习。 因为 Cortex-A 芯片一上电 SP 指针还没初始化&#xff0c;C 环境还没准…

Open CASCADE 教程 – AIS:自定义呈现

文章目录 开始 (Getting Started)呈现构建器 (Presentation builders)基元数组 (Primitive arrays)基元外观 (Primitive aspects)二次构建器 (Quadric builders)计算选择 (Computing selection)突出显示选择所有者 (Highlighting selection owner)突出显示的方法 (Highlighting…

[QT] 断点调试

目录 一 设置断点 二 调试窗口信息 2.1 默认窗口 2.2 详细窗口属性 三 调试方法和技巧 一 设置断点 在QtCreator中我们有两种方式添加断点。 用鼠标直接点击代码编辑窗口中的某一行按下F9添加/取消断点(操作的是当前鼠标光标所在的代码行) 二 调试窗口信息 2.1 默认窗…

Linux 信号保存

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;Linux知识分享⏪   &#x1f69a;代码仓库:Linux代码练习&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Linux知识   &#x1f51d; 目录 前言 阻塞信号 1. 信号其他相关常见…

MFC的CPen与CBush画图对象使用步骤

在MFC中&#xff0c;CPen和CBrush是两个常用的绘图对象&#xff0c;分别用于定义画笔和画刷&#xff0c;可以用于绘制图形、填充区域等。下面我会详细介绍如何在MFC中使用CPen和CBrush来绘制和填充图形。 使用 CPen 绘制图形&#xff1a; 创建 CPen 对象&#xff1a; 首先&am…

JAVA基础面试题(第十一篇)上! JVM

Hello好久不见&#xff01;&#xff0c;最近我们讲更新JVM部分的面试题。 JVM 这块比较难理解&#xff0c;而且也是不擅长的点。所以今天我更新一下JVM希望小伙伴们能在面试中取得好成绩&#xff01; JVM 1. 什么是JVM内存结构&#xff1f; jvm将虚拟机分为5大区域&#xff0…