SpringBoot使用redis實(shí)現(xiàn)session共享功能
1.安裝redis,并配置密碼
這里就不針對(duì)于redis的安裝約配置進(jìn)行說(shuō)明了,直接在項(xiàng)目中使用。
redis在windows環(huán)境下安裝:Window下Redis的安裝和部署詳細(xì)圖文教程_Redis_腳本之家 (jb51.net)
2.pom.xml文件中引入需要的maven
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
3.在項(xiàng)目的配置文件中加入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的配置文件放在項(xiàng)目的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.具體實(shí)現(xiàn)思路(手動(dòng)實(shí)現(xiàn))
實(shí)現(xiàn)思路
創(chuàng)建一個(gè)過(guò)濾器,攔截除了登錄之外的所有請(qǐng)求,判斷請(qǐng)求中是否存在cookie,如果存在cookie則判斷redis中是否存在以cookie為key的鍵值對(duì)數(shù)據(jù),如果有則取出對(duì)應(yīng)的value同時(shí)對(duì)這個(gè)key的過(guò)期時(shí)間進(jìn)行續(xù)期,如果沒(méi)有則返回一個(gè)響應(yīng),說(shuō)明登錄已經(jīng)過(guò)期了,將Value就是session進(jìn)行Jason反序列化得到session對(duì)象,然后把Session綁定到當(dāng)前的請(qǐng)求中,如果不存在cookie,則直接返回一個(gè)響應(yīng),說(shuō)明還未登錄。如果是登錄請(qǐng)求的話,直接到controller中進(jìn)行登錄校驗(yàn),讓深沉的session通過(guò)json序列化放到redis中,并且以u(píng)uid為key,同時(shí),返回給前端一個(gè)cookie字段,cookie字段的值就是uuid,請(qǐng)求完成之后,在過(guò)濾器中將會(huì)話數(shù)據(jù)session更新到redis中。
下面是思路流程圖
代碼實(shí)現(xiàn)
創(chuàng)建過(guò)濾器 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)) { // 直接轉(zhuǎn)發(fā)到登錄控制器 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) { // 續(xù)期 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); // 繼續(xù)執(zhí)行過(guò)濾器鏈 chain.doFilter(request, response); // 更新 Session 到 Redis if (wrappedSession.isDirty()) { jedis.set(sessionId, objectMapper.writeValueAsString(wrappedSession.getSessionData())); } } else { // 登錄過(guò)期 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 方法 ... }
注冊(cè)過(guò)濾器
在 Spring 配置中注冊(cè)過(guò)濾器:
@Bean public FilterRegistrationBean<SessionFilter> sessionFilterRegistration(SessionFilter sessionFilter) { FilterRegistrationBean<SessionFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(sessionFilter); registrationBean.addUrlPatterns("/*"); return registrationBean; }
創(chuàng)建 HttpSessionWrapper 類
將session和sessionid封裝到這個(gè)類里面
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; } // ... 實(shí)現(xiàn) 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) { // ... 登錄邏輯 ... // 創(chuàng)建新的會(huì)話 String sessionId = UUID.randomUUID().toString(); Map<String, Object> sessionAttributes = new HashMap<>(); // 填充會(huì)話屬性 sessionAttributes.put("user", user); // 使用 JsonUtil 序列化會(huì)話并存儲(chǔ)到 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"; } // 創(chuàng)建 Cookie 并設(shè)置給客戶端 Cookie sessionCookie = new Cookie("SESSIONID", sessionId); sessionCookie.setPath("/"); sessionCookie.setHttpOnly(true); // 確保 Cookie 不會(huì)被 JavaScript 訪問(wèn) 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{ //對(duì)象的所有字段全部列入 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串,看起來(lái)比較好看,但是有換行符等符號(hào),會(huì)比沒(méi)有格式化的大 * @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; } } /** * 轉(zhuǎn)換集合 * 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框架實(shí)現(xiàn)
引入Spring Session Data Redis 的依賴
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>2.7.0</version> </dependency>
創(chuàng)建Spring Session的配置類
創(chuàng)建一個(gè)配置類 SessionConfig
,使用 @EnableRedisHttpSession
注解來(lái)啟用 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 { // 配置會(huì)話過(guò)期時(shí)間(例如,設(shè)置為 1800 秒,即 30 分鐘) @Bean public RedisHttpSessionConfiguration redisHttpSessionConfiguration() { RedisHttpSessionConfiguration configuration = new RedisHttpSessionConfiguration(); configuration.setMaxInactiveIntervalInSeconds(1800); return configuration; } // 如果你使用的是 Spring Boot 2.3 或更高版本,你可能需要定義這個(gè) Bean 來(lái)避免警告 @Bean public static ConfigureRedisAction configureRedisAction() { return ConfigureRedisAction.NO_OP; } }
@EnableRedisHttpSession
是一個(gè)方便的注解,它做了以下幾件事情:
- 啟用 Spring Session 的支持,使得 HttpSession 能夠被 Spring Session 管理。
- 配置 Redis 作為會(huì)話數(shù)據(jù)的存儲(chǔ)后端。
- 注冊(cè)一個(gè)
SessionRepositoryFilter
的 Bean,這個(gè) Filter 負(fù)責(zé)攔截請(qǐng)求,并將標(biāo)準(zhǔn)的 HttpSession 替換為 Spring Session 的實(shí)現(xiàn)。
Session的創(chuàng)建存儲(chǔ)和獲取
做完上面的配置之后,你可以像使用常規(guī) HttpSession 一樣使用 Spring Session。每次修改會(huì)話時(shí),Spring Session 都會(huì)自動(dòng)將這些更改同步到 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實(shí)現(xiàn)session共享的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot redis session共享的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java在linux系統(tǒng)下開(kāi)機(jī)啟動(dòng)無(wú)法使用sudo命令的原因及解決辦法
每次開(kāi)機(jī)自動(dòng)啟動(dòng)的java進(jìn)程,頁(yè)面上的關(guān)機(jī)按鈕都無(wú)法實(shí)現(xiàn)關(guān)機(jī)功能,但是此時(shí)如果以chb賬號(hào)通過(guò)ssh登錄該服務(wù)器,手動(dòng)殺掉tomcat進(jìn)程,然后再重新啟動(dòng)tomcat,頁(yè)面上的關(guān)機(jī)按鈕就有效了2013-08-08Mybatis實(shí)現(xiàn)一對(duì)多映射處理
MyBatis是一種流行的Java持久化框架,這篇文章主要為大家介紹了Mybatis如何實(shí)現(xiàn)一對(duì)多映射處理,文中的示例代碼講解詳細(xì),需要的可以參考下2023-08-08使用EasyPoi實(shí)現(xiàn)word文檔生成和段落循環(huán)
EasyPoi是一個(gè)Java的Excel和Word處理庫(kù),主要用于將Java對(duì)象轉(zhuǎn)換為Excel或Word文檔,本文主要介紹了如何使用EasyPoi實(shí)現(xiàn)word文檔生成和段落循環(huán),有需要的可以了解下2025-04-04java多線程數(shù)據(jù)分頁(yè)處理實(shí)例講解
在本篇內(nèi)容里小編給大家分享了一篇關(guān)于java多線程數(shù)據(jù)分頁(yè)處理實(shí)例講解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-01-01Java的關(guān)鍵字與標(biāo)識(shí)符小結(jié)
這篇文章主要介紹了Java的關(guān)鍵字與標(biāo)識(shí)符,總結(jié)整理了Java各種常見(jiàn)的關(guān)鍵字與標(biāo)識(shí)符功能、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04