領先一步
VMware 提供培訓和認證,助力您的進步。
瞭解更多Spring Security 6.4.1 是您處理認證和授權項的一站式商店,而且這個版本真是太棒了(doozie)!釋出說明 充滿可能性!
釋出說明是個謊言!
我的意思是,它們不是謊言。它們只是沒有很好地捕捉和傳達這個版本有多麼出色。與許多以前的版本相比,這個版本有更多面向用戶的“玩具”(功能)。這可能是自 Spring Security 至少長出 Java 配置 DSL 以來我最喜歡的版本!
看看那些釋出說明。看到那些關於 Passkeys 和 一次性令牌登入 (One-Time Token Login) 的微小章節了嗎?是的。那就是謊言。這些內容值得擁有自己的章節!我們會回到它們。我保證。讓我們快速看看其他內容。有太多不同的內容了。
@AuthorizeReturnObject
特性,並且可以在 @PreAuthorize
和 @PostAuthorize
生命週期回撥中引用 bean。SecurityAnnotationScanners
API 提供了一種掃描安全註解的方式,以便為自定義註解新增 Spring Security 的選擇和模板特性。oauth2Login()
方法現在接受 OAurth2AuthorizationRequestResolver
作為 @Bean
。ClientRegistrations
現在支援外部獲取的配置。login page()
logout+jwt
的退出令牌。RestClient
現在可以透過配置 OAuth2ClientHttpRequestInterceptor
來進行受保護資源請求。您可以在發出 HTTP 請求時,讓它將您的令牌提供給下游服務。registrationId
得到了簡化。application/samlmetadata+xml
MIME 型別。記住我 (Remember Me)
cookie。ServerWebExchangeFirewall
物件作為 bean 載入。AclAuthorizationStrategyImpl
支援 RoleHierarchy
型別,這個型別也相當新!現在,讓我們回到 Passkeys 和一次性令牌的討論。
讓我們看一個簡單的應用。
我們會有一個想要鎖定(保護)的 HTTP 控制器。
package com.example.bootiful_34.security;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.security.Principal;
import java.util.Map;
@Controller
@ResponseBody
class SecuredController {
@GetMapping("/admin")
Map<String, String> admin(Principal principal) {
return Map.of("admin", principal.getName());
}
@GetMapping("/")
Map<String, String> hello(Principal principal) {
return Map.of("user", principal.getName());
}
}
要鎖定它,我們需要定義一個包含一些常見元素的 SecurityFilterChain
:HTTP 表單登入、一些授權規則等。
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.authorizeHttpRequests(requests -> requests
.requestMatchers("/admin").hasRole("ADMIN")
.requestMatchers("/error").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
// ...
.build();
}
我們需要讓 Spring Security 瞭解我們系統中的使用者,所以我們提供一個 UserDetailsService
。
package com.example.bootiful_34.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@ImportRuntimeHints(UiResourcesRuntimeHintsRegistrar.class)
class SecurityConfiguration {
@Bean
UserDetailsService userDetailsService() {
var josh = User.withUsername("josh").password("pw").roles("USER").build();
var rob = User.withUsername("rob").password("pw").roles("USER", "ADMIN").build();
return new InMemoryUserDetailsManager(josh, rob);
}
// ...
}
這個應用有兩個使用者:josh
和 rob
。josh
有一個角色 USER
,而 rob
同時擁有 ADMIN
和 USER
兩個角色。
在一個非簡單的應用中,您會希望使用指向身份提供者的替代 UserDetailsService
實現。或者至少,使用一個包含編碼密碼且永不儲存明文密碼的 SQL 資料庫。我在這裡註冊使用者及其密碼,但一個設計良好的系統會盡可能減少密碼的使用。關於密碼的常識和普遍認知很可能是錯誤的。美國國家標準與技術研究院(美國商務部的一個組織)去年(2024 年)釋出了其更新的密碼定義指南。
NIST 特別出版物 800-63B 提供了關於建立、處理、更新和儲存密碼(稱為“記憶的秘密”)的具體指導,並概述了對一次性令牌和基於硬體的認證器(例如 YubiKeys/WebAuthn)等替代方法的建議。讓我們看看他們的一些建議。
如果您的系統將使用密碼,有很多需要了解。幸好,Spring Security 可以為您完成以下大部分(全部?)指導。
使用者選擇的密碼必須至少八個字元長,而 CSP(憑證服務提供商)隨機生成的密碼必須至少六個字元長。不鼓勵使用複雜度規則(例如,要求包含大小寫字母、數字和符號)。不應任意限制密碼(例如,限制最大長度或排除某些字元)。密碼必須對照常用、已洩露或預期的密碼列表進行檢查(例如,字典詞彙、重複模式、服務特定術語)。如果密碼被標記為弱密碼,使用者必須選擇更強的替代方案。建議使用密碼強度計或反饋來幫助使用者建立強密碼。指南建議允許複製貼上功能以鼓勵使用密碼管理器,並提供可選的顯示輸入密碼功能以最大程度地減少輸入錯誤。文件建議將連續失敗的登入嘗試限制在不超過 100 次,並實施 CAPTCHA、增加時間延遲或其他自適應措施,以防止因濫用而導致賬戶鎖定。有趣的是,它建議重新認證策略應根據保證級別而有所不同(例如,對於高保證級別,要求每 12 小時或在 30 分鐘不活動後進行重新認證)。另一方面,不應任意或定期要求更改密碼。只有在有證據表明密碼已被洩露時才應強制更改。它建議使用單向關鍵派生函式(例如 PBKDF2、BCrypt 或 Argon2)對密碼進行雜湊和加鹽處理。Spring Security 預設使用 BCrypt。
NIST 建議使用密碼的替代方案,如一次性令牌(OTPs)。單因素 OTP 裝置生成基於時間或基於計數器的一次性令牌。OTPs 必須在密碼學上安全,並具有至少 20 位的熵。多因素 OTP 裝置需要在生成 OTP 之前要求第二個認證因素(例如 PIN 或生物識別)。帶外認證(Out-of-band authentication)利用次要通訊通道(例如移動裝置)進行 OTP 傳遞或確認,這需要安全通道並限制弱方法。也鼓勵使用基於硬體的認證器(例如 YubiKeys、WebAuthn)。WebAuthn 是 FIDO 聯盟的一部分,被鼓勵作為一種健壯且能抵抗網路釣魚的認證方法。像 YubiKeys 這樣的硬體認證器使用加密協議來確保安全。多因素加密裝置必須將“您擁有的東西”(例如 YubiKey)與“您知道的東西”(例如 PIN)或“您是什麼”(例如生物識別)結合起來。金鑰應保留在防篡改硬體中,並且無法被提取。
透過優先考慮這些方法,NIST 強調向多因素認證(MFA)和加密解決方案過渡,將其作為傳統密碼的更安全替代方案。
因此,在 Spring Security 應用上下文中,一次性令牌透過依賴使用者才能擁有的帶外因素來認證使用者——也許是使用者接收簡訊、訪問電子郵件等的能力。您很可能之前在其他地方使用過這個功能。您訪問一個網站,輸入使用者名稱,網站會向您傳送一封包含連結的電子郵件,您可以點選該連結登入。這些有時被稱為“魔法連結”。
Spring Security 不提供與您的電子郵件提供商或首選訊息應用程式等整合。您可以使用 Sendgrid、Twilio 或其他一百萬個服務中的任何一個來實現這一點。但 Spring Security 提供了透過連結建立和認證所需的基礎設施(plumbing)。
這是我們需要新增到 Spring Security 配置中的一小段程式碼,以便它列印
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
// ..
.oneTimeTokenLogin(configurer -> configurer.tokenGenerationSuccessHandler(
(request, response, oneTimeToken) -> {
var msg = "go to https://:8080/login/ott?token=" + oneTimeToken.getTokenValue();
System.out.println(msg);
response.setContentType(MediaType.TEXT_PLAIN_VALUE);
response.getWriter().print("you've got console mail!");
}))
// ..
.build();
}
看到沒?它只是一個簡單的 lambda 表示式,您在其中提供足夠的上下文來生成併發送一個連結給使用者,一旦點選,使用者就可以登入。很簡單!在這個例子中,我只是透過點選控制檯中的連結來登入。再說一次,您可以傳送電子郵件或更實際的方式。
這非常方便,讓使用者不必擔心或者——更糟——分享兩個密碼。希望他們已經鎖定了他們的電子郵件密碼!我寧願他們有一個不跨網站共享的好密碼,也不願他們有一打糟糕且共享的密碼。
另一種方法——另一個安全層——可能涉及比文字連結更復雜的東西,以防潛在駭客。例如您的指紋、面部 ID 掃描或獨立的硬體加密狗。問題是:如何在需要的地方整合這些東西?為了讓它們工作,我們需要修改瀏覽器、伺服器端處理邏輯、作業系統等,讓它們都遵循一個標準協議。
您會很高興地知道,這正是幾乎所有人都已經在做的事情。所討論的協議稱為 WebAuthn,其背後的組織稱為 FIDO 聯盟。FIDO 聯盟得到了普遍支援!以下是一些 最傑出的成員。名單包括 DELL、Apple、Google、Intuit、NTT DOCOMO、Microsoft、meta、LastPass、DashLane、美國銀行、1Password、Intel、CISCO、CVS Health、美國運通、VISA 以及無數其他公司。關鍵在於,那些真金白銀的公司都在支援這種方法。瀏覽器也是如此!所有主流瀏覽器——Chrome、Safari、Edge、Firefox、iOS 上的 Safari、Android 上的 Chrome、iOS 上的 Chrome 和 iOS 上的 Edge——都支援它。它無處不在!現在,得益於 Spring Security 的這個版本,它也很容易整合到您的應用中。
這是我們的 Spring Security 過濾器鏈示例中的相關配置。
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
// ...
.webAuthn(c -> c
.rpId("localhost")
.rpName("bootiful passkeys")
.allowedOrigins("https://:8080")
)
// ...
.build();
}
重啟您的應用,然後登入 localhost:8080/webauthn/register
。註冊您的 Passkey。我使用 Apple 生態系統,因此它會提示我在 iPhone 上進行 FaceID 或在我的 macOS Apple Silicon 筆記型電腦上使用 TouchID。然後,瀏覽器將 Passkey 儲存在作業系統的鑰匙串中。現在它已在 iCloud 中同步。因此,我不僅有了一種有效的登入方式,而且它還與我的 iCloud 賬戶繫結,這樣我就可以在一臺裝置上使用 Face ID 登入,在另一臺裝置上使用 Touch ID 登入。無需額外工作!
要檢視實際效果,請退出登入:localhost:8080/logout
。
不僅非常安全,而且對使用者來說更容易!是不是很酷?