springSecurity简介
Spring Security 核心原理与组件详解
Spring Security 是基于 Spring 框架的安全解决方案,专注于为 Java 应用提供身份认证和授权功能。它利用 Spring AOP 和 Servlet 过滤器实现,其核心本质是一个过滤器链,通过一系列过滤器协同工作完成安全控制。
核心概念解析
主体(Principal)
使用系统的用户、设备或其他系统,即 “谁在使用系统”。在 Spring Security 中,主体通常通过 Authentication 对象表示。
认证(Authentication)
确认主体身份的过程,即 “证明你是谁”。认证成功后,用户信息会被存储在安全上下文中。
授权(Authorization)
授予主体操作权限的过程,即 “你能做什么”。通过判断主体是否拥有特定权限来允许或拒绝访问。
快速入门依赖
使用 Spring Boot 集成 Spring Security 只需添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
添加依赖后,应用会自动启用安全保护:
- 默认生成一个用户名为
user的用户 - 随机生成密码并打印在控制台
- 所有请求需要认证后才能访问
- 默认提供一个登录页面
核心组件详解
1. SecurityContextHolder
用于存储当前安全上下文信息的工具类,包含当前登录用户的认证信息。
存储策略:
MODE_THREADLOCAL:默认策略,使用 ThreadLocal 存储,仅当前线程可见MODE_INHERITABLETHREADLOCAL:子线程可继承父线程的安全上下文MODE_GLOBAL:全局共享一个安全上下文
使用示例:
// 获取当前认证信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 获取当前用户
Object principal = authentication.getPrincipal();
// 判断是否已认证
boolean isAuthenticated = authentication.isAuthenticated();
注意:默认情况下,每次请求结束后,Spring Security 会自动清除当前线程的安全上下文,避免内存泄漏。
2. Authentication
表示认证信息的接口,继承自 java.security.Principal,包含以下核心方法:
public interface Authentication extends Principal, Serializable {
// 获取权限集合
Collection<? extends GrantedAuthority> getAuthorities();
// 获取凭证(通常是密码,认证后会被清除)
Object getCredentials();
// 获取额外信息(如IP地址、会话ID等)
Object getDetails();
// 获取主体(通常是UserDetails对象)
Object getPrincipal();
// 是否已认证
boolean isAuthenticated();
// 设置认证状态
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
最常用的实现类是 UsernamePasswordAuthenticationToken,用于存储用户名密码认证信息。
3. AuthenticationManager
认证的核心接口,定义了认证方法:
public interface AuthenticationManager {
// 认证方法:接收一个未认证的Authentication,返回一个已认证的Authentication
Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
默认实现类:ProviderManager
ProviderManager 本身不直接处理认证,而是委托给一组 AuthenticationProvider 实例:
public class ProviderManager implements AuthenticationManager {
private List<AuthenticationProvider> providers;
public Authentication authenticate(Authentication authentication) {
for (AuthenticationProvider provider : providers) {
if (provider.supports(authentication.getClass())) {
return provider.authenticate(authentication);
}
}
throw new ProviderNotFoundException("No provider found");
}
}
在配置类中配置自定义认证提供者:
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 添加自定义认证提供者
auth.authenticationProvider(customAuthenticationProvider);
}
4. AuthenticationProvider
具体处理认证逻辑的接口,ProviderManager 会遍历所有 AuthenticationProvider 直到找到支持当前认证类型的实现:
public interface AuthenticationProvider {
// 执行认证
Authentication authenticate(Authentication authentication) throws AuthenticationException;
// 是否支持某种认证类型
boolean supports(Class<?> authentication);
}
常用实现类:DaoAuthenticationProvider
这是处理用户名密码认证的默认实现,工作流程:
- 调用
UserDetailsService获取用户信息 - 验证密码(使用 PasswordEncoder)
- 检查用户状态(是否锁定、过期等)
核心方法:
// 检查密码等额外认证逻辑
protected void additionalAuthenticationChecks(
UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) {
// 密码验证逻辑
}
// 获取用户信息
protected UserDetails retrieveUser(
String username,
UsernamePasswordAuthenticationToken authentication) {
// 调用UserDetailsService.loadUserByUsername()
}
5. UserDetails
封装用户详细信息的接口,包含认证和授权所需的用户信息:
public interface UserDetails extends Serializable {
// 获取用户权限
Collection<? extends GrantedAuthority> getAuthorities();
// 获取密码(存储的正确密码)
String getPassword();
// 获取用户名
String getUsername();
// 账户是否未过期
boolean isAccountNonExpired();
// 账户是否未锁定
boolean isAccountNonLocked();
// 凭证是否未过期
boolean isCredentialsNonExpired();
// 账户是否启用
boolean isEnabled();
}
Spring Security 提供了默认实现 User,也可自定义实现类扩展用户信息。
6. UserDetailsService
用于加载用户信息的接口,是应用程序与安全框架之间获取用户数据的桥梁:
public interface UserDetailsService {
// 根据用户名加载用户信息
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
自定义实现示例(从数据库加载用户):
public class CustomUserDetailsService implements UserDetailsService {
private UserRepository userRepository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从数据库查询用户
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
// 转换为UserDetails返回
return User.builder()
.username(user.getUsername())
.password(user.getPassword())
.authorities(getAuthorities(user.getRoles()))
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.enabled(true)
.build();
}
private Collection<? extends GrantedAuthority> getAuthorities(List<String> roles) {
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
}
}
7. GrantedAuthority
表示用户权限的接口,通常通过角色或权限字符串表示:
public interface GrantedAuthority extends Serializable {
// 返回权限字符串(如"ROLE_ADMIN"、"USER_CREATE")
String getAuthority();
}
最常用的实现类是 SimpleGrantedAuthority。
认证流程详解
以表单登录为例,Spring Security 的认证流程如下:
1. UsernamePasswordAuthenticationFilter 拦截登录请求
该过滤器专门处理 /login 路径的 POST 请求:
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
// 仅允许POST方法
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("不支持的认证方法: " + request.getMethod());
}
// 获取用户名和密码
String username = obtainUsername(request);
String password = obtainPassword(request);
// 封装为未认证的Authentication对象
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// 设置额外信息(如IP地址、会话ID)
setDetails(request, authRequest);
// 委托给AuthenticationManager进行认证
return this.getAuthenticationManager().authenticate(authRequest);
}
2. AuthenticationManager 处理认证
ProviderManager 会找到支持 UsernamePasswordAuthenticationToken 的 DaoAuthenticationProvider 进行处理。
3. DaoAuthenticationProvider 执行认证逻辑
- 调用
UserDetailsService.loadUserByUsername()获取用户信息 - 验证密码(使用
PasswordEncoder) - 检查用户状态(是否锁定、过期等)
- 生成已认证的
Authentication对象
4. 认证结果处理
- 认证成功:将
Authentication存入SecurityContextHolder,并重定向到首页 - 认证失败:清除安全上下文,重定向到登录页并显示错误信息
配置用户名密码
除了使用默认用户,还可以通过配置文件自定义:
spring:
security:
user:
name: admin
password: 123456
roles: ADMIN
在实际应用中,通常会使用自定义 UserDetailsService 从数据库加载用户信息,如前面的示例所示。
总结
Spring Security 的核心工作流程可以概括为:
- 过滤器拦截请求,获取用户提交的认证信息
- 将认证信息封装为
Authentication对象 AuthenticationManager委托AuthenticationProvider进行认证AuthenticationProvider通过UserDetailsService获取用户信息并验证- 认证成功后,将
Authentication存入SecurityContextHolder - 后续请求通过安全上下文判断用户身份和权限