分散式環境下Session共享問題解決和原理講解
1、分散式環境下Session共享問題:
2、幾種解決方法
3、通過後端統一儲存方法在實際專案中問題的體現:
當session的作用域只限於auth.gulimall.com時,在auth.gulimall.com下登入賬號所返回包含使用者資訊的session無法共享給gulimall.com
當我們把作用域放大更改為.gulimall.com時,auth.gulimall.com下登入賬號所返回包含使用者資訊的session就能共享給gulimall.com
至此解決session共享跨域問題的核心關鍵為放大session的作用域範圍。
4、利用SpringSession作用域問題
①匯入必要的包
<!--springsession解決session共享問題--> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>2.2.0.RELEASE</version> </dependency>
②在需要進行session共享的微服務的配置檔案中新增指定session存取至redis
spring.session.store-type=redis
③在需要進行session共享的微服務主啟動類上開啟reids的session的存取功能
@EnableRedisHttpSession//整合redis作為session存取 @EnableDiscoveryClient @EnableFeignClients @SpringBootApplication public class GulimallAuthServerApplication { public staticvoid main(String[] args) { SpringApplication.run(GulimallAuthServerApplication.class, args); } } @EnableRedisHttpSession//整合redis作為session存取 @EnableCaching @EnableFeignClients(basePackages = "com.atguigu.gulimall.product.feign") @EnableDiscoveryClient @MapperScan(basePackages = "com.atguigu.gulimall.product.dao") @SpringBootApplication public class GulimallProductApplication { public static void main(String[] args) { SpringApplication.run(GulimallProductApplication.class, args); } }
④讓session在不同域名下進行共享並對session進行序列化並以json格式的方式儲存
官方文件:
實際程式碼:
@Configuration public class GulimallSessionConfig { //解決session跨域問題 @Bean public CookieSerializer cookieSerializer() { DefaultCookieSerializer cookieSerializer= new DefaultCookieSerializer(); //將session作用域放大到*.gulimall.com cookieSerializer.setDomainName("gulimall.com"); cookieSerializer.setCookieName("GULISESSION"); /* serializer.setCookieName("JSESSIONID"); serializer.setCookiePath("/"); serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");*/ return cookieSerializer; } //Session序列化後轉為json格式 @Bean public RedisSerializer<Object> springSessionDefaultRedisSerializer() { return new GenericJackson2JsonRedisSerializer(); } }
5、結果:
當我在auth.gulimall.com域名下的登入服務下將使用者的賬戶資訊傳入session時:
if (oauthlogin.getCode()==0){ MemberResVo data = oauthlogin.getData("data", new TypeReference<MemberResVo>() {}); //TODO:1、session只作用當前域,無法跨域訪問 //TODO:2、希望能使用JSON序列化物件 session.setAttribute("loginUser",data); return "redirect:http://gulimall.com"; }
此處使用MemberResVo實體類,所以要對其進行序列化:
@ToString @Data public class MemberResVo implements Serializable { private Long id; /** * 會員等級id */ private Long levelId; /** * 使用者名稱 */ private String username; /** * 密碼 */ private String password; //private ..... //private ..... }
至此我們可以將session返回給gulimall.com取得值並進行顯示:
<a href="http://auth.gulimall.com/login.html">你好,[[${session.loginUser!=null?(session.loginUser.nickname!=null?session.loginUser.nickname:session.loginUser.socialUid):'請登入'}]]</a>
6、@EnableRedisHttpSession原理
@EnableRedisHttpSession匯入RedisHttpSessionConfiguration.class
1、RedisHttpSessionConfiguration在容器中添加了RedisIndexedSessionRepository元件:redis操作session,對資料進行持久化處理
2、被RedisHttpSessionConfiguration繼承的SpringHttpSessionConfiguration中添加了SessionRepositoryFilter(session過濾器)
2.1、SessionRepositoryFilter建立時自動獲取到SessionRepository;
2.2、SessionRepositoryFilter的doFilterInternal方法把原生的request和response被包裝成wrappedRequest和wrappedResponse,以後獲取session將不再通過原生的request.session()方法而是通過wrappedRequest.getsession(),wrappedRequest.getsession()方法中重寫了request.session(),wrappedRequest.getsession()的session是從SessionRepository獲取得到的,做到從redis獲取session
核心程式碼:
所以,我們可以通過自定義SessionRepository介面更改對session的增刪查改方法