Spring Security(四) Spring Security Session管理
技術標籤:SpringBoot & SpringCloud 學習實戰spring bootSpring Security
Spring Security Session管理
目錄
Session超時設定
Session超時時間也就是使用者登入的有效時間。要設定Session超時時間很簡單,只需要在配置檔案中新增,單位為秒,通過上面的配置,Session的有效期為一個小時。
server:
session:
timeout: 3600
Session的最小有效期為60秒,也就是說即使你設定為小於60秒的值,其有效期還是為60秒
Session失效後,重新整理頁面後將跳轉到認證頁面,我們可以再新增一些配置,自定義Session失效後的一些行為。
在Spring Security中配置Session管理器,並配置Session失效後要跳轉的URL:上面配置了Session失效後跳轉到/session/invalid
,並且將這個URL新增到了免認證路徑中。
@Override protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 新增驗證碼校驗過濾器 .addFilterBefore(smsCodeFilter,UsernamePasswordAuthenticationFilter.class) // 新增簡訊驗證碼校驗過濾器 .formLogin() // 表單登入 .loginPage("/authentication/require") // 登入跳轉 URL .loginProcessingUrl("/login") // 處理表單登入 URL .successHandler(authenticationSucessHandler) // 處理登入成功 .failureHandler(authenticationFailureHandler) // 處理登入失敗 .and() .authorizeRequests() // 授權配置 .antMatchers("/authentication/require", "/login.html", "/code/image","/code/sms","/session/invalid").permitAll() // 無需認證的請求路徑 .anyRequest() // 所有請求 .authenticated() // 都需要認證 .and() .sessionManagement() // 新增 Session管理器 .invalidSessionUrl("/session/invalid") // Session失效後跳轉到這個連結 ...... }
在Controller裡新增一個方法,對映該請求:
@GetMapping("/session/invalid")
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public String sessionInvalid(){
return "session已失效,請重新認證";
}
為了演示,我們將Session的超時時間設定為最小值60秒,重啟專案,認證後等待60秒並重新整理頁面:
Session併發控制
Session併發控制可以控制一個賬號同一時刻最多能登入多少個。我們在Spring Security配置中繼續新增Session相關配置:
@Autowired
private MySessionExpiredStrategy sessionExpiredStrategy;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 新增驗證碼校驗過濾器
.addFilterBefore(smsCodeFilter,UsernamePasswordAuthenticationFilter.class) // 新增簡訊驗證碼校驗過濾器
.formLogin() // 表單登入
.loginPage("/authentication/require") // 登入跳轉 URL
.loginProcessingUrl("/login") // 處理表單登入 URL
.successHandler(authenticationSucessHandler) // 處理登入成功
.failureHandler(authenticationFailureHandler) // 處理登入失敗
.and()
.authorizeRequests() // 授權配置
.antMatchers("/authentication/require",
"/login.html", "/code/image","/code/sms","/session/invalid").permitAll() // 無需認證的請求路徑
.anyRequest() // 所有請求
.authenticated() // 都需要認證
.and()
.sessionManagement() // 新增 Session管理器
.invalidSessionUrl("/session/invalid") // Session失效後跳轉到這個連結
.maximumSessions(1)
.expiredSessionStrategy(sessionExpiredStrategy)
.and()
......
maximumSessions
配置了最大Session併發數量為1個,如果mrbird這個賬戶登入後,在另一個客戶端也使用mrbird賬戶登入,那麼第一個使用mrbird登入的賬戶將會失效,類似於一個先入先出佇列。expiredSessionStrategy
配置了Session在併發下失效後的處理策略,這裡為我們自定義的策略MySessionExpiredStrategy
。
MySessionExpiredStrategy
實現SessionInformationExpiredStrategy
:
@Component
public class MySessionExpiredStrategy implements SessionInformationExpiredStrategy {
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
HttpServletResponse response = event.getResponse();
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("您的賬號已經在別的地方登入,當前登入已失效。如果密碼遭到洩露,請立即修改密碼!");
}
}
除了後者將前者踢出的策略,我們也可以控制當Session達到最大有效數的時候,不再允許相同的賬戶登入。
要實現這個功能只需要在上面的配置中新增:
......
.and()
.sessionManagement() // 新增 Session管理器
.invalidSessionUrl("/session/invalid") // Session失效後跳轉到這個連結
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.expiredSessionStrategy(sessionExpiredStrategy)
.and()
......
重啟系統,在chrome上登入mrbird賬戶後,在firefox上嘗試使用mrbird賬戶登入:可以看到登入受限。
Session叢集處理
Session叢集聽著高大上,其實實現起來很簡單。當我們登入成功後,使用者認證的資訊儲存在Session中,而這些Session預設是儲存在執行運用的伺服器上的,比如Tomcat,netty等。當應用叢集部署的時候,使用者在A應用上登入認證了,後續通過負載均衡可能會把請求傳送到B應用,而B應用伺服器上並沒有與該請求匹配的認證Session資訊,所以使用者就需要重新進行認證。要解決這個問題,我們可以把Session資訊儲存在第三方容器裡(如Redis叢集),而不是各自的伺服器,這樣應用叢集就可以通過第三方容器來共享Session了。
我們引入Redis和Spring Session依賴:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然後在yml中配置Session儲存方式為Redis:
spring:
session:
store-type: redis
開啟Redis,並且啟動兩個應用例項,一個埠為8080,另一個埠為9090。
我們現在8080埠應用上登入:
然後訪問9090埠應用的主頁:
可以看到登入也是生效的。這就實現了叢集化Session管理。