Java Spring Security基础

Spring Security 是 Spring 生态中处理认证和授权的框架,功能强大但配置起来也确实有点绕。这篇记录核心概念和基本配置方式。

认证与授权

两个最基本的概念:

  • 认证(Authentication):你是谁?验证用户身份。用户名密码登录、OAuth 登录、API Key 验证都属于认证。
  • 授权(Authorization):你能做什么?验证用户是否有权限执行特定操作。基于角色(ROLE_ADMIN)或基于权限(READ_ARTICLE)。

Spring Security 的整体架构就是围绕这两件事展开的。

SecurityFilterChain

Spring Security 5.7+ 推荐使用 SecurityFilterChain Bean 来配置(替代之前继承 WebSecurityConfigurerAdapter 的方式):

核心配置通常包括:哪些路径需要认证、哪些路径公开、登录方式是什么、Session 策略是什么。

一个典型的配置会做这些事情:

  1. 放行公共路径(如 /api/public/**/login、静态资源)
  2. 要求管理接口有 ADMIN 角色
  3. 其他请求都需要认证
  4. 配置表单登录或 HTTP Basic 认证

SecurityFilterChain 本质上是一个 Servlet Filter 链,每个请求都会经过这条链上的多个 Filter,包括 UsernamePasswordAuthenticationFilterBasicAuthenticationFilterExceptionTranslationFilter 等。

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)更常见:

  1. 用户登录成功后,服务端生成一个 JWT Token 返回给前端
  2. Token 中包含用户信息和签名,有过期时间
  3. 前端每次请求在 Header 中携带 Token:Authorization: Bearer xxx
  4. 服务端验证 Token 签名和有效期,提取用户信息

JWT 的优点是无状态——服务端不需要存 Session,天然适合分布式部署。缺点是 Token 签发后无法主动撤销(除非引入黑名单机制,但那又变成有状态了)。

在 Spring Security 中集成 JWT 需要:

  1. 写一个 JWT 工具类来处理 Token 的生成和解析
  2. 自定义一个 Filter 在每次请求时从 Header 提取并验证 Token
  3. 验证通过后将用户信息放入 SecurityContext
  4. 在 SecurityFilterChain 中注册这个 Filter,并将 Session 策略设为 STATELESS

小结

Spring Security 的学习曲线确实陡,主要是因为它的 Filter 链机制、各种 Provider 和 Handler 的关系比较复杂。但核心思路就两点:谁来提供用户信息(UserDetailsService),以及怎么判断权限(配置规则 + 注解)。先把这两点搞清楚,其他的根据需求逐步深入就行。