Spring Security 是 Spring 生态中处理认证和授权的框架,功能强大但配置起来也确实有点绕。这篇记录核心概念和基本配置方式。
认证与授权
两个最基本的概念:
- 认证(Authentication):你是谁?验证用户身份。用户名密码登录、OAuth 登录、API Key 验证都属于认证。
- 授权(Authorization):你能做什么?验证用户是否有权限执行特定操作。基于角色(ROLE_ADMIN)或基于权限(READ_ARTICLE)。
Spring Security 的整体架构就是围绕这两件事展开的。
SecurityFilterChain
Spring Security 5.7+ 推荐使用 SecurityFilterChain Bean 来配置(替代之前继承 WebSecurityConfigurerAdapter 的方式):
核心配置通常包括:哪些路径需要认证、哪些路径公开、登录方式是什么、Session 策略是什么。
一个典型的配置会做这些事情:
- 放行公共路径(如
/api/public/**、/login、静态资源) - 要求管理接口有 ADMIN 角色
- 其他请求都需要认证
- 配置表单登录或 HTTP Basic 认证
SecurityFilterChain 本质上是一个 Servlet Filter 链,每个请求都会经过这条链上的多个 Filter,包括 UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter、ExceptionTranslationFilter 等。
UserDetailsService
这是 Spring Security 获取用户信息的核心接口,只有一个方法:根据用户名加载用户。
实现这个接口时,通常是从数据库查询用户,然后构造一个包含用户名、密码、角色列表的对象返回给框架。Spring Security 会用返回的信息去做密码比对和权限判断。
如果你用 JPA,一般会有一个 User 实体类,然后在 UserDetailsService 实现中通过 Repository 查询。
密码编码器
永远不要明文存储密码。 Spring Security 提供了 PasswordEncoder 接口,推荐使用 BCryptPasswordEncoder。
BCrypt 的特点是每次加密同一个密码都会产生不同的哈希值(因为包含随机 salt),而且可以通过调整 strength 参数控制计算成本,抵御暴力破解。
配置密码编码器后,注册用户时用它来加密密码存入数据库,登录时框架会自动用同一个编码器来验证密码是否匹配。
基于角色的访问控制
Spring Security 中角色就是带 ROLE_ 前缀的权限字符串。在配置中可以按角色限制访问,也可以在 Controller 方法上用注解做更细粒度的控制。
常用注解包括:
@PreAuthorize("hasRole('ADMIN')")— 方法调用前检查@PostAuthorize— 方法调用后检查(可以根据返回值判断)@Secured("ROLE_USER")— 简化版,只支持角色检查
使用方法级别的注解需要在配置类上启用 @EnableMethodSecurity。
实际项目中角色和权限往往是分开的——角色是权限的集合。一个 ADMIN 角色可能拥有 CREATE_USER、DELETE_USER、VIEW_REPORTS 等多个权限。
JWT 简介
在前后端分离的项目中,Session 方案不太适用,JWT(JSON Web Token)更常见:
- 用户登录成功后,服务端生成一个 JWT Token 返回给前端
- Token 中包含用户信息和签名,有过期时间
- 前端每次请求在 Header 中携带 Token:
Authorization: Bearer xxx - 服务端验证 Token 签名和有效期,提取用户信息
JWT 的优点是无状态——服务端不需要存 Session,天然适合分布式部署。缺点是 Token 签发后无法主动撤销(除非引入黑名单机制,但那又变成有状态了)。
在 Spring Security 中集成 JWT 需要:
- 写一个 JWT 工具类来处理 Token 的生成和解析
- 自定义一个 Filter 在每次请求时从 Header 提取并验证 Token
- 验证通过后将用户信息放入 SecurityContext
- 在 SecurityFilterChain 中注册这个 Filter,并将 Session 策略设为 STATELESS
小结
Spring Security 的学习曲线确实陡,主要是因为它的 Filter 链机制、各种 Provider 和 Handler 的关系比较复杂。但核心思路就两点:谁来提供用户信息(UserDetailsService),以及怎么判断权限(配置规则 + 注解)。先把这两点搞清楚,其他的根据需求逐步深入就行。