Spring Security 實戰乾貨:實現自定義退出登陸

logout.png

1. 前言

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

2. 咱們使用 Spring Security 登陸後都作了什麼

這個問題咱們必須搞清楚!通常登陸後,服務端會給用戶發一個憑證。常見有如下的兩種:前端

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

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

2. 退出登陸須要咱們作什麼

  1. 當前的用戶登陸狀態失效。這就須要咱們清除服務端的用戶狀態。
  2. 退出登陸接口並非 permitAll, 只有攜帶對應用戶的憑證才退出。
  3. 將退出結果返回給請求方。
  4. 退出登陸後用戶能夠經過從新登陸來認證該用戶。

3. Spring Security 中的退出登陸

接下來咱們來分析並實戰 如何定製退出登陸邏輯。首先咱們要了解 LogoutFilter編程

3.1 LogoutFilter

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

  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 提供了一些直接配置來知足你的須要。以下:cookie

  • 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

相關文章
相關標籤/搜索