1. 程式人生 > 其它 >Spring Security(四) Spring Security Session管理

Spring Security(四) Spring Security Session管理

技術標籤:SpringBoot & SpringCloud 學習實戰spring bootSpring Security

Spring Security Session管理

目錄

Spring Security Session管理

Session超時設定

Session併發控制

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管理。