Spring Security 實戰干貨:實現自定義退出登錄


logout.png

1. 前言

上一篇對 Spring Security 所有內置的 Filter 進行了介紹。今天我們來實戰如何安全退出應用程序。

2. 我們使用 Spring Security 登錄后都做了什么

這個問題我們必須搞清楚!一般登錄后,服務端會給用戶發一個憑證。常見有以下的兩種:

  • 基於 Session 客戶端會存 cookie 來保存一個 sessionId ,服務端存一個 Session

  • 基於 token 客戶端存一個 token 串,服務端會在緩存中存一個用來校驗此 token 的信息。

2. 退出登錄需要我們做什么

  1. 當前的用戶登錄狀態失效。這就需要我們清除服務端的用戶狀態。
  2. 退出登錄接口並不是 permitAll, 只有攜帶對應用戶的憑證才退出。
  3. 將退出結果返回給請求方。
  4. 退出登錄后用戶可以通過重新登錄來認證該用戶。

3. Spring Security 中的退出登錄

接下來我們來分析並實戰 如何定制退出登錄邏輯。首先我們要了解 LogoutFilter

3.1 LogoutFilter

通過 Spring Security 實戰干貨:內置 Filter 全解析 我們知道退出登錄邏輯是由過濾器 LogoutFilter 來執行的。 它持有三個接口類型的屬性:

  1. RequestMatcher logoutRequestMatcher 這個用來攔截退出請求的 URL
  2. LogoutHandler handler 用來處理退出的具體邏輯
  3. LogoutSuccessHandler logoutSuccessHandler 退出成功后執行的邏輯

我們通過對以上三個接口的實現就能實現我們自定義的退出邏輯。

3.2 LogoutConfigurer

我們一般不會直接操作 LogoutFilter ,而是通過 LogoutConfigurer 來配置 LogoutFilter。 你可以通過 HttpSecurity#logout() 方法來初始化一個 LogoutConfigurer 。 接下來我們來實戰操作一下。

3.2.1 實現自定義退出登錄請求URL

LogoutConfigurer 提供了 logoutRequestMatcher(RequestMatcher logoutRequestMatcher)logoutUrl(Sring logoutUrl) 兩種方式來定義退出登錄請求的 URL 。它們作用是相同的,你選擇其中一種方式即可。

3.2.2 處理具體的邏輯

默認情況下 Spring Security 是基於 Session 的。LogoutConfigurer 提供了一些直接配置來滿足你的需要。如下:

  • clearAuthentication(boolean clearAuthentication) 是否在退出時清除當前用戶的認證信息
  • deleteCookies(String... cookieNamesToClear) 刪除指定的 cookies
  • invalidateHttpSession(boolean invalidateHttpSession) 是否移除 HttpSession

如果上面滿足不了你的需要就需要你來定制 LogoutHandler 了。

3.2.3 退出成功邏輯

  • logoutSuccessUrl(String logoutSuccessUrl) 退出成功后會被重定向到此 URL你可以寫一個Controller 來完成最終返回,但是需要支持 GET 請求和 匿名訪問 。 通過 setDefaultTargetUrl 方法注入到 LogoutSuccessHandler
  • defaultLogoutSuccessHandlerFor(LogoutSuccessHandler handler, RequestMatcher preferredMatcher) 用來構造默認的 LogoutSuccessHandler 我們可以通過添加多個來實現從不同 URL 退出執行不同的邏輯。
  • LogoutSuccessHandler logoutSuccessHandler 退出成功后執行的邏輯的抽象根本接口。

3.3 Spring Security 退出登錄實戰

現在前后端分離比較多,退出后返回json。 而且只有用戶在線才能退出登錄。否則不能進行退出操作。我們采用實現 LogoutHandlerLogoutSuccessHandler 接口這種編程的方式來配置 。退出請求的 url 依然通過 LogoutConfigurer#logoutUrl(String logoutUrl)來定義。

3.3.1 自定義 LogoutHandler

默認情況下清除認證信息 (invalidateHttpSession),和Session 失效(invalidateHttpSession) 已經由內置的SecurityContextLogoutHandler 來完成。我們自定義的 LogoutHandler 會在SecurityContextLogoutHandler 來執行。

 @Slf4j
 public class CustomLogoutHandler implements LogoutHandler {
     @Override
     public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
         User user = (User) authentication.getPrincipal();
         String username = user.getUsername();
         log.info("username: {} is offline now", username);
     }
 }

以上是我們實現的 LogoutHandler 。 我們可以從 logout 方法的 authentication 變量中 獲取當前用戶信息。你可以通過這個來實現你具體想要的業務。比如記錄用戶下線退出時間、IP 等等。

3.3.2 自定義 LogoutSuccessHandler

如果我們實現了自定義的 LogoutSuccessHandler 就不必要設置 LogoutConfigurer#logoutSuccessUrl(String logoutSuccessUrl) 了。該處理器處理后會響應給前端。你可以轉發到其它控制器。重定向到登錄頁面,也可以自行實現其它 MediaType ,可以是 json 或者頁面

  @Slf4j
  public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
      @Override
      public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
          User user = (User) authentication.getPrincipal();
          String username = user.getUsername();
          log.info("username: {} is offline now", username);
  
  
          responseJsonWriter(response, RestBody.ok("退出成功"));
      }
  
      private static void responseJsonWriter(HttpServletResponse response, Rest rest) throws IOException {
          response.setStatus(HttpServletResponse.SC_OK);
          response.setCharacterEncoding("utf-8");
          response.setContentType(MediaType.APPLICATION_JSON_VALUE);
          ObjectMapper objectMapper = new ObjectMapper();
          String resBody = objectMapper.writeValueAsString(rest);
          PrintWriter printWriter = response.getWriter();
          printWriter.print(resBody);
          printWriter.flush();
          printWriter.close();
      }
  }

3.3.4 自定義退出的 Spring Security 配置

為了方便調試我 注釋掉了我們 實現的自定義登錄,你可以通過 http:localhost:8080/login 來登錄,然后通過 http:localhost:8080/logout 測試退出。

       @Override
          protected void configure(HttpSecurity http) throws Exception {
              http.csrf().disable()
                      .cors()
                      .and()
                      .authorizeRequests().anyRequest().authenticated()
                      .and()
  // .addFilterBefore(preLoginFilter, UsernamePasswordAuthenticationFilter.class)
                      // 登錄
                      .formLogin().loginProcessingUrl(LOGIN_PROCESSING_URL).successForwardUrl("/login/success").failureForwardUrl("/login/failure")
                      .and().logout().addLogoutHandler(new CustomLogoutHandler()).logoutSuccessHandler(new CustomLogoutSuccessHandler());
  
          }

4. 總結

本篇 我們實現了 在 Spring Security 下的自定義退出邏輯。相對比較簡單,你可以根據你的業務需要來實現你的退出邏輯。有什么疑問可以通過 關注公眾號:Felordcn 來私信提問 。相關DEMO代碼也可以通過關注后回復 ss04 獲取。

關注公眾號:Felordcn獲取更多資訊

個人博客:https://felord.cn


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM