處理登出

在終端使用者可以登入的應用程式中,他們也應該能夠登出。

預設情況下,Spring Security 會啟動一個 /logout 端點,因此無需額外的程式碼。

本節的其餘部分涵蓋了一些你需要考慮的用例

理解登出的架構

當你包含 spring-boot-starter-security 依賴或使用 @EnableWebSecurity 註解時,Spring Security 將新增其登出支援,並且預設情況下會響應 GET /logoutPOST /logout

如果你請求 GET /logout,那麼 Spring Security 將顯示一個登出確認頁面。除了為使用者提供有價值的雙重檢查機制外,它還提供了一種簡單的方式來為 POST /logout 提供所需的 CSRF 令牌

請注意,如果配置中停用了CSRF 保護,則不會向用戶顯示登出確認頁面,並且將直接執行登出。

在你的應用程式中,無需使用 GET /logout 來執行登出。只要請求中存在所需的 CSRF 令牌,你的應用程式只需 POST /logout 即可引發登出。

如果你請求 POST /logout,那麼它將使用一系列LogoutHandler 例項執行以下預設操作

完成後,它將執行其預設的LogoutSuccessHandler,該處理程式將重定向到 /login?logout

自定義登出 URI

由於 LogoutFilter 出現在過濾器鏈中的AuthorizationFilter之前,因此預設情況下無需明確允許 /logout 端點。因此,只有你自行建立的自定義登出端點通常需要 permitAll 配置才能訪問。

例如,如果你只想更改 Spring Security 匹配的 URI,可以透過以下方式在 logout DSL 中進行更改

自定義登出 URI
  • Java

  • Kotlin

  • Xml

http
    .logout((logout) -> logout.logoutUrl("/my/logout/uri"))
http {
    logout {
        logoutUrl = "/my/logout/uri"
    }
}
<logout logout-url="/my/logout/uri"/>

並且不需要進行任何授權更改,因為它只是調整了 LogoutFilter

但是,如果你設定了自己的登出成功端點(或者在極少數情況下,你自己的登出端點),例如使用Spring MVC,你將需要在 Spring Security 中允許它。這是因為 Spring MVC 在 Spring Security 處理請求之後才處理你的請求。

你可以使用 authorizeHttpRequests<intercept-url> 這樣做

自定義登出端點
  • Java

  • Kotlin

  • Xml

http
    .authorizeHttpRequests((authorize) -> authorize
        .requestMatchers("/my/success/endpoint").permitAll()
        // ...
    )
    .logout((logout) -> logout.logoutSuccessUrl("/my/success/endpoint"))
http {
    authorizeHttpRequests {
        authorize("/my/success/endpoint", permitAll)
    }
    logout {
        logoutSuccessUrl = "/my/success/endpoint"
    }
}
<http>
    <filter-url pattern="/my/success/endpoint" access="permitAll"/>
    <logout logout-success-url="/my/success/endpoint"/>
</http>

在此示例中,你告訴 LogoutFilter 在完成後重定向到 /my/success/endpoint。並且,你在AuthorizationFilter中明確允許 /my/success/endpoint 端點。

但是,指定兩次可能會很麻煩。如果你正在使用 Java 配置,可以改為在登出 DSL 中設定 permitAll 屬性,如下所示

允許自定義登出端點
  • Java

  • Kotlin

http
    .authorizeHttpRequests((authorize) -> authorize
        // ...
    )
    .logout((logout) -> logout
        .logoutSuccessUrl("/my/success/endpoint")
        .permitAll()
    )
http
    authorizeHttpRequests {
        // ...
    }
    logout {
        logoutSuccessUrl = "/my/success/endpoint"
        permitAll = true
    }

這將為你將所有登出 URI 新增到允許列表中。

新增清理操作

如果你正在使用 Java 配置,可以透過在 logout DSL 中呼叫 addLogoutHandler 方法來新增自己的清理操作,如下所示

自定義登出處理器
  • Java

  • Kotlin

CookieClearingLogoutHandler cookies = new CookieClearingLogoutHandler("our-custom-cookie");
http
    .logout((logout) -> logout.addLogoutHandler(cookies))
http {
    logout {
        addLogoutHandler(CookieClearingLogoutHandler("our-custom-cookie"))
    }
}
由於LogoutHandler例項用於清理,因此它們不應丟擲異常。
由於LogoutHandler是一個函式式介面,你可以將其作為 lambda 提供。

