Spring Security 登录认证
这一章我们站在博客项目的视角,理解“登录到底是怎么走通的”。
博客后台登录的目标
后台管理系统通常需要这些能力:
- 用户输入账号密码。
- 后端校验身份。
- 登录成功后发放凭证。
- 后续请求带着凭证访问接口。
- 后端识别并恢复当前用户身份。
传统 Session 登录流程
mermaid
flowchart TD
A[用户提交用户名密码] --> B[后端校验账号密码]
B --> C[认证成功]
C --> D[服务端保存 Session]
D --> E[浏览器保存 SessionId]
E --> F[后续请求自动带 Cookie]
F --> G[服务端恢复用户身份]这种方式简单直观,但前后端分离项目里往往更常见 Token/JWT。
Token 登录流程
mermaid
flowchart TD
A[用户登录] --> B[服务端认证成功]
B --> C[生成 Token]
C --> D[返回给前端]
D --> E[前端保存 Token]
E --> F[请求头携带 Token]
F --> G[过滤器解析 Token]
G --> H[恢复用户身份]
H --> I[放入 SecurityContext]为什么现在很多项目选 Token
因为前后端分离场景里,Token 更适合:
- App。
- 小程序。
- 管理后台。
- 网关统一鉴权。
它不像 Session 那样强依赖服务端会话存储。
用户密码为什么不能明文存储
如果密码明文入库,一旦数据库泄露,风险非常大。
正确做法通常是:
- 前端传明文密码到后端。
- 后端用密码编码器加密后和数据库比对。
- 数据库保存的是加密结果,而不是原密码。
Spring Security 常用 PasswordEncoder 处理这件事。
典型登录流程拆解
第一步:接收登录请求
用户提交:
- 用户名。
- 密码。
第二步:构建认证对象
Spring Security 会把这些信息封装成一个待认证对象。
第三步:查询用户
通过 UserDetailsService 去数据库查:
- 用户是否存在。
- 用户是否被禁用。
- 用户密码密文。
- 用户角色和权限。
第四步:密码校验
用 PasswordEncoder.matches(...) 对比输入密码和数据库密文。
第五步:认证成功
认证成功后生成已认证对象,并写入安全上下文。
第六步:返回凭证
如果是 Token 模式,就把 Token 返回给前端。
自定义登录在博客项目里常见的改造点
- 登录接口改成
/api/admin/login。 - 认证成功后返回 JSON,而不是跳转页面。
- 认证失败后返回统一错误信息。
- 使用 JWT 或自定义 Token。
- 配合 Redis 实现登录状态管理。
JWT 的作用要怎么理解
JWT 可以理解成一个“带签名的身份说明书”。
它通常会记录:
- 用户 id。
- 用户名。
- 过期时间。
- 签发时间。
服务端收到 Token 后,可以校验签名和过期时间,再恢复出当前用户身份。
JWT 不是万能的
很多初学者会误以为“用了 JWT 就彻底不用管登录状态了”,其实不是。
JWT 的常见问题包括:
- 一旦签发,天然不方便立刻作废。
- 权限变化后,旧 Token 可能还有效。
- 退出登录通常要配合黑名单或 Redis。
所以真实项目里常见组合是:
- JWT 负责身份载体。
- Redis 负责状态控制。
过滤器为什么这么重要
因为后续每个请求都要靠过滤器解析 Token。
典型流程是:
- 从请求头取 Token。
- 校验 Token。
- 解析出用户 id。
- 查 Redis 或查数据库。
- 构造认证对象。
- 放入
SecurityContextHolder。
这样 Controller 层才知道当前用户是谁。
登录相关的典型异常
- 用户不存在。
- 密码错误。
- 用户被禁用。
- Token 为空。
- Token 过期。
- Token 非法或签名错误。
这些最好不要直接把底层异常原样抛给前端,而是转换成更明确的业务提示。
博客项目里的推荐做法
- 登录接口单独放开,不做拦截。
- 后台管理接口默认需要登录。
- 前台公开接口只对敏感操作做鉴权。
- 密码统一加密。
- Token 配合过期时间和刷新策略。
代码 Demo:登录接口
java
@RestController
@RequestMapping("/api/admin")
public class LoginController {
private final AuthenticationManager authenticationManager;
private final JwtTokenService jwtTokenService;
@PostMapping("/login")
public LoginResponse login(@RequestBody LoginRequest request) {
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(request.username(), request.password());
Authentication authentication = authenticationManager.authenticate(token);
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
String accessToken = jwtTokenService.createAccessToken(loginUser.getUserId());
return new LoginResponse(accessToken);
}
}密码加密:
java
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}保存用户时不要存明文密码:
java
user.setPassword(passwordEncoder.encode(rawPassword));本章小结
登录认证本质上是三件事:
- 验证身份真假。
- 生成后续访问凭证。
- 在每次请求中恢复当前用户身份。
只要你把这三步连起来看,Spring Security 的登录流程就不会再显得绕。
