学习目标与前置
建议用时:60-90 分钟准备:已有 Spring Boot + Todo 接口
- 实现 Session 登录与鉴权拦截。
- 实现 JWT 登录与无状态鉴权。
- 配置 CORS,理解 CSRF 与弱口令、重复提交风险。
Step 1:Session 版本
flowchart LR A[登录请求] --> B[校验账号密码] B -->|通过| C[写 Session: uid] C --> D[后续请求携带 SessionID] D --> E[拦截器检查 uid] --> F[放行访问资源] B -->|失败| G[返回 401]
record LoginReq(String username, String password) {}
@PostMapping("/api/login")
public ApiResp<Void> login(@RequestBody LoginReq req, HttpSession session) {
if ("admin".equals(req.username()) && "123456".equals(req.password())) {
session.setAttribute("uid", 1L);
return ApiResp.ok(null);
}
return ApiResp.err(401, "bad credentials");
}
@GetMapping("/api/profile")
public ApiResp<Map<String, Object>> profile(HttpSession session) {
Object uid = session.getAttribute("uid");
if (uid == null) return ApiResp.err(401, "unauthorized");
return ApiResp.ok(Map.of("uid", uid, "role", "admin"));
}
拦截器校验 Session:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
if (req.getRequestURI().startsWith("/api/login")) return true;
if (req.getSession().getAttribute("uid") == null) { res.setStatus(401); return false; }
return true;
}
});
}
Step 2:JWT 版本(示意)
flowchart LR A[登录请求] --> B[生成 JWT: uid+过期时间+签名] B --> C[返回 token 给前端] C --> D[前端放入 Authorization: Bearer xxx] D --> E[过滤器/拦截器解析校验签名] E -->|合法| F[设置 uid 上下文后放行] E -->|非法/过期| G[返回 401]
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
String token = Jwts.builder()
.setSubject("1") // uid
.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
.signWith(SignatureAlgorithm.HS256, "secret")
.compact();
// 拦截器/过滤器中校验
String header = req.getHeader("Authorization");
if (header == null || !header.startsWith("Bearer ")) { res.setStatus(401); return false; }
String jwt = header.substring(7);
// 解析校验签名,取出 uid,放入上下文
说明:JWT 适合无状态;要设置过期时间与刷新策略,密钥务必保密。
Step 3:配置 CORS
flowchart LR A[前端 http://localhost:5173] -->|预检 OPTIONS| B[后端 8080] B --> C[检查允许的 Origin/Methods/Headers] C -->|通过| D[正常处理实际请求] C -->|不通过| E[浏览器拦截 CORS 错误]
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:5173")
.allowedMethods("GET","POST","PUT","DELETE")
.allowCredentials(true);
}
}
说明:前端端口不同会触发跨域,需设置允许的来源、方法、是否带凭证。
Step 4:操作与验证
- 未登录访问 /api/profile,返回 401;登录后再次访问成功。
- 退出:
session.invalidate()或前端清理 token。 - 跨域:从 5173 端口调用 8080,未配置 CORS 前失败,配置后成功。
安全提示(基础)
- 密码存储:使用 BCrypt,不要明文。
- 防重复提交:前端按钮禁用 + 后端幂等检查(唯一索引/幂等键)。
- 弱口令:登录失败次数限制(如 5 次锁定 5 分钟)。
- CSRF:Cookie + Session 跨站时需 CSRF Token(本课不展开)。
常见问题与排查
- 401 一直返回:检查拦截器放行的路径;确认登录后写入 Session 或生成 token。
- CORS 错误:确保 allowedOrigins/Methods 正确,且前端请求携带的头在允许列表内。
- JWT 解析失败:确认签名算法与密钥一致;检查过期时间。
课堂练习
- 为 Todo 增删接口增加登录校验。
- 实现“滑动过期”:Session/JWT 30 分钟有效,访问时刷新过期时间。
课后巩固
- 接入 BCrypt 校验密码,记录失败次数,超过 5 次锁定 5 分钟。
- 撰写对比:Session 与 JWT 的优缺点与适用场景。