会话、认证与基础安全(零基础详细版)

目标:实现 Session 或 JWT 登录保护,配置 CORS,了解 CSRF、防弱口令与防重复提交。

← 返回一级入口

学习目标与前置

建议用时:60-90 分钟准备:已有 Spring Boot + Todo 接口

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:操作与验证

  1. 未登录访问 /api/profile,返回 401;登录后再次访问成功。
  2. 退出:session.invalidate() 或前端清理 token。
  3. 跨域:从 5173 端口调用 8080,未配置 CORS 前失败,配置后成功。

安全提示(基础)

常见问题与排查

课堂练习

课后巩固