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