SpringSecurity 下 Session 使用

在之前的幾章裏面,我們分別做了快速入門、自定義表單登錄、自定義手機登錄。他們有一個共同點,就是目前我們與客戶端之間的交互都依賴於 Session。那麼本章我們就帶大家來了解一下 SpringSecurity 下 Session 的使用與設置。

Session 是什麼?

Session 中文意思爲會議,在計算機中,尤其在網絡應用中稱爲會話控制

Session直接翻譯成中文比較困難,一般都譯成時域。在計算機專業術語中,Session是指一個終端用戶與交互系統進行通信的時間間隔,通常指從註冊進入系統到註銷退出系統之間所經過的時間。以及如果需要的話,可能還有一定的操作空間。

百度百科

爲什麼要使用 Session?

Http 協議本身是無狀態的,多次連接之間的狀態無法共享,服務端就判斷是否是同一個用戶操作的就比較麻煩。

而 Session 就是用來保存狀態的,允許通過將對象存儲在 Web 服務器內存中,在整個用戶會話過程中進行共享。

Session 原理

瀏覽器第一次訪問服務器時,服務端會生成 Session,然後同時爲該 Session 生成一個唯一的 SessionId,然後將 SessionId 與 Session 以鍵值對的形式存儲到內存中(Java 默認)。將 SessionId 以 Cookie 的方式返回給瀏覽器。瀏覽器下一次訪問的時候就攜帶 SessionId 來訪問,這樣服務端通過查詢就可以取出 Session 達到共享了。

在 SpringSecurity 裏面的使用

單機 Session

  • Session 超時設置
    • 設置 Session 超時時間,在系統配置文件(application.properties/yml) 中配置

      server:
        session:
          timeout: 600 #單位爲 Second,設置的時間低於 1 分鐘,按 1 分鐘處理
      
    • 設置 Session 超時/失效後繼續訪問處理,可以設置跳轉頁面,也可以設置跳轉接口。前後端分離一般返回 Json,不分離可以引導頁面跳轉。兩者都要兼容,可以跳轉到自定義接口判斷實際請求,進行處理。

      // 設置 Session 失效後訪問跳轉的頁面
      http.sessionManagement().invalidSessionUrl("/session/invalid");
      
  • Session 併發控制
    • Session 最大數設置,限制系統中用戶數量

      http.sessionManagement().maximumSessions(1)// 設置同時在線的最大 Session 數
      
    • 設置 Session 數量達到最大值時,是否阻止後面用戶登錄

      // 設置當 Session 數量達到最大數量時,阻止後面的登錄
      http.sessionManagement().maxSessionsPreventsLogin(true);
      
    • 自定義設置 Session 數量達到最大時後續登錄的處理策略,默認實現會將前面用戶的 Session 置爲失效

      // 設置 Session 併發登錄時處理策略
      http.sessionManagement().expiredSessionStrategy(new DefaultExpiredSessionStrategy());
      
      /**
       * @author: hblolj
       * @Date: 2019/3/16 9:13
       * @Description: Session 併發登錄時的處理策略
       * @Version:
       **/
      public class DefaultExpiredSessionStrategy implements SessionInformationExpiredStrategy{
      
          @Override
          public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
              event.getResponse().setCharacterEncoding("UTF-8");
              event.getResponse().getWriter().write("併發登錄!");
          }
      }
      

集羣 Session(單機 Session 下的設置在集羣下一樣有效)

在單機 Session 下基本上只要處理好上面的問題就可以了,但是隨着系統越來越大,單體應用可能滿足不了我們的需求。如果我們對應用進行了集羣,那麼用戶在每個應用上都會產生一個 Session,並且這些 Session 還不是共享的,就達不到 Session 使用的初衷了。

爲了讓 Session 可以共享,一般我們就會改變 Session 的存儲方式,Session 默認存儲在內存中,多個主機間就不方便共享。主流處理方案有:

  • 保存在數據庫
  • 保存在 Redis 中
  • 利用 Servlet 容器來進行 Session 同步
  • 或者使用 Nginx 使用用戶 ip 進行哈希處理,在負載均衡時,讓用戶每次訪問同一個主機避免多 Session 問題

