上一篇文章中,我們使用Redis集成了Spring Session。大多數的配置都是Spring Boot幫我們自動配置的,這一節我們介紹一點Spring Session較為高級的特性。
## 集成Spring Security
之所以把Spring Session和Spring Security放在一起討論,是因為我們的應用在集成Spring Security之后,用戶相關的認證與Session密不可分,如果不注意一些細節,會引發意想不到的問題。
與Spring Session相關的依賴可以參考上一篇文章,這里給出增量的依賴:
~~~
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
~~~
我們引入依賴后,就已經自動配置了Spring Security,我們在application.yml添加一個內存中的用戶:
~~~
security:
user:
name: admin
password: admin
~~~
測試登錄點沿用上一篇文章的端點,訪問http://localhost:8080/test/cookie?browser=chrome端點后會出現http basic的認證框,我們輸入admin/admin,即可獲得結果,也遇到了第一個坑點,我們會發現每次請求,sessionId都會被刷新,這顯然不是我們想要的結果。

這個現象筆者研究了不少源碼,但并沒有得到非常滿意的解釋,只能理解為SecurityAutoConfiguration提供的默認配置,沒有觸發到響應的配置,導致了session的不斷刷新(如果讀者有合理的解釋可以和我溝通)。Spring Session之所以能夠替換默認的tomcat httpSession是因為配置了springSessionRepositoryFilter這個過濾器,且提供了非常高的優先級,這歸功于AbstractSecurityWebApplicationInitializer ,AbstractHttpSessionApplicationInitializer 這兩個初始化器,當然,也保證了Spring Session會在Spring Security之前起作用。
而解決上述的詭異現象也比較容易(但原理不清),我們使用@EnableWebSecurity對Spring Security進行一些配置,即可解決這個問題。
~~~
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()//<1>
.and()
.logout().permitAll();
}
// @formatter:on
// @formatter:off
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin").roles("USER");//<2>
}
// @formatter:on
}
~~~
<1> 不想大費周章寫一個登錄頁面,于是開啟了http basic認證
<2> 配置了security config之后,springboot的autoConfig就會失效,于是需要手動配置用戶。(需要注掉yml中配置)
再次請求,可以發現SessionId返回正常,@EnableWebSecurity似乎觸發了相關的配置,當然了,我們在使用Spring Security時不可能使用autoconfig,但是這個現象的確是一個疑點。
## 使用自定義CookieSerializer
~~~
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}
~~~
### 根據用戶名查找用戶歸屬的SESSION
這個特性聽起來非常有意思,你可以在一些有趣的場景下使用它,如知道用戶名后即可刪除其SESSION。一直以來我們都是通過線程綁定的方式,讓用戶操作自己的SESSION,包括獲取用戶名等操作。但如今它提供了一個反向的操作,根據用戶名獲取SESSION,恰巧,在一些項目中真的可以使用到這個特性,最起碼,當別人問起你,或者討論到和SESSION相關的知識時,你可以明晰一點,這是可以做到的。
我們使用Redis作為Session Store還有一個好處,就是其實現了FindByIndexNameSessionRepository接口,下面讓我們來見證這一點。
~~~
@Controller
public class CookieController {
@Autowired
FindByIndexNameSessionRepository<? extends ExpiringSession> sessionRepository;
@RequestMapping("/test/findByUsername")
@ResponseBody
public Map findByUsername(@RequestParam String username) {
Map<String, ? extends ExpiringSession> usersSessions = sessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
return usersSessions;
}
}
~~~
由于一個用戶可能擁有多個Session,所以返回的是一個Map信息,而這里的username,則就是與Spring Security集成之后的用戶名,最令人感動Spring厲害的地方,是這一切都是自動配置好的。我們在內存中配置的用戶的username是admin,于是我們訪問這個端點,可以看到如下的結果

連同我們存入session中的browser=chrome,browser=360都可以看見(只有鍵名)。
## 總結
Spring Session對各種場景下的Session管理提供一套非常完善的實現。文中所介紹的,僅僅是Spring Session常用的一些特性,更多的知識點可以在spring.io的文檔中一覽無余。
- java
- 設計模式
- 設計模式總覽
- 設計原則
- 工廠方法模式
- 抽象工廠模式
- 單例模式
- 建造者模式
- 原型模式
- 適配器模式
- 裝飾者模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
- 策略模式
- 模板方法模式
- 觀察者模式
- 迭代子模式
- 責任鏈模式
- 命令模式
- 備忘錄模式
- 狀態模式
- 訪問者模式
- 中介者模式
- 解釋器模式
- 附錄
- JVM相關
- JVM內存結構
- Java虛擬機的內存組成以及堆內存介紹
- Java堆和棧
- 附錄-數據結構的堆棧和內存分配的堆區棧區的區別
- Java內存之Java 堆
- Java內存之虛擬機和內存區域概述
- Java 內存之方法區和運行時常量池
- Java 內存之直接內存(堆外內存)
- JAVA內存模型
- Java內存模型介紹
- 內存模型如何解決緩存一致性問題
- 深入理解Java內存模型——基礎
- 深入理解Java內存模型——重排序
- 深入理解Java內存模型——順序一致性
- 深入理解Java內存模型——volatile
- 深入理解Java內存模型——鎖
- 深入理解Java內存模型——final
- 深入理解Java內存模型——總結
- 內存可見性
- JAVA對象模型
- JVM內存結構 VS Java內存模型 VS Java對象模型
- Java的對象模型
- Java的對象頭
- HotSpot虛擬機
- HotSpot虛擬機對象探秘
- 深入分析Java的編譯原理
- Java虛擬機的鎖優化技術
- 對象和數組并不是都在堆上分配內存的
- 垃圾回收
- JVM內存管理及垃圾回收
- JVM 垃圾回收器工作原理及使用實例介紹
- JVM內存回收理論與實現(對象存活的判定)
- JVM參數及調優
- CMS GC日志分析
- JVM實用參數(一)JVM類型以及編譯器模式
- JVM實用參數(二)參數分類和即時(JIT)編譯器診斷
- JVM實用參數(三)打印所有XX參數及值
- JVM實用參數(四)內存調優
- JVM實用參數(五)新生代垃圾回收
- JVM實用參數(六) 吞吐量收集器
- JVM實用參數(七)CMS收集器
- JVM實用參數(八)GC日志
- Java性能調優原則
- JVM 優化經驗總結
- 面試題整理
- 面試題1
- java日志規約
- Spring安全
- OAtuth2.0簡介
- Spring Session 簡介(一)
- Spring Session 簡介(二)
- Spring Session 簡介(三)
- Spring Security 簡介(一)
- Spring Security 簡介(二)
- Spring Security 簡介(三)
- Spring Security 簡介(四)
- Spring Security 簡介(五)
- Spring Security Oauth2 (一)
- Spring Security Oauth2 (二)
- Spring Security Oauth2 (三)
- SpringBoot
- Shiro
- Shiro和Spring Security對比
- Shiro簡介
- Session、Cookie和Cache
- Web Socket
- Spring WebFlux