Spring Security 7 中的多因素身份驗證
1. 概述
身份驗證是所有網路應用程式的第一道防線。傳統上,應用程式依靠使用者名稱和密碼來驗證使用者身份。然而,專家認為,對於現代系統而言,僅依賴單一身分驗證因素已不再安全。
多因素身份驗證 (MFA)**透過要求使用者在存取系統之前使用多個獨立因素來驗證其身分來解決這個問題。**
Spring Security 7引入了對多因素身份驗證 (MFA)的內建支持,允許開發人員使用現有的授權模型強制執行多重身份驗證步驟。在本文中,我們將探討 Spring Security 7 中的 MFA 工作原理以及如何在 Spring Boot 應用程式中實現它。
2. 理解 Spring Security 7 中的 MFA
Spring Security 7**引入了一種使用授權機制來建模身份驗證因素的新方法。 Spring **Security 不再將多因素身份驗證 (MFA) 視為單獨的身份驗證機制,而是根據每個成功的身份驗證因素逐步授予相應的授權。
多因素身份驗證 (MFA) 透過要求使用者使用多個獨立因素驗證身分來增強安全性。這些因素通常分為三類:使用者知道的訊息,例如密碼或 PIN 碼;使用者擁有的物品,例如行動裝置或電子郵件令牌;以及使用者本身的特徵,例如指紋或其他生物辨識資料。透過結合這些因素,應用程式可以顯著降低憑證外洩和未經授權存取的風險。
每次使用者使用特定因素成功通過身份驗證時,Spring Security 都會在身份驗證物件中新增相應的授權。此授權代表身分驗證過程中已驗證的因素。一些常見範例包括:用於基於密碼的身份驗證的FACTOR_PASSWORD 、用於基於證書的身份驗證的FACTOR_X509以及用於一次性令牌身份驗證的FACTOR_OTT 。這些授權在內部由FactorGrantedAuthority類別表示,並成為已驗證使用者安全上下文的一部分。
這種設計允許授權規則在授予對受保護資源的存取權之前,驗證是否符合所需的身份驗證因素。
3. 項目設定
在實現多因素身份驗證之前,我們將使用Spring Initializr和 Spring Security 7 設定一個簡單的 Spring Boot 應用程式。首先,我們需要配置所需的依賴項。
我們提供了適用於[web](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web/4.0.3) , [security](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security/4.0.3/dependencies) , [starter-test](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test/4.0.3/dependencies) , [webmvc-test](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-webmvc-test/4.0.3) ,和[security-testing](https://mvnrepository.com/artifact/org.springframework.security/spring-security-test/7.0.0)的 Spring Boot starters:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>4.0.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<version>4.0.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>7.0.0</version>
<scope>test</scope>
</dependency>
此配置新增了應用程式所需的核心相依性。它包括 Spring Boot 的 Web 支援(用於建立 REST 端點)、Spring Security 框架(用於實現身份驗證和授權)以及 Spring Security 測試工具(用於編寫以安全為中心的單元測試)。
有了這些依賴項,應用程式現在就具備了配置和啟用多因素身份驗證所需的一切。
4. 在全球啟用多因素身份驗證
Spring Security 7 引進了一個名為@EnableMultiFactorAuthentication的便利註解。此註解允許開發人員定義所有受保護端點所需的身份驗證因素。
以下配置啟用使用密碼和 X509 身份驗證因素的全域 MFA:
@Configuration
@EnableWebSecurity
@EnableMultiFactorAuthentication(
authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.X509_AUTHORITY }
)
public class GlobalMfaSecurityConfig {
@Bean
@Order(3)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher("/**").authorizeHttpRequests(auth ->auth.requestMatchers("/public")
.permitAll().anyRequest().authenticated()).formLogin(withDefaults());
return http.build();
}
}
此配置為應用程式啟用 Spring Security,並全域啟用多因素身份驗證。作為設定的一部分,它指定使用者必須完成兩項身份驗證,系統才會認為已完全通過身份驗證。
在這種情況下,所需的驗證因素是密碼和 X.509 證書認證。配置啟動後,應用程式中的每個安全端點都需要同時驗證這兩種因素才能獲得存取權限。像這樣全域應用多因素身份驗證 (MFA) 有助於避免不一致,並降低在各個授權配置中遺漏 MFA 規則的風險。
5. 將多因素身份驗證應用於特定端點
在許多應用程式中,多因素身份驗證 (MFA) 僅應用於敏感端點,例如帳戶設定、財務操作或管理員操作。 Spring Security 提供了AuthorizationManagerFactory API,用於以程式設計方式定義 MFA 規則。
以下配置僅要求對管理員端點啟用 MFA:
@Configuration
@EnableWebSecurity
public class AdminMfaSecurityConfig {
@Bean
@Order(1)
SecurityFilterChain adminSecurityFilterChain(HttpSecurity http) throws Exception {
AuthorizationManagerFactory<Object> mfa = AuthorizationManagerFactories.multiFactor()
.requireFactors(
FactorGrantedAuthority.PASSWORD_AUTHORITY,
FactorGrantedAuthority.X509_AUTHORITY)
.build();
http.securityMatcher("/admin/**").authorizeHttpRequests(auth ->auth.requestMatchers("/admin/**")
.access(mfa.hasRole("ADMIN")).anyRequest().authenticated()).formLogin(withDefaults());
return http.build();
}
}
在此組態中,多因素身份驗證僅套用於符合/admin/ ` 端點的請求。當使用者嘗試存取這些路由時,Spring Security 會檢查所需的身份驗證因素是否存在。
除了完成兩種身份驗證因素外,使用者還必須擁有ADMIN角色。這種方法可以對安全規則進行精細控制,允許開發人員僅在敏感端點上強制執行多因素身份驗證 (MFA),同時保持應用程式的其餘部分可透過標準身份驗證存取。
6. 實施基於時間的身份驗證規則
某些應用程式要求使用者在執行敏感操作之前重新進行身份驗證。例如,銀行應用程式可能要求用戶在更新付款資訊之前重新登入。 Spring Security 7 支援基於時間的 MFA 規則。
以下配置要求使用者在過去五分鐘內驗證密碼:
@Configuration
@EnableWebSecurity
public class TimeBasedMfaSecurityConfig {
@Bean
@Order(2)
SecurityFilterChain profileSecurityFilterChain(HttpSecurity http) throws Exception {
AuthorizationManagerFactory<Object> recentLogin = AuthorizationManagerFactories.
multiFactor().requireFactor(
factor -> factor.passwordAuthority().validDuration(Duration.ofMinutes(5)))
.build();
http.securityMatcher("/profile", "/profile/**").authorizeHttpRequests(
auth -> auth.requestMatchers("/profile", "/profile/**")
.access(recentLogin.authenticated())
.anyRequest().
authenticated()).formLogin(withDefaults());
return http.build();
}
}
此規則允許使用者使用已驗證的會話瀏覽應用程式的大部分內容。但是,當使用者嘗試存取*/profile/**下的端點時,系統會驗證密碼驗證是否最近已完成。
如果密碼驗證時間超過五分鐘,Spring Security 會要求使用者重新進行身份驗證才能授予存取權限。這種方法通常允許使用者執行敏感操作,確保他們僅在最近一次經過驗證的身份驗證步驟之後才能執行關鍵操作。
7. 實施基於使用者的 MFA 規則
有時,多因素身份驗證 (MFA) 規則僅適用於特定使用者。例如,管理員可能需要使用 MFA,而普通使用者可以使用單一因素身份驗證登入。 Spring Security 允許實作自訂授權管理員來支援此類場景。
以下範例僅對管理員使用者強制執行多因素身份驗證:
@Component
public class AdminMfaAuthorizationManager implements AuthorizationManager<Object> {
AuthorizationManager<Object> mfa =
AllAuthoritiesAuthorizationManager.hasAllAuthorities(FactorGrantedAuthority.OTT_AUTHORITY,
FactorGrantedAuthority.PASSWORD_AUTHORITY);
@Override
public AuthorizationResult authorize(Supplier<? extends Authentication> authentication, Object context) {
Authentication auth = authentication.get();
if (auth != null && "admin".equals(auth.getName())) {
return mfa.authorize(authentication, context);
}
return new AuthorizationDecision(true);
}
}
此邏輯會檢查已認證使用者的身份,並根據情況應用多因素身份驗證 (MFA) 規則。如果已認證使用者是管理員,系統會驗證該使用者是否已完成所有必要的身份驗證因素,然後才會授予存取權限。
對於其他所有用戶,系統允許該請求,而無需強制執行額外的多因素身份驗證 (MFA) 檢查。這種方法在逐步引入 MFA 時非常有用,它允許組織先對高權限使用者實施更嚴格的安全措施,然後再擴展到所有使用者。
8. 為藝術碩士課程編寫單元測試
測試身份驗證流程對於確保安全規則按預期運行至關重要。如果沒有適當的測試,就很難驗證角色、權限或多因素身份驗證 (MFA) 等身份驗證要求是否正確執行。
Spring Security 提供專用的測試工具,可以更輕鬆地模擬已認證使用者並驗證授權行為。這些工具允許開發人員編寫針對性強的測試,以驗證安全性配置是否正常運作,而無需進行完整的身份驗證設定。
8.1 控制器範例
在編寫測試之前,我們定義一個簡單的控制器,該控制器公開受 MFA 配置保護的端點:
@RestController
public class DemoController {
@GetMapping("/public")
public String publicEndpoint() {
return "public endpoint";
}
@GetMapping("/profile")
public String profileEndpoint() {
return "profile endpoint";
}
@GetMapping("/admin/dashboard")
public String adminDashboard() {
return "admin dashboard";
}
}
該控制器公開了三個端點/public, /profile,和/admin/dashboard ,有助於展示不同的 MFA 強制執行策略。
8.2. 多因素身份驗證安全測試
此測試驗證系統在全球強制執行多因素身份驗證時的運作:
@SpringBootTest(classes = Application.class)
@AutoConfigureMockMvc
class GlobalMfaSecurityTest {
@Autowired
MockMvc mockMvc;
@Test
void givenUserWithoutMfa_whenAccessProfile_thenForbidden() throws Exception {
mockMvc.perform(get("/profile").with(user("user").roles("USER")))
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location", containsString("/login")));
}
}
此測試模擬使用者嘗試在未完成必需的多因素身份驗證 (MFA) 的情況下存取管理端點。由於身份驗證請求僅包含基本角色,而未包含必要的身份驗證因素權限,因此該請求不符合配置的安全性規則。結果,Spring Security 傳回 403 Forbidden 回應,因為使用者尚未完成所有必需的身份驗證因素。
8.3. 管理員端點多因素身份驗證測試
接下來,我們將驗證系統是否對管理員端點強制執行多因素身份驗證 (MFA):
@SpringBootTest(classes = Application.class)
@AutoConfigureMockMvc
class AdminMfaSecurityTest {
@Autowired
MockMvc mockMvc;
@Test
void givenAdminWithoutMfa_whenAccessAdminEndpoint_thenForbidden() throws Exception {
mockMvc.perform(get("/admin/dashboard").with(user("admin").roles("ADMIN")))
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location", containsString("/login")));
}
}
此測試模擬管理員嘗試存取/admin/dashboard端點。儘管使用者擁有所需的 ADMIN 角色,但請求中缺少必要的 MFA 授權資訊。由於身份驗證因素不完整,Spring Security 會將要求重定向到登入頁面。這證實了 MFA 對特權端點的強制執行工作正常。
8.4. 基於時間的 MFA 安全測試
某些操作要求使用者最近進行過身份驗證,以確保會話未被竄改。以下測試驗證了個人資料端點是否需要最近一次的身份驗證:
@SpringBootTest(classes = Application.class)
@AutoConfigureMockMvc
class TimeBasedMfaSecurityTest {
@Autowired
MockMvc mockMvc;
@Test
void givenUserWithoutRecentAuthentication_whenAccessProfile_thenForbidden() throws Exception {
mockMvc.perform(get("/profile").with(user("user").roles("USER")))
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location", containsString("/login")));
}
}
此測試檢查/profile端點是否需要最近一次的身份驗證。由於模擬請求不包含必要的 MFA 因素訊息,Spring Security 會將請求重定向到登入頁面。這證實系統正確執行了基於時間的 MFA 規則。
9. 結論
多因素身份驗證 (MFA) 是現代應用程式的關鍵安全措施,它透過要求多個驗證因素來幫助降低未經授權存取的風險。
Spring Security 7 內建了對多因素身份驗證 (MFA) 的支持,允許開發者使用現有的授權模型強制執行多重身份驗證步驟。本文探討了FactorGrantedAuthority如何對 MFA 進行建模,如何使用@EnableMultiFactorAuthentication全域啟用 MFA,以及如何使用AuthorizationManagerFactory將其選擇性地應用於特定端點。此外,本文還介紹了基於時間的規則、自訂的基於使用者的策略,以及如何使用 Spring Security 測試工具測試 MFA 行為。
利用這些特性,開發者可以建立安全且靈活的身份驗證流程。隨著安全威脅的不斷演變,實施多因素身份驗證 (MFA) 已不再是可選項,而是保護使用者資料和關鍵系統的必要步驟。與往常一樣,此範例的程式碼可在 GitHub 上找到。