Spring Security 全面指南:从基础到高级实践
一、Spring Security 概述与核心概念
1.1 Spring Security 简介
Spring Security 是 Spring 生态系统中的安全框架,为基于 Java 的企业应用提供全面的安全服务。它起源于 2003 年的 Acegi Security 项目,2008 年正式成为 Spring 官方子项目,现已发展为企业级安全的事实标准。
核心特性:
- 认证(Authentication):验证用户身份
- 授权(Authorization):控制访问权限
- 防护(Protection):抵御常见攻击(CSRF、XSS 等)
- 集成(Integration):与 Spring、Servlet API、OAuth2 等无缝集成
- 扩展(Extensibility):高度模块化,支持自定义扩展
1.2 安全过滤器链
Spring Security 的核心是基于 Servlet Filter 的过滤器链(Filter Chain):
客户端请求 → DelegatingFilterProxy → FilterChainProxy → 安全过滤器链 → 应用
关键过滤器:
SecurityContextPersistenceFilter
:维护安全上下文UsernamePasswordAuthenticationFilter
:处理表单登录BasicAuthenticationFilter
:处理 HTTP 基本认证RememberMeAuthenticationFilter
:处理"记住我"功能AnonymousAuthenticationFilter
:匿名用户处理ExceptionTranslationFilter
:处理安全异常FilterSecurityInterceptor
:授权决策
1.3 核心组件
组件 | 职责 | 重要实现类 |
---|---|---|
SecurityContextHolder | 保存安全上下文 | ThreadLocalSecurityContextHolder |
Authentication | 封装认证信息 | UsernamePasswordAuthenticationToken |
UserDetails | 用户详细信息 | User |
UserDetailsService | 加载用户数据 | InMemoryUserDetailsManager |
AuthenticationManager | 认证入口 | ProviderManager |
AccessDecisionManager | 授权决策 | AffirmativeBased |
二、快速入门:基础配置
2.1 添加依赖
Maven 配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
2.2 最小配置示例
@Configuration
@EnableWebSecurity
public class BasicSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
2.3 自定义用户存储
2.3.1 JDBC 用户存储
@Autowired
private DataSource dataSource;
@Bean
public UserDetailsService userDetailsService() {
return new JdbcUserDetailsManager(dataSource);
}
初始化 SQL 脚本(schema.sql):
CREATE TABLE users (
username VARCHAR(50) NOT NULL PRIMARY KEY,
password VARCHAR(100) NOT NULL,
enabled BOOLEAN NOT NULL
);
CREATE TABLE authorities (
username VARCHAR(50) NOT NULL,
authority VARCHAR(50) NOT NULL,
FOREIGN KEY (username) REFERENCES users(username)
);
INSERT INTO users VALUES ('user', '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', true);
INSERT INTO authorities VALUES ('user', 'ROLE_USER');
2.3.2 自定义用户服务
@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 new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.getAuthorities());
}
}
三、认证机制详解
3.1 密码编码器
Spring Security 5 强制要求密码编码:
编码器 | 描述 | 示例 |
---|---|---|
BCryptPasswordEncoder | BCrypt 哈希 | $2a$10$N9qo8uLOickgx2ZMRZoMy... |
Pbkdf2PasswordEncoder | PBKDF2 | 5d923b44a6d129f3ddf3e3c8... |
SCryptPasswordEncoder | SCrypt | $e0801$N8QxJj5YQnK7vjMp... |
Argon2PasswordEncoder | Argon2 | $argon2id$v=19$m=1024,t=... |
配置示例:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
3.2 多种认证方式
3.2.1 表单登录
http.formLogin()
.loginPage("/login") // 自定义登录页
.loginProcessingUrl("/auth") // 处理登录的URL
.usernameParameter("uname") // 用户名参数名
.passwordParameter("pwd") // 密码参数名
.defaultSuccessUrl("/home") // 登录成功跳转
.failureUrl("/login?error"); // 登录失败跳转
3.2.2 HTTP 基本认证
http.httpBasic()
.realmName("My App");
3.2.3 Remember-Me 认证
http.rememberMe()
.key("myAppKey") // 加密密钥
.tokenValiditySeconds(86400) // 有效期(秒)
.rememberMeParameter("remember"); // 参数名
3.3 多认证提供者
@Autowired
private CustomAuthenticationProvider customProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(customProvider)
.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(passwordEncoder());
}
自定义认证提供者:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
String username = auth.getName();
String password = auth.getCredentials().toString();
UserDetails user = userDetailsService.loadUserByUsername(username);
if (passwordEncoder.matches(password, user.getPassword())) {
return new UsernamePasswordAuthenticationToken(
username, password, user.getAuthorities());
}
throw new BadCredentialsException("认证失败");
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
四、授权机制详解
4.1 请求级别授权
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.antMatchers("/api/**").access("hasRole('API') or hasIpAddress('192.168.1.0/24')")
.antMatchers("/db/**").access("hasAuthority('DBA') and hasIpAddress('192.168.1.100')")
.anyRequest().authenticated();
匹配器类型:
antMatchers()
:Ant 风格路径regexMatchers()
:正则表达式mvcMatchers()
:Spring MVC 路径
4.2 方法级别安全
4.2.1 启用方法安全
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class MethodSecurityConfig {
// 配置...
}
4.2.2 常用注解
注解 | 示例 | 描述 |
---|---|---|
@PreAuthorize | @PreAuthorize("hasRole('ADMIN')") | 方法执行前检查 |
@PostAuthorize | @PostAuthorize("returnObject.owner == authentication.name") | 方法执行后检查 |
@Secured | @Secured("ROLE_ADMIN") | 简单角色检查 |
@RolesAllowed | @RolesAllowed("USER") | JSR-250 标准注解 |
@PreFilter | @PreFilter("filterObject.owner == authentication.name") | 过滤集合参数 |
@PostFilter | @PostFilter("filterObject.status == 'ACTIVE'") | 过滤返回值 |
4.3 动态权限控制
自定义权限评估器:
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(
Authentication auth, Object target, Object permission) {
if (auth == null || !(permission instanceof String)) {
return false;
}
// 实现自定义逻辑
return checkPermission(auth, target, (String) permission);
}
@Override
public boolean hasPermission(
Authentication auth, Serializable targetId,
String targetType, Object permission) {
// 实现基于ID的权限检查
return false;
}
}
注册评估器:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Autowired
private CustomPermissionEvaluator permissionEvaluator;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler handler =
new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
}
五、高级安全特性
5.1 CSRF 防护
默认配置:
http.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
禁用 CSRF(仅限无状态 API):
http.csrf().disable();
自定义配置:
http.csrf()
.ignoringAntMatchers("/api/**")
.csrfTokenRepository(new HttpSessionCsrfTokenRepository());
5.2 CORS 配置
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Arrays.asList("https://example.com"));
config.setAllowedMethods(Arrays.asList("GET", "POST"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
5.3 安全头信息
http.headers()
.contentSecurityPolicy("default-src 'self'")
.and()
.referrerPolicy(ReferrerPolicy.SAME_ORIGIN)
.and()
.frameOptions().sameOrigin()
.and()
.httpStrictTransportSecurity()
.includeSubDomains(true)
.maxAgeInSeconds(31536000);
5.4 会话管理
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/invalidSession")
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.expiredUrl("/sessionExpired");
会话策略:
ALWAYS
:总是创建会话IF_REQUIRED
:必要时创建(默认)NEVER
:不创建,但可能使用已有会话STATELESS
:完全无状态(如JWT)
六、OAuth2 与 JWT 集成
6.1 OAuth2 资源服务器
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("my-resource");
}
}
6.2 JWT 配置
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("my-secret-key");
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
6.3 OAuth2 客户端
@Configuration
@EnableOAuth2Client
public class OAuth2ClientConfig {
@Bean
public OAuth2RestTemplate oauth2RestTemplate(
OAuth2ClientContext context,
OAuth2ProtectedResourceDetails details) {
return new OAuth2RestTemplate(details, context);
}
}
七、测试与调试
7.1 测试安全配置
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SecurityTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser(username="user", roles="USER")
public void testUserAccess() throws Exception {
mockMvc.perform(get("/user/profile"))
.andExpect(status().isOk());
}
@Test
@WithAnonymousUser
public void testUnauthorizedAccess() throws Exception {
mockMvc.perform(get("/admin"))
.andExpect(status().isForbidden());
}
}
7.2 调试技巧
-
启用调试日志:
logging.level.org.springframework.security=DEBUG
-
自定义访问决策日志:
@Component public class SecurityLogger { private static final Logger log = LoggerFactory.getLogger(SecurityLogger.class); @PreAuthorize("hasRole('ADMIN')") public void adminOnly() { log.info("Admin method accessed"); } }
-
使用 SecurityContextHolder:
Authentication auth = SecurityContextHolder.getContext().getAuthentication(); log.info("Current user: {}", auth.getName());
八、最佳实践
8.1 安全配置建议
-
密码策略:
- 使用强密码编码器(BCrypt)
- 定期更新加密密钥
- 实现密码过期策略
-
API 安全:
- REST API 使用无状态认证(JWT)
- 实现速率限制
- 记录安全相关事件
-
生产环境配置:
http .requiresChannel() .anyRequest().requiresSecure() .and() .headers() .contentTypeOptions() .and() .xssProtection() .and() .cacheControl();
8.2 性能优化
-
缓存用户数据:
@Bean public UserDetailsService userDetailsService() { return new CachingUserDetailsService( new JdbcUserDetailsManager(dataSource)); }
-
优化会话存储:
- 使用 Redis 等分布式存储
- 配置合适的会话超时
-
减少过滤器链:
http.securityMatcher("/api/**"); // 仅对API路径应用安全
8.3 常见问题解决
-
循环依赖:
- 避免在
WebSecurityConfigurerAdapter
中直接注入服务 - 使用
@Lazy
注解
- 避免在
-
配置顺序问题:
- 确保
@Order
注解正确使用 - 重要配置放在高优先级
- 确保
-
跨域问题:
- 正确配置 CORS
- 确保安全过滤器在 CORS 过滤器之后
九、Spring Security 5 新特性
9.1 OAuth2 登录
http.oauth2Login()
.loginPage("/login")
.defaultSuccessUrl("/home")
.userInfoEndpoint()
.userService(customOAuth2UserService);
9.2 响应式安全
@EnableWebFluxSecurity
public class ReactiveSecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.pathMatchers("/admin/**").hasRole("ADMIN")
.anyExchange().authenticated()
.and()
.httpBasic()
.and()
.build();
}
}
9.3 密码编码迁移
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
十、总结
Spring Security 作为企业级安全框架,提供了从认证授权到攻击防护的全面解决方案。通过本文的学习,我们掌握了:
- 核心架构与基础配置
- 认证与授权机制
- 高级安全特性
- OAuth2 与 JWT 集成
- 测试与最佳实践
无论是简单的 Web 应用还是复杂的微服务架构,Spring Security 都能提供合适的安全解决方案。希望本指南能帮助你在项目中构建更加安全可靠的系统。
PS:如果你在学习过程中遇到问题,别担心!欢迎在评论区留言,我会尽力帮你解决!😄