一些登出處理程式配置非常常見,它們直接在 logout DSL 和 <logout> 元素中公開。一個例子是配置會話失效,另一個是應該刪除哪些附加 cookie。

例如,你可以配置上面看到的CookieClearingLogoutHandler

或者你可以改為設定相應的配置值,如下所示

  • Java

  • Kotlin

  • Xml

http
    .logout((logout) -> logout.deleteCookies("our-custom-cookie"))
http {
    logout {
        deleteCookies("our-custom-cookie")
    }
}
<http>
    <logout delete-cookies="our-custom-cookie"/>
</http>
指定 JSESSIONID cookie 不是必需的,因為SecurityContextLogoutHandler透過使會話失效來刪除它。

使用 Clear-Site-Data 登出使用者

Clear-Site-Data HTTP 標頭是瀏覽器支援的一種指令,用於清除屬於所屬網站的 cookie、儲存和快取。這是一種方便且安全的方法,可以確保在登出時清除所有內容,包括會話 cookie。

你可以配置 Spring Security 以便在登出時寫入 Clear-Site-Data 標頭,如下所示

使用 Clear-Site-Data
  • Java

  • Kotlin

HeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(Directives.ALL));
http
    .logout((logout) -> logout.addLogoutHandler(clearSiteData))
val clearSiteData = HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(Directives.ALL))
http {
    logout {
        addLogoutHandler(clearSiteData)
    }
}

你為 ClearSiteDataHeaderWriter 建構函式提供了要清除的專案列表。

上述配置清除了所有站點資料,但你也可以將其配置為僅清除 cookie,如下所示

使用 Clear-Site-Data 清除 Cookie
  • Java

  • Kotlin

HeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(Directive.COOKIES));
http
    .logout((logout) -> logout.addLogoutHandler(clearSiteData))
val clearSiteData = HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(Directive.COOKIES))
http {
    logout {
        addLogoutHandler(clearSiteData)
    }
}

自定義登出成功

雖然在大多數情況下 logoutSuccessUrl 就足夠了,但你可能需要在登出完成後執行與重定向到 URL 不同的操作。LogoutSuccessHandler 是 Spring Security 中用於自定義登出成功操作的元件。

例如,與其重定向,你可能只想返回一個狀態碼。在這種情況下,你可以提供一個成功處理程式例項,如下所示

自定義登出成功以返回 HTTP 狀態碼
  • Java

  • Kotlin

  • Xml

http
    .logout((logout) -> logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()))
http {
    logout {
        logoutSuccessHandler = HttpStatusReturningLogoutSuccessHandler()
    }
}
<bean name="mySuccessHandlerBean" class="org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler"/>
<http>
    <logout success-handler-ref="mySuccessHandlerBean"/>
</http>
由於LogoutSuccessHandler是一個函式式介面,你可以將其作為 lambda 提供。

建立自定義登出端點

強烈建議你使用提供的 logout DSL 來配置登出。原因之一是很容易忘記呼叫所需的 Spring Security 元件以確保正確和完整的登出。

事實上,註冊自定義 LogoutHandler 通常比為執行登出建立Spring MVC端點更簡單。

也就是說,如果你發現自己需要自定義登出端點,如下所示

自定義登出端點
  • Java

  • Kotlin

@PostMapping("/my/logout")
public String performLogout() {
    // .. perform logout
    return "redirect:/home";
}
@PostMapping("/my/logout")
fun performLogout(): String {
    // .. perform logout
    return "redirect:/home"
}

那麼你將需要讓該端點呼叫 Spring Security 的SecurityContextLogoutHandler,以確保安全且完整的登出。至少需要以下內容

自定義登出端點
  • Java

  • Kotlin

SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();

@PostMapping("/my/logout")
public String performLogout(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
    // .. perform logout
    this.logoutHandler.logout(request, response, authentication);
    return "redirect:/home";
}
val logoutHandler = SecurityContextLogoutHandler()

@PostMapping("/my/logout")
fun performLogout(val authentication: Authentication, val request: HttpServletRequest, val response: HttpServletResponse): String {
    // .. perform logout
    this.logoutHandler.logout(request, response, authentication)
    return "redirect:/home"
}

此外,你需要明確允許該端點

未能呼叫SecurityContextLogoutHandler意味著SecurityContext可能仍可在後續請求中可用,這意味著使用者實際上並未登出。

測試登出

配置登出後,你可以使用Spring Security 的 MockMvc 支援對其進行測試。

更多登出相關參考資料

© . This site is unofficial and not affiliated with VMware.