文章目录
-
- 一、核心概念:先分清「认证」和「授权」
- [二、底层实现基石:Servlet Filter 链](#二、底层实现基石:Servlet Filter 链)
- 三、核心组件解析:各司其职的「安全积木」
-
- [1. Authentication:认证信息载体](#1. Authentication:认证信息载体)
- [2. AuthenticationManager:认证入口](#2. AuthenticationManager:认证入口)
- [3. UserDetailsService:用户信息加载器](#3. UserDetailsService:用户信息加载器)
- [4. SecurityContext:认证信息存储容器](#4. SecurityContext:认证信息存储容器)
- [5. PasswordEncoder:密码加密与校验器](#5. PasswordEncoder:密码加密与校验器)
- 四、核心工作流程:从请求到响应的安全校验
-
- [1. 整体时序图(认证+授权全流程)](#1. 整体时序图(认证+授权全流程))
- [2. 流程拆解说明](#2. 流程拆解说明)
- 五、简化流程图:认证与授权的整体逻辑
- [六、快速实践:SpringBoot + SpringSecurity 示例](#六、快速实践:SpringBoot + SpringSecurity 示例)
-
- [1. 引入依赖](#1. 引入依赖)
- [2. 核心配置类](#2. 核心配置类)
- [3. 测试接口](#3. 测试接口)
- [4. 测试结果](#4. 测试结果)
- [七、总结:SpringSecurity 的核心思想与扩展点](#七、总结:SpringSecurity 的核心思想与扩展点)
在 Java 生态中,SpringSecurity 是最主流的安全框架,核心目标是解决应用的认证(Authentication) 和授权(Authorization) 问题。它基于 Servlet Filter 链实现,通过组件化设计提供灵活的安全控制能力,支持表单登录、JWT、OAuth2 等多种认证方式,适配从单体应用到微服务的各类场景。
本文将从核心概念、底层基石、关键组件、工作流程四个维度,结合可视化图表,带你搞懂 SpringSecurity 的技术原理。
一、核心概念:先分清「认证」和「授权」
很多人会混淆这两个核心概念,用通俗的话解释:
- 认证(Authentication):验证「你是谁」(比如登录时输入用户名密码,证明你是系统合法用户);
- 授权(Authorization):验证「你能做什么」(比如普通用户不能访问管理员接口,验证权限是否匹配)。
SpringSecurity 的所有功能,都是围绕这两个核心展开的。
二、底层实现基石:Servlet Filter 链
SpringSecurity 的核心底层是 Servlet Filter 链------它不直接侵入业务代码,而是通过过滤器拦截所有请求,在请求到达 Controller 之前完成认证、授权等安全校验。
但 SpringSecurity 没有直接使用原生 Filter,而是封装了一个核心入口:FilterChainProxy(过滤器链代理),其核心作用是:
- 统一管理所有安全相关的过滤器(称为「Security Filter」);
- 根据请求路径匹配对应的过滤器链(支持多链配置,适配不同资源的安全规则);
- 屏蔽原生 Filter 的配置复杂性,提供 Spring 风格的灵活扩展。
你可以把 FilterChainProxy 理解为「安全安检总入口」,里面包含了多个「安检通道」(过滤器链),每个通道里有多个「安检步骤」(Security Filter),比如「身份验证」「行李检查(权限校验)」「违禁品排查(CSRF 防护)」等。
核心 Security Filter 示例(按执行顺序):
| 过滤器名称 | 核心作用 |
|---|---|
UsernamePasswordAuthenticationFilter |
处理表单登录的认证逻辑 |
JwtAuthenticationFilter(自定义) |
处理 JWT 令牌的认证逻辑 |
FilterSecurityInterceptor |
授权校验的核心过滤器(最后执行) |
LogoutFilter |
处理退出登录逻辑 |
三、核心组件解析:各司其职的「安全积木」
SpringSecurity 通过组件化设计解耦功能,每个组件承担单一职责,可灵活替换扩展。以下是最核心的组件:
1. Authentication:认证信息载体
本质是一个「认证信息容器」,存储用户的身份、权限等信息,有两个核心状态:
- 未认证:存储用户输入的原始信息(如用户名、密码);
- 已认证:存储校验通过后的用户信息(如用户ID、角色、权限集合)。
核心方法:
java
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities(); // 获取权限集合
Object getCredentials(); // 获取凭证(如密码,认证后通常会清空)
Object getDetails(); // 获取额外信息(如IP地址)
Object getPrincipal(); // 获取用户主体(如用户名、UserDetails对象)
boolean isAuthenticated(); // 是否已认证
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
2. AuthenticationManager:认证入口
认证的「总指挥」,是认证流程的核心入口,仅定义了一个认证方法:
java
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
- 输入:未认证的
Authentication(含用户名、密码); - 输出:已认证的
Authentication(含权限信息); - 异常:认证失败时抛出
AuthenticationException(如用户名不存在、密码错误)。
实际开发中,我们常用它的实现类 ProviderManager,支持配置多个 AuthenticationProvider(认证提供者),适配多种认证方式(如表单登录、JWT、LDAP)。
3. UserDetailsService:用户信息加载器
负责「加载用户的原始信息」(如从数据库、缓存、配置文件中获取),是连接应用与用户数据源的桥梁。
核心方法:
java
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
- 输入:用户名;
- 输出:
UserDetails对象(包含用户名、加密后的密码、权限集合、账户状态等); - 异常:用户名不存在时抛出
UsernameNotFoundException。
UserDetails 是 SpringSecurity 定义的「用户信息标准接口」,我们可以自定义实现(如关联数据库中的 User 表)。
4. SecurityContext:认证信息存储容器
存储已认证用户的 Authentication 对象,本质是一个「线程安全的存储容器」,通过 SecurityContextHolder 静态工具类访问。
核心特性:
-
线程绑定:默认使用
ThreadLocal存储,确保每个请求线程的认证信息隔离; -
生命周期:请求开始时创建,认证成功后存入
Authentication,请求结束时清除; -
便捷访问:在任何地方(Controller、Service)都能通过以下代码获取当前用户:
java// 获取当前认证用户信息 Authentication auth = SecurityContextHolder.getContext().getAuthentication(); String username = auth.getName(); // 获取用户名 Collection<? extends GrantedAuthority> authorities = auth.getAuthorities(); // 获取权限
5. PasswordEncoder:密码加密与校验器
负责密码的「加密存储」和「校验匹配」,解决明文密码泄露风险。SpringSecurity 5.x 后强制要求配置,否则会报错。
核心方法:
java
public interface PasswordEncoder {
String encode(CharSequence rawPassword); // 明文密码加密
boolean matches(CharSequence rawPassword, String encodedPassword); // 校验明文与加密密码是否匹配
}
常用实现类:
BCryptPasswordEncoder(推荐):基于 BCrypt 算法,自动生成盐值,加密后密码不可逆;NoOpPasswordEncoder:不加密(仅用于测试,生产禁用)。
四、核心工作流程:从请求到响应的安全校验
SpringSecurity 的工作流程本质是「Filter 链的执行流程」,分为「认证流程」和「授权流程」两个阶段。以下结合时序图,完整拆解请求的流转过程。
1. 整体时序图(认证+授权全流程)
客户端 FilterChainProxy(过滤器链代理) 认证过滤器(如UsernamePasswordAuthFilter) AuthenticationManager UserDetailsService SecurityContext 授权过滤器(FilterSecurityInterceptor) 目标资源(Controller/Service) 发送请求(登录/访问受保护资源) 执行认证过滤器 提取请求中的认证信息(用户名/密码/JWT) 调用authenticate()发起认证 调用loadUserByUsername()加载用户信息 返回UserDetails(用户名+加密密码+权限) 通过PasswordEncoder校验密码 返回已认证的Authentication对象 将Authentication存入SecurityContext 认证通过,继续执行过滤器链 抛出AuthenticationException 返回401未授权响应 alt [认证成功] [认证失败] 执行授权过滤器(最后一个Security Filter) 从SecurityContext获取Authentication 校验用户权限是否匹配资源要求 授权通过 访问目标资源 返回资源响应 返回403禁止访问响应 alt [授权通过] [授权失败] 客户端 FilterChainProxy(过滤器链代理) 认证过滤器(如UsernamePasswordAuthFilter) AuthenticationManager UserDetailsService SecurityContext 授权过滤器(FilterSecurityInterceptor) 目标资源(Controller/Service)
2. 流程拆解说明
(1)认证流程(「你是谁」的校验)
- 客户端发送请求(如表单登录、携带 JWT 令牌访问接口);
FilterChainProxy拦截请求,执行对应的认证过滤器(如表单登录对应UsernamePasswordAuthenticationFilter);- 认证过滤器提取请求中的认证信息(如用户名密码、JWT 令牌),封装为未认证的
Authentication对象; - 认证过滤器调用
AuthenticationManager的authenticate()方法发起认证; AuthenticationManager委托UserDetailsService加载用户原始信息(UserDetails);AuthenticationManager通过PasswordEncoder校验凭证(如密码是否匹配、JWT 是否有效);- 认证成功:返回已认证的
Authentication对象,存入SecurityContext; - 认证失败:抛出
AuthenticationException,返回 401 响应。
(2)授权流程(「你能做什么」的校验)
- 认证通过后,请求继续执行过滤器链,最终到达
FilterSecurityInterceptor(授权核心过滤器); - 授权过滤器从
SecurityContext中获取已认证的Authentication对象; - 提取当前请求的资源要求(如访问
/admin/**需ADMIN角色); - 校验用户的权限集合是否满足资源要求:
- 满足:放行请求,访问目标资源(Controller/Service);
- 不满足:返回 403 禁止访问响应。
五、简化流程图:认证与授权的整体逻辑
为了更直观理解,用流程图总结核心逻辑:

六、快速实践:SpringBoot + SpringSecurity 示例
理论结合实践,通过一个简单示例,感受 SpringSecurity 的核心配置(基于 SpringBoot 3.x)。
1. 引入依赖
xml
<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>
2. 核心配置类
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity // 启用SpringSecurity
public class SecurityConfig {
// 1. 配置密码加密器(必须)
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 2. 配置用户信息(模拟从数据库加载,实际用MyBatis/JPA替换)
@Bean
public UserDetailsService userDetailsService() {
// 管理员:用户名admin,密码123456,角色ADMIN
UserDetails admin = User.withUsername("admin")
.password(passwordEncoder().encode("123456"))
.roles("ADMIN") // 角色本质是一种权限(ROLE_ADMIN)
.build();
// 普通用户:用户名user,密码123456,角色USER
UserDetails user = User.withUsername("user")
.password(passwordEncoder().encode("123456"))
.roles("USER")
.build();
// 内存级用户管理器(生产环境替换为数据库实现)
return new InMemoryUserDetailsManager(admin, user);
}
// 3. 配置安全规则(URL授权、登录/退出行为)
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// 配置URL授权规则
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll() // 公开接口,无需认证
.requestMatchers("/admin/**").hasRole("ADMIN") // 管理员接口需ADMIN角色
.anyRequest().authenticated() // 其他所有请求需认证
)
// 配置表单登录(默认提供登录页面,可自定义)
.formLogin(form -> form
.defaultSuccessUrl("/index", true) // 登录成功后跳转页面
.permitAll() // 登录页面无需认证
)
// 配置退出登录
.logout(logout -> logout
.logoutSuccessUrl("/login") // 退出成功后跳转登录页
.permitAll()
);
return http.build();
}
}
3. 测试接口
java
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("/public/hello")
public String publicHello() {
return "Hello, Public!";
}
// 普通用户接口:登录后即可访问
@GetMapping("/user/hello")
public String userHello() {
return "Hello, User!";
}
// 管理员接口:需ADMIN角色才能访问
@GetMapping("/admin/hello")
public String adminHello() {
return "Hello, Admin!";
}
// 登录成功跳转页
@GetMapping("/index")
public String index() {
return "Login Success!";
}
}
4. 测试结果
- 访问
/public/hello:直接返回响应(无需登录); - 访问
/user/hello:跳转登录页,登录user/123456后正常访问,登录admin/123456也能访问(管理员拥有普通用户权限); - 访问
/admin/hello:登录user/123456后返回 403,登录admin/123456后正常访问; - 未登录访问受保护接口:自动跳转登录页(302 重定向)。
七、总结:SpringSecurity 的核心思想与扩展点
核心思想
- 基于 Filter 链的拦截机制:不侵入业务代码,通过过滤器链实现安全校验,解耦性强;
- 组件化设计 :核心组件(
AuthenticationManager、UserDetailsService等)均可替换,支持灵活扩展; - 线程绑定的认证信息存储 :通过
SecurityContextHolder便捷访问当前用户,简化业务开发。
常见扩展场景
- 自定义认证方式 :如 JWT 认证,需自定义
JwtAuthenticationFilter和JwtAuthenticationProvider; - 自定义用户数据源 :将
UserDetailsService替换为数据库查询(如 MyBatis 加载sys_user表); - 自定义授权逻辑 :实现
AccessDecisionManager接口,支持复杂的权限校验(如数据权限); - 自定义登录/退出页面 :通过
formLogin().loginPage("/custom-login")配置自定义登录页。
SpringSecurity 看似复杂,但核心逻辑围绕「认证+授权」展开,理解了 Filter 链、核心组件和工作流程,就能灵活应对各类安全需求。