SpringBoot使用redis实现session共享功能

 更新时间:2024年05月10日 09:51:02   作者:编程小猹  
这篇文章主要介绍了pringboot项目使用redis实现session共享,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下

1.安装redis,并配置密码

这里就不针对于redis的安装约配置进行说明了,直接在项目中使用。

redis在windows环境下安装:Window下Redis的安装和部署详细图文教程_Redis_脚本之家 (jb51.net)

2.pom.xml文件中引入需要的maven

     <dependency>
		<groupId>redis.clients</groupId>
		<artifactId>jedis</artifactId>
		<version>2.9.0</version>
	</dependency>

3.在项目的配置文件中加入redis的配置

redis:
  database: 0
  host: localhost
  password: 123456
  pool:
    max-active: 8
    max-idle: 8
    max-wait: -1
    min-idle: 0
  port: 6379
  timeout: 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;
 
	@Bean
	public JedisPool redisPoolFactory() {
		JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		jedisPoolConfig.setMaxIdle(maxIdle);
		jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
 
		JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout);
		return jedisPool;
	}
 
	@Bean
	public 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 seconds
 
    public SessionFilter(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
        this.objectMapper = new ObjectMapper();
    }
 
    @Override
    public 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 {
            // 检查 Cookie
            Cookie[] 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);
 
                        // 反序列化 Session
                        Map<String, Object> sessionAttributes = objectMapper.readValue(sessionDataJson, Map.class);
                        HttpSessionWrapper wrappedSession = new HttpSessionWrapper(sessionAttributes);
                        request.setAttribute("httpSession", wrappedSession);
 
                        // 继续执行过滤器链
                        chain.doFilter(request, response);
 
                        // 更新 Session 到 Redis
                        if (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 序列化会话并存储到 Redis
        String 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 分钟)
    @Bean
    public RedisHttpSessionConfiguration redisHttpSessionConfiguration() {
        RedisHttpSessionConfiguration configuration = new RedisHttpSessionConfiguration();
        configuration.setMaxInactiveIntervalInSeconds(1800);
        return configuration;
    }
 
    // 如果你使用的是 Spring Boot 2.3 或更高版本,你可能需要定义这个 Bean 来避免警告
    @Bean
    public 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");
    }
}

以上就是SpringBoot使用redis实现session共享的详细内容,更多关于SpringBoot redis session共享的资料请关注脚本之家其它相关文章!

相关文章

  • java分割字符串多种方法(附例子)

    java分割字符串多种方法(附例子)

    这篇文章主要给大家介绍了关于java分割字符串多种方法的相关资料,Java中有多种方法可以实现字符串分割,文中将每张方法都给出了代码示例,需要的朋友可以参考下
    2023-10-10
  • 14个编写Spring MVC控制器的实用小技巧(吐血整理)

    14个编写Spring MVC控制器的实用小技巧(吐血整理)

    这篇文章主要介绍了14个编写Spring MVC控制器的实用小技巧(吐血整理),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • SpringBoot项目中实现接口幂等的五种方法

    SpringBoot项目中实现接口幂等的五种方法

    本文介绍了SpringBoot项目中实现接口幂等性的五种主要方案,Token令牌机制、数据库唯一约束、乐观锁、分布式锁和请求摘要,具有一定的参考价值,感兴趣的可以了解一下
    2025-10-10
  • Java后端中dto、vo、entity的区别浅析

    Java后端中dto、vo、entity的区别浅析

    这篇文章主要给大家介绍了关于Java后端中dto、vo、entity区别的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2023-01-01
  • MybatisPlus实现数据权限隔离的示例详解

    MybatisPlus实现数据权限隔离的示例详解

    Mybatis Plus对Mybatis做了无侵入的增强,非常的好用,今天就给大家介绍它的其中一个实用功能:数据权限插件,感兴趣的可以跟随小编一起了解下
    2024-04-04
  • Java数组与字符串深入探索使用方法

    Java数组与字符串深入探索使用方法

    在今天的文章中,我将为你详细讲述Java学习中重要的一节 [ 数组与字符串 ] ,带你深入了解Java语言中数组的声明、创建和初始化方法,字符串的定义以及常用到的操作方法
    2022-07-07
  • Java中的LinkedHashSet集合解读

    Java中的LinkedHashSet集合解读

    这篇文章主要介绍了Java中的LinkedHashSet集合解读,LInkedHashSet这个容器不知道大家在平时的工作用的多吗,反正我基本上没有用过,所以,本文主要对于它的特点、使用场景、实现原理,做一个讲解,希望对大家平时的工作有所帮助,需要的朋友可以参考下
    2023-09-09
  • Java Swagger使用教程

    Java Swagger使用教程

    Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化 Restful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法、参数和模型紧密集成到服务器端的代码,允许API来始终保持同步
    2022-07-07
  • springboot项目Redis统计在线用户的实现示例

    springboot项目Redis统计在线用户的实现示例

    最近做个项目需要统计在线用户,本文主要介绍了springboot项目Redis统计在线用户的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-06-06
  • 使用Spring Security和JWT实现安全认证机制

    使用Spring Security和JWT实现安全认证机制

    在现代 Web 应用中,安全认证和授权是保障数据安全和用户隐私的核心机制,Spring Security 是 Spring 框架下专为安全设计的模块,具有高度的可配置性和扩展性,而 JWT则是当前流行的认证解决方案,所以本文介绍了如何使用Spring Security和JWT实现安全认证机制
    2024-11-11

最新评论