【身份认证与控制二】分布式session共享(序列化问题)
?
/*** 保存session** @param req 請求信心* @return 結果*/@GetMapping("/saveSession")public AjaxResult saveSession(HttpServletRequest req){System.out.println(new Date()+":::"+req.getSession().getId());HttpSession session = req.getSession();session.setAttribute("num",num++);String sessionString = JSON.toJSONString(session);redisFullUtil.set(session.getId(),sessionString);return AjaxResult.success(session.getId()+"::"+session.getAttribute("num"));}/*** 獲取保存的session** @param req 請求信息* @return 結果*/@GetMapping("/getSession")public AjaxResult getSession(HttpServletRequest req){System.out.println(new Date()+":::"+req.getSession().getId());String sessionString = redisFullUtil.get(req.getSession().getId());HttpSession session = JSON.parseObject(sessionString, HttpSession.class);return AjaxResult.success(session.getAttribute("num"));}程序運行后直接報錯
com.alibaba.fastjson.JSONException: illegal setter
?原因是redis序列化依賴于set和get方法,session中有些數據屬性是沒有的,
spring-session-data-redis
轉變思路一番搜索,現有技術是如何實現session分離,發現spring-session-data-redis就是專門做這件事的,還幫我們完成了大部分的工作,只需要做一些簡答的配置工作即可完成session的分離
1. pom引入依賴
<!-- spring boot 與 redis 應用的基本配置 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency><!-- spring session 與redis 應用環境基本配置, 需要開啟redis后才可以使用, 不然啟動會報錯 --> <dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId> </dependency>2. 配置: application.yml
spring:redis:database: 0host: 你的redisipport: 6379password: 你的redis密碼pool:max-idle: 8min-idle: 0max-active: 8max-wait: 1timeout: 5000session:store-type: redisredis:hostname: 你的redisipport: 6379pawssword: 你的redis密碼3. 增加配置類
SessionConfig.Class
package org.huber.sharesession.config;import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; import org.springframework.session.web.http.DefaultCookieSerializer;import javax.validation.Valid;/*** @Classname SessionConfig* @Description TODO* @Date 2020/1/17 12:43* @Author by Ren Jie*/ @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) public class SessionConfig {@Value("{$redis.hostname}")private String hostName;@Value("${redis.port}")private int port;@Beanpublic RedisStandaloneConfiguration connectionFactory() {RedisStandaloneConfiguration jedisConnectionFactory = new RedisStandaloneConfiguration();jedisConnectionFactory.setHostName(hostName);jedisConnectionFactory.setPort(port);return jedisConnectionFactory;}@Beanpublic DefaultCookieSerializer defaultCookieSerializer(){DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();defaultCookieSerializer.setCookiePath("/");return defaultCookieSerializer;} }核心就在這個反序列化上?
package org.huber.sharesession.config;import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;/*** @Classname SessionInitializer* @Description TODO* @Date 2020/1/17 12:47* @Author by Ren Jie*/ public class SessionInitializer extends AbstractHttpSessionApplicationInitializer {public SessionInitializer(){super(SessionConfig.class);} }結果測試
如果是同一個sessionid,則說明session分離成功
演示如下:
?
完了還是可能有問題:
我們來看看redis的序列化:
對redis進行配置,實現發序列化,因為reids序列化已經實現了,這里就不用再對序列化進行研究了(redis序列化見文章:DefaultSerializer requires a Serializable payload but received an object of type [select.system.dto.User] - 葉語婷 - 博客園)。
這里對key和value分別進行反序列化,進而在redis中生成字符型的key和對象型value。
/*** @author yeyuting* @create 2021/2/19*/ @Configuration public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();template.setConnectionFactory(factory);//key序列化方式template.setKeySerializer(redisSerializer);//value序列化template.setValueSerializer(new JdkSerializationRedisSerializer());//value hashmap序列化template.setHashValueSerializer(redisSerializer);//key haspmap序列化template.setHashKeySerializer(redisSerializer);return template;}}執行后redis中是這樣的
?可知,key反序列話成功了,但是value對象反序列化失敗了,接著就將注意力轉移到value對象的反序列化中來。
當我們的數據存儲到Redis的時候,我們的鍵(key)和值(value)都是通過Spring提供的Serializer序列化到數據庫的。RedisTemplate默認使用的是JdkSerializationRedisSerializer,StringRedisTemplate默認使用的是StringRedisSerializer。?用JdkSerializationRedisSerializer序列化的話,被序列化的對象必須實現Serializable接口。在存儲內容時,除了屬性的內容外還存了其它內容在里面,總長度長,且不容易閱讀。我們要求是存儲的數據可以方便查看,也方便反系列化,方便讀取數據。JacksonJsonRedisSerializer和GenericJackson2JsonRedisSerializer,兩者都能系列化成json,但是后者會在json中加入@class屬性,類的全路徑包名,方便反系列化。前者如果存放了List則在反系列化的時候如果沒指定TypeReference則會報錯java.util.LinkedHashMap cannot be cast to 。
因此保險起見,我們將JdkSerializationRedisSerializer換成GenericJackson2JsonRedisSerializer,修改后的代碼如下:
/*** @author yeyuting* @create 2021/2/19*/ @Configuration public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();template.setConnectionFactory(factory);//key序列化方式template.setKeySerializer(redisSerializer);//value序列化template.setValueSerializer(new GenericJackson2JsonRedisSerializer());//value hashmap序列化template.setHashValueSerializer(redisSerializer);return template;}}執行后redis緩存內容正常顯示,如下:
原理就是將httpSession替換成自己實現的httpsession
參考:1、Redis應用:session分離 【session序列化失敗 JSONException異常 spring-session-data-redis實現】_奮斗zhe的博客-CSDN博客
?2、Java簡單實現session保存到redis的方法示例_李修睿的博客-CSDN博客_java session存入redis
3、?
總結
以上是生活随笔為你收集整理的【身份认证与控制二】分布式session共享(序列化问题)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【身份认证及权限控制一】单点登录
- 下一篇: idea 编译显示source1.3不支