我們這裏主要還是討論 SpringSecurity 下集羣 Session 的處理方案,其他擴展信息,大家感興趣可以自行查詢,或者留言討論。

我們這裏示例使用 Redis 作爲 Session 存儲方式,通過下列配置 Session 就會默認存儲在 Redis 中了。關於 SpringBoot 下 Redis 的使用,不瞭解的可以查詢資料使用,上手還是比較簡單的。

  • 使用 Redis 作爲 Session 的存儲,通過配置文件指定存儲方式。

    spring:
      session:
        store-type: redis #設置將 Session 存儲在 Redis 中
      redis:
        host: redis 所在的主機
        port: 默認是 6379
        database: 選擇使用的 database
        password: your self password
        pool:
          max-wait: -1
          max-idle: 1
          max-active: 8
          min-idle: 0
    
  • 添加 Redis 依賴

    <!-- SpringBoot 版本 1.5.19.RELEASE-->
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
  • 添加 Spring-Session 依賴

    <!-- SpringBoot 版本 1.5.19.RELEASE-->
    <dependency>
       <groupId>org.springframework.session</groupId>
       <artifactId>spring-session</artifactId>
    </dependency>
    
  • 除了 Redis,SpringBoot 還支持一下存儲方式,大家可以自行擴展使用

    public enum StoreType {
        REDIS,
        MONGO,
        JDBC,
        HAZELCAST,
        HASH_MAP,
        NONE;
    
        private StoreType() {
        }
    }
    

退出登錄

既然 Session 中共享用戶多次請求的狀態,那麼如果我們要用戶登出,就只要清空 Session 即可。那麼在 SpringSecurity 下是怎樣設置的呢?

  • 如何退出

    • 請求登出地址,默認爲 /logout

    • 自定義登出地址

      http.logout().logoutUrl("/quit");// 默認是 logout
      
  • 退出處理原理 (訪問退出地址,退出處理邏輯 SpringSecurity 有默認實現)

    • 使當前 Session 失效

      (HttpSession)session.invalidate();
      
    • 清除與當前用戶先關的 remember-me 記錄

      if (authentication != null) {
          this.tokenRepository.removeUserTokens(authentication.getName());
      }
      
    • 清空當前的 SecurityContext

      if (this.clearAuthentication) {
          SecurityContext context = SecurityContextHolder.getContext();
          context.setAuthentication((Authentication)null);
      }
      
      SecurityContextHolder.clearContext();
      
  • 與退出相關的配置

    • 自定義退出成功跳轉頁面或處理策略,同時設置只有 logoutSuccessHandler 會生效

      http
      	.logout()
          .logoutSuccessUrl("http://www.baidu.com") // 設置退出成功跳轉的頁面
          .logoutSuccessHandler(new DefaultLogoutSuccessHandler()) // 設置退出成功的處理策略
      
      /**
       * @author: hblolj
       * @Date: 2019/3/16 10:28
       * @Description:
       * @Version:
       **/
      @Slf4j
      public class DefaultLogoutSuccessHandler implements LogoutSuccessHandler{
      
          @Override
          public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
      
              log.info("Logout Success!");
          }
      }
      
    • 設置退出時刪除客戶端 Cookie

      http.logout().deleteCookies("JSESSIONID") // 退出時刪除客戶端指定的 Cookie
      

OK,到了這裏,基本上在 SpringSecurity 裏面 Session 的常規設置與使用都介紹完了。大家如果有什麼問題,歡迎在下面的留言區留言討論。

實際上,在現在的開發中,單體 Web 應用可能還是以 Session 爲主,但是如果要同時支持 App 的話, Session 就不是那麼好用了,因爲 Session 依賴 Cookie,雖然 App 訪問時可以模擬瀏覽器設置 Cookie,但是契合度還是不如 Web,所以對於 App 或者集羣環境下,現在主流使用基於 OAuth2 的 Token 作爲驗證。是不是以爲我們下一章要講 SpringSecurity 下 OAuth2 協議的集成與 Token 使用呢?哈哈,下一章我們計劃帶大家集成一下 SpringSecurity 下第三方登錄,比如 QQ 快速登錄、微信快速登錄等(也依賴於 OAuth 協議)。至於集成 Token 認證還要往後幾章哦!

To Be Continue!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章