HoRain云--Spring Security认证与授权全解析
本文详细介绍了Spring Security框架的认证和授权流程。通过流程图展示了HTTP请求从抵达至处理的完整安全流程,包括认证(验证用户身份)和授权(判断用户权限)两个核心环节。文章深入解析了认证流程中的请求拦截、凭证提取、密码校验等步骤,以及授权流程中的权限规则获取、决策机制等关键点。同时提供了基础配置示例、方法级授权实现和最佳实践建议(如密码加密、最小权限原则等)。本文适合需要快速掌握Sp

🎬 HoRain云小助手:个人主页
🔥 个人专栏: 《Linux 系列教程》《c语言教程》
⛺️生活的理想,就是为了理想的生活!
⛳️ 推荐
前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。
专栏介绍
|
专栏名称 |
专栏介绍 |
|
本专栏主要撰写C干货内容和编程技巧,让大家从底层了解C,把更多的知识由抽象到简单通俗易懂。 |
|
|
本专栏主要是注重从底层来给大家一步步剖析网络协议的奥秘,一起解密网络协议在运行中协议的基本运行机制! |
|
|
全面深入解析 docker 容器,从基础到进阶,涵盖原理、操作、实践案例,助您精通 docker。 |
|
|
本专栏主要撰写Linux干货内容,从基础到进阶,知识由抽象到简单通俗易懂,帮你从新手小白到扫地僧。 |
|
|
本专栏着重撰写Python相关的干货内容与编程技巧,助力大家从底层去认识Python,将更多复杂的知识由抽象转化为简单易懂的内容。 |
|
|
本专栏主要是发布一些考试和练习题库(涵盖软考、HCIE、HRCE、CCNA等) |
目录

