一、核心概念
Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架,用于保护基于 Spring 的应用程序。
1.1 核心组件
组件 | 说明 |
---|---|
AuthenticationManager |
身份验证管理器,负责处理认证请求 |
UserDetailsService |
加载用户特定数据的核心接口 |
PasswordEncoder |
密码编码器,用于密码加密与验证 |
SecurityFilterChain |
安全过滤器链,定义请求的处理流程 |
GrantedAuthority |
用户权限(角色/权限) |
Authentication |
当前用户的认证信息 |
SecurityContext / SecurityContextHolder |
存储当前安全上下文(包含认证信息) |
1.2 关键术语
- Authentication(认证):确认“你是谁”。
- Authorization(授权):决定“你能做什么”。
- Principal(主体):当前用户。
- Credentials(凭证):如密码、Token。
- Authorities(权限):用户拥有的权限(如
ROLE_ADMIN
,READ_USER
)。
二、详细操作步骤(适合快速上手)
步骤 1:创建 Spring Boot 项目
使用 start.spring.io 或 IDE 创建项目,添加以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 可选:数据库支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
步骤 2:配置基本安全配置类
创建配置类 SecurityConfig.java
:
package com.example.demo.config;
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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity // 启用 Spring Security
public class SecurityConfig {
// 定义安全过滤器链
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll() // 公共路径放行
.requestMatchers("/admin/**").hasRole("ADMIN") // /admin 需 ADMIN 角色
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN") // USER 或 ADMIN
.anyRequest().authenticated() // 其他所有请求都需要认证
)
.formLogin(form -> form
.loginPage("/login") // 自定义登录页路径
.loginProcessingUrl("/doLogin") // 登录表单提交地址
.defaultSuccessUrl("/home", true) // 登录成功后跳转
.permitAll() // 登录页所有人可访问
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
)
.csrf().disable(); // 开发阶段可关闭 CSRF(生产需开启)
return http.build();
}
// 用户详情服务:内存用户
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
// 密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
步骤 3:创建控制器测试
package com.example.demo.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/public/hello")
public String hello() {
return "Hello, Public!";
}
@GetMapping("/user/profile")
public String userProfile() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return "Hello, " + auth.getName() + "! You are logged in.";
}
@GetMapping("/admin/dashboard")
public String adminDashboard() {
return "Welcome to Admin Dashboard!";
}
@GetMapping("/home")
public String home() {
return "Welcome Home!";
}
@GetMapping("/login")
public String loginPage() {
return "Please login";
}
}
步骤 4:运行并测试
- 启动应用。
- 访问
http://localhost:8080/public/hello
→ 直接访问。 - 访问
http://localhost:8080/user/profile
→ 跳转到登录页。 - 使用用户名
user
密码password
登录。 - 访问
/admin/dashboard
→ 拒绝访问(user 无 ADMIN 角色)。 - 使用
admin/admin
登录后可访问。
三、常见错误与解决方案
错误 | 原因 | 解决方案 |
---|---|---|
There is no PasswordEncoder mapped for the id "null" |
未配置 PasswordEncoder 或密码未编码 |
确保 UserDetailsService 返回的密码已用 PasswordEncoder 编码 |
403 Forbidden | 权限不足或 CSRF 阻止 | 检查角色/权限配置;开发时可 http.csrf().disable() |
登录失败但无错误信息 | 登录处理路径不匹配 | 检查 loginProcessingUrl 是否与表单 action 一致 |
循环重定向 | 安全配置不当 | 检查 permitAll() 是否包含登录页、静态资源等 |
@PreAuthorize 不生效 |
未启用方法级安全 | 添加 @EnableMethodSecurity |
四、注意事项
- 永远不要在生产环境关闭 CSRF:
http.csrf().disable()
仅用于开发测试。 - 密码必须加密存储:使用
BCryptPasswordEncoder
、SCryptPasswordEncoder
等。 - 避免内存用户用于生产:应使用数据库、LDAP 或 OAuth2。
- 角色前缀:Spring Security 默认角色需以
ROLE_
开头(如hasRole("ADMIN")
实际检查ROLE_ADMIN
)。 - Session 管理:合理配置会话超时、并发控制等。
五、使用技巧
5.1 获取当前用户信息
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
5.2 方法级安全
@Configuration
@EnableMethodSecurity // 启用方法安全注解
public class MethodSecurityConfig {
}
// 在 Service 或 Controller 方法上使用
@PreAuthorize("hasRole('ADMIN')")
public void deleteUserData() { ... }
@PostAuthorize("returnObject.owner == authentication.name")
public UserData getUserData(Long id) { ... }
5.3 自定义登录成功/失败处理器
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.formLogin(form -> form
.successHandler(myAuthenticationSuccessHandler())
.failureHandler(myAuthenticationFailureHandler())
);
return http.build();
}
@Bean
public AuthenticationSuccessHandler myAuthenticationSuccessHandler() {
return (request, response, authentication) -> {
// 自定义逻辑,如记录日志、重定向
response.sendRedirect("/custom-home");
};
}
六、最佳实践
- 最小权限原则:只授予必要的权限。
- 使用 HTTPS:保护传输中的凭证。
- 定期轮换密钥/密码:增强安全性。
- 日志审计:记录登录、权限变更等关键操作。
- 使用 OAuth2 / OpenID Connect:对于现代应用,优先考虑第三方认证。
- 分离配置:将安全配置与业务逻辑分离。
七、性能优化
- 缓存用户信息:对于数据库用户,使用
Spring Cache
缓存UserDetailsService
结果。 - 减少权限检查开销:
- 避免在循环内进行
@PreAuthorize
。 - 使用角色继承(
RoleHierarchy
)减少重复检查。
- 避免在循环内进行
- Session 优化:
- 对于无状态 API,使用 JWT 代替 Session。
- 配置合理的 Session 超时时间。
- 异步认证:对于耗时认证逻辑(如远程调用),考虑异步处理(需谨慎)。
八、扩展:JWT 集成(无状态认证)
适用于前后端分离、微服务架构。
- 添加依赖:
jjwt-api
,jjwt-impl
,jjwt-jackson
。 - 创建
JwtUtil
工具类生成/解析 Token。 - 自定义
OncePerRequestFilter
拦截请求验证 JWT。 - 配置
SecurityFilterChain
放行登录路径,添加 JWT 过滤器。
示例代码较长,此处略,可按需提供。
总结
通过以上步骤,你已掌握 Spring Boot 集成 Spring Security 的核心知识。从基础配置到高级技巧,结合最佳实践,可快速构建安全可靠的应用。
下一步建议:
- 集成数据库用户(
JpaUserDetailsManager
)。 - 实现 OAuth2 登录(如 GitHub、微信)。
- 学习 Spring Security OAuth2 Resource Server(保护 REST API)。
本文档基于 Spring Boot 3.x + Spring Security 6.x 编写。