Spring Security AuthenticationManager 接口詳解與實(shí)戰(zhàn)
概述
在 Spring Security 框架中,AuthenticationManager 接口扮演著核心角色,負(fù)責(zé)處理認(rèn)證請求并決定用戶身份是否合法。本文將詳細(xì)講解這一接口的工作原理、應(yīng)用場景,并結(jié)合 Spring Boot 3.4.3 版本提供實(shí)戰(zhàn)示例。
AuthenticationManager 接口概述
AuthenticationManager 是 Spring Security 認(rèn)證體系的核心接口,位于 org.springframework.security.authentication 包下,其定義非常簡潔:
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}該接口僅包含一個(gè)方法 authenticate(),用于處理認(rèn)證請求,其工作流程如下:
- 接收一個(gè)
Authentication對象作為參數(shù),該對象包含用戶提交的認(rèn)證信息(如用戶名 / 密碼) - 執(zhí)行認(rèn)證邏輯
- 認(rèn)證成功時(shí),返回一個(gè)包含完整用戶信息和權(quán)限的
Authentication對象 - 認(rèn)證失敗時(shí),拋出
AuthenticationException異常
核心實(shí)現(xiàn)類
在實(shí)際應(yīng)用中,我們通常不會(huì)直接實(shí)現(xiàn) AuthenticationManager 接口,而是使用其現(xiàn)成的實(shí)現(xiàn)類:
ProviderManager:
- 最常用的實(shí)現(xiàn)類
- 委托一個(gè)或多個(gè)
AuthenticationProvider實(shí)例處理認(rèn)證請求 - 支持多種認(rèn)證機(jī)制并存
AuthenticationProvider:
- 不是
AuthenticationManager的實(shí)現(xiàn)類,而是由ProviderManager調(diào)用 - 每個(gè)
AuthenticationProvider處理特定類型的認(rèn)證請求
- 不是
DaoAuthenticationProvider:
- 常用的
AuthenticationProvider實(shí)現(xiàn) - 通過
UserDetailsService獲取用戶信息并驗(yàn)證密碼
- 常用的
工作原理
AuthenticationManager 的認(rèn)證流程可概括為:
- 客戶端提交認(rèn)證信息(如用戶名 / 密碼)
- 認(rèn)證信息被封裝成
Authentication對象 AuthenticationManager接收該對象并調(diào)用authenticate()方法ProviderManager會(huì)遍歷其配置的AuthenticationProvider列表- 找到支持當(dāng)前
Authentication類型的AuthenticationProvider并委托其進(jìn)行認(rèn)證 - 認(rèn)證成功后,返回包含完整信息的
Authentication對象 - 認(rèn)證結(jié)果被 SecurityContext 存儲,用于后續(xù)的授權(quán)判斷
應(yīng)用場景
AuthenticationManager 適用于各種需要身份認(rèn)證的場景:
- 表單登錄認(rèn)證:處理用戶名 / 密碼登錄
- API 認(rèn)證:驗(yàn)證 API 密鑰或令牌
- 多因素認(rèn)證:結(jié)合多種認(rèn)證方式
- 第三方登錄:如 OAuth2、OpenID Connect 等
- 自定義認(rèn)證:實(shí)現(xiàn)特定業(yè)務(wù)需求的認(rèn)證邏輯
實(shí)戰(zhàn)示例(Spring Boot 3.4.3)
下面我們將通過一個(gè)完整示例展示如何在 Spring Boot 3.4.3 中配置和使用 AuthenticationManager。
1. 添加依賴
首先在 pom.xml 中添加必要依賴:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 其他依賴 -->
</dependencies>2. 配置 SecurityConfig
創(chuàng)建 Security 配置類,配置 AuthenticationManager 和安全規(guī)則:
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final UserDetailsService userDetailsService;
public SecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
// 配置 AuthenticationProvider
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
// 配置 PasswordEncoder
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 配置 AuthenticationManager
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
// 配置安全過濾鏈
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.defaultSuccessUrl("/api/home", true)
.permitAll()
)
.logout(logout -> logout.permitAll());
// 注冊自定義的 AuthenticationProvider
http.authenticationProvider(authenticationProvider());
return http.build();
}
}
3. 實(shí)現(xiàn) UserDetailsService
創(chuàng)建自定義的 UserDetailsService 實(shí)現(xiàn),用于加載用戶信息:
package com.example.demo.service;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 在實(shí)際應(yīng)用中,這里應(yīng)該從數(shù)據(jù)庫或其他數(shù)據(jù)源加載用戶信息
if ("user".equals(username)) {
return User.withUsername("user")
.password("$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW") // 密碼是 "password"
.roles("USER")
.build();
} else if ("admin".equals(username)) {
return User.withUsername("admin")
.password("$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW") // 密碼是 "password"
.roles("ADMIN", "USER")
.build();
} else {
throw new UsernameNotFoundException("User not found with username: " + username);
}
}
}
4. 創(chuàng)建認(rèn)證控制器
創(chuàng)建一個(gè)控制器來演示如何在代碼中使用 AuthenticationManager:
package com.example.demo.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final AuthenticationManager authenticationManager;
public AuthController(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@PostMapping("/login")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
// 創(chuàng)建 Authentication 對象
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
// 將認(rèn)證結(jié)果存入 SecurityContext
SecurityContextHolder.getContext().setAuthentication(authentication);
// 返回認(rèn)證成功的響應(yīng)
return ResponseEntity.ok(new JwtResponse("dummy-token",
authentication.getName(),
authentication.getAuthorities().toString()));
}
// 內(nèi)部類用于接收登錄請求
public static class LoginRequest {
private String username;
private String password;
// getters 和 setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
// 內(nèi)部類用于返回認(rèn)證響應(yīng)
public static class JwtResponse {
private String token;
private String username;
private String roles;
public JwtResponse(String token, String username, String roles) {
this.token = token;
this.username = username;
this.roles = roles;
}
// getters
public String getToken() { return token; }
public String getUsername() { return username; }
public String getRoles() { return roles; }
}
}
5. 測試接口
創(chuàng)建一個(gè)簡單的測試接口來驗(yàn)證認(rèn)證效果:
package com.example.demo.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/api/public/hello")
public String publicHello() {
return "Hello, Public!";
}
@GetMapping("/api/home")
public String home() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return "Hello, " + auth.getName() + "! You have roles: " + auth.getAuthorities();
}
@GetMapping("/api/admin/hello")
public String adminHello() {
return "Hello, Admin!";
}
}
自定義 AuthenticationManager
在某些場景下,我們可能需要自定義 AuthenticationManager 來實(shí)現(xiàn)特定的認(rèn)證邏輯。例如,實(shí)現(xiàn)一個(gè)多因素認(rèn)證:
package com.example.demo.config;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class CustomAuthenticationManager implements AuthenticationManager {
private final List<AuthenticationProvider> providers;
public CustomAuthenticationManager(List<AuthenticationProvider> providers) {
this.providers = providers;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
AuthenticationException lastException = null;
for (AuthenticationProvider provider : providers) {
if (provider.supports(authentication.getClass())) {
try {
// 調(diào)用 AuthenticationProvider 進(jìn)行認(rèn)證
Authentication result = provider.authenticate(authentication);
if (result.isAuthenticated()) {
// 可以在這里添加額外的認(rèn)證邏輯,如多因素認(rèn)證
return result;
}
} catch (AuthenticationException e) {
lastException = e;
}
}
}
if (lastException != null) {
throw lastException;
}
throw new BadCredentialsException("Authentication failed");
}
}
總結(jié)
AuthenticationManager 是 Spring Security 認(rèn)證體系的核心組件,負(fù)責(zé)協(xié)調(diào)認(rèn)證過程并委托具體的認(rèn)證邏輯給 AuthenticationProvider 實(shí)現(xiàn)。通過本文的講解和示例,我們了解了:
AuthenticationManager的基本概念和工作原理- 核心實(shí)現(xiàn)類及其各自的職責(zé)
- 在 Spring Boot 3.4.3 中如何配置和使用
AuthenticationManager - 如何通過自定義實(shí)現(xiàn)來滿足特定的認(rèn)證需求
掌握 AuthenticationManager 的使用,將有助于我們更好地理解和擴(kuò)展 Spring Security 的認(rèn)證功能,為應(yīng)用程序提供更安全、更靈活的身份驗(yàn)證機(jī)制。
到此這篇關(guān)于Spring Security AuthenticationManager 接口詳解與實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)springsecurity authenticationmanager接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?@Scheduled定時(shí)任務(wù)不執(zhí)行解決辦法
這篇文章主要給大家介紹了關(guān)于Java?@Scheduled定時(shí)任務(wù)不執(zhí)行解決的相關(guān)資料,當(dāng)@Scheduled定時(shí)任務(wù)不執(zhí)行時(shí)可以根據(jù)以下步驟進(jìn)行排查和解決,需要的朋友可以參考下2023-10-10
springboot配置kafka批量消費(fèi),并發(fā)消費(fèi)方式
文章介紹了如何在Spring Boot中配置Kafka進(jìn)行批量消費(fèi),并發(fā)消費(fèi),需要注意的是,并發(fā)量必須小于等于分區(qū)數(shù),否則會(huì)導(dǎo)致線程空閑,文章還總結(jié)了創(chuàng)建Kafka分區(qū)的命令,并鼓勵(lì)讀者分享經(jīng)驗(yàn)2024-12-12
用攔截器修改返回response,對特定的返回進(jìn)行修改操作
這篇文章主要介紹了用攔截器修改返回response,對特定的返回進(jìn)行修改操作。具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
Java中的WebSocket與實(shí)時(shí)通信詳解
本文介紹WebSocket的概念、工作原理及Java中實(shí)現(xiàn)WebSocket的技術(shù)手段,包括JavaAPIforWebSocket(JSR356)和SpringWebSocket,通過實(shí)際應(yīng)用場景(如即時(shí)聊天、實(shí)時(shí)通知、在線游戲)進(jìn)行了詳細(xì)分析,展示了WebSocket在現(xiàn)代Web應(yīng)用中的重要性,感興趣的朋友跟隨小編一起看看吧2025-11-11
Java 使用POI生成帶聯(lián)動(dòng)下拉框的excel表格實(shí)例代碼
本文通過實(shí)例代碼給大家分享Java 使用POI生成帶聯(lián)動(dòng)下拉框的excel表格,代碼簡單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-09-09
深入解析Java類加載的案例與實(shí)戰(zhàn)教程
本篇文章主要介紹Tomcat類加載器架構(gòu),以及基于類加載和字節(jié)碼相關(guān)知識,去分析動(dòng)態(tài)代理的原理,對Java類加載相關(guān)知識感興趣的朋友一起看看吧2022-05-05
Spring Cache和EhCache實(shí)現(xiàn)緩存管理方式
這篇文章主要介紹了Spring Cache和EhCache實(shí)現(xiàn)緩存管理方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06