Spring Security 是 Spring 生态中负责安全的框架,其核心功能认证(Authentication) 和 授权(Authorization) 通过一系列精密的组件协作完成。简单来说,认证解决“你是谁”的问题,而授权则判断“你能做什么” 。
为了让你快速建立整体认知,下图清晰地描绘了一个请求从抵达至被处理的完整安全流程,它本质上是一个过滤器链:
flowchart TD
A[HTTP请求] --> B[过滤器链]
B --> C[SecurityContextPersistenceFilter<br>加载SecurityContext]
C --> D{是否为认证请求?}
D -- 是<br>如/login --> E[认证过滤器<br>如UsernamePasswordAuthenticationFilter]
E --> F[AuthenticationManager<br>认证入口]
F --> G[AuthenticationProvider<br>执行具体认证逻辑]
G --> H[UserDetailsService<br>加载用户信息]
H --> I[PasswordEncoder<br>密码校验]
I --> J{认证成功?}
J -- 是 --> K[构建Authentication对象<br>并存入SecurityContextHolder]
J -- 否 --> L[抛出AuthenticationException]
L --> M[ExceptionTranslationFilter<br>处理异常<br>如返回401]
K --> N[FilterSecurityInterceptor<br>执行授权检查]
N --> O[SecurityMetadataSource<br>获取资源所需权限]
O --> P[AccessDecisionManager<br>进行授权决策]
P --> Q{有权访问?}
Q -- 是 --> R[访问目标资源]
Q -- 否 --> S[抛出AccessDeniedException]
S --> T[ExceptionTranslationFilter<br>处理异常<br>如返回403]
🔑 深入认证流程
认证的目的是确认用户的身份。其核心步骤与上图的左侧部分对应:
-
请求拦截与凭证提取:当用户提交登录请求(如访问
/login),UsernamePasswordAuthenticationFilter会拦截该请求,并从请求中提取出用户名和密码,将其封装成一个未认证的UsernamePasswordAuthenticationToken对象 。 -
认证逻辑执行:这个未认证的 Token 被传递给
AuthenticationManager(其默认实现是ProviderManager)。ProviderManager会遍历一个AuthenticationProvider列表,找到一个能够处理该类型 Token 的 Provider(通常是DaoAuthenticationProvider)来执行实际认证 。 -
加载用户信息与密码校验:
DaoAuthenticationProvider会调用我们自定义的UserDetailsService实现。UserDetailsService的loadUserByUsername(String username)方法负责从数据库或其他数据源中根据用户名加载用户详细信息,并返回一个UserDetails对象 。接着,DaoAuthenticationProvider会使用配置的PasswordEncoder(如BCryptPasswordEncoder)来比对用户输入的密码和UserDetails中存储的已加密密码 。 -
认证结果处理:
-
成功:认证成功后,
AuthenticationProvider会返回一个包含用户信息和权限(GrantedAuthority)的已认证的Authentication对象。该对象会被设置到SecurityContextHolder中,这意味着用户在此次会话(或请求)中已被视为登录状态 。 -
失败:如果任何一步出错(如用户不存在、密码错误),则会抛出特定的
AuthenticationException,并由ExceptionTranslationFilter捕获,最终返回 401 等错误状态 。
-
🛡️ 深入授权流程
授权发生在认证之后,决定已认证的用户是否有权限访问某个资源。其核心步骤与上图的右侧部分对应:
-
触发授权检查:当用户访问一个受保护的资源(如
/admin)时,请求会到达过滤器链末端的FilterSecurityInterceptor。 -
获取权限规则:
FilterSecurityInterceptor会通过SecurityMetadataSource查询访问该资源所需的权限(例如,访问/admin/**需要ROLE_ADMIN)。这些规则通常在我们的SecurityConfig配置类中通过http.authorizeRequests()进行定义 。 -
授权决策:
FilterSecurityInterceptor调用AccessDecisionManager来做出最终的授权决策。AccessDecisionManager本身不直接做决定,而是依赖于一组AccessDecisionVoter进行“投票” 。Spring Security 提供了三种主要的决策策略:-
AffirmativeBased(默认):只要有一个投票器通过即放行 。 -
ConsensusBased:多数投票器通过则放行 。 -
UnanimousBased:要求所有投票器一致通过才放行 。常见的
RoleVoter会检查Authentication对象中的权限是否包含资源所需的权限 。
-
-
决策结果处理:
-
通过:请求被放行,访问目标资源。
-
拒绝:如果权限不足,
AccessDecisionManager会抛出AccessDeniedException,同样由ExceptionTranslationFilter捕获,通常会返回 403 错误 。
-
💻 实践指南与最佳实践
1. 基础配置示例
以下是一个整合了认证和授权的配置类示例,它展示了如何保护不同的URL路径:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll() // 公开访问
.requestMatchers("/admin/**").hasRole("ADMIN") // 需要ADMIN角色
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN") // 需要USER或ADMIN角色
.anyRequest().authenticated() // 其他所有请求需要认证
)
.formLogin(form -> form
.loginPage("/login") // 自定义登录页面
.defaultSuccessUrl("/home") // 登录成功后的默认跳转页面
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll()
);
return http.build();
}
// 配置用户详情服务(此处为内存演示,生产环境需连接数据库)
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
UserDetails admin = User.withUsername("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
// 密码编码器,必须配置
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
2. 方法级授权
除了在Web层面进行授权,还可以在Service层的方法上进行更细粒度的控制。首先需要启用该功能:
@Configuration
@EnableMethodSecurity(prePostEnabled = true) // 启用方法级安全控制
public class MethodSecurityConfig {
}
然后,可以直接在方法上使用注解:
@Service
public class BookService {
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
public void deleteUserBook(Long userId, String bookId) {
// 只有管理员或资源所有者本人可以执行此操作
}
@PostAuthorize("returnObject.owner == authentication.name")
public Book getBookDetails(String bookId) {
// 方法执行后检查,确保用户只能访问属于自己的书籍详情
}
}
3. 最佳实践
-
密码加密:永远使用强大的
PasswordEncoder(如BCryptPasswordEncoder)来加密存储密码,切勿使用明文 。 -
最小权限原则:只授予用户完成其工作所必需的最小权限。
-
动态加载权限:对于复杂系统,应从数据库动态加载权限规则,而非在代码中写死。这通常通过自定义
UserDetailsService实现,在loadUserByUsername方法中关联查询用户的角色和权限 。 -
正确处理异常:自定义
AuthenticationEntryPoint和AccessDeniedHandler以给前端返回统一的、用户友好的错误信息 。
希望这份详细的流程解析和实践指南能帮助你更好地理解和应用 Spring Security。如果你对某个特定环节(如集成JWT、OAuth2.0)有进一步的兴趣,我们可以继续深入探讨。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄
💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍
🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙
更多推荐



所有评论(0)