跳到主要内容

认证授权系统设计

问题

如何设计一个统一的认证授权系统?

答案

认证方案对比

方案原理优点缺点
Session服务端存储会话安全、可控分布式需共享 Session
JWT客户端存储 Token无状态、跨服务无法主动失效
OAuth 2.0授权框架标准、第三方登录流程复杂

JWT + 双 Token 方案

JWT 生成与验证
@Service
public class JwtService {
@Value("${jwt.secret}")
private String secret;

public String generateAccessToken(Long userId, List<String> roles) {
return Jwts.builder()
.setSubject(String.valueOf(userId))
.claim("roles", roles)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
.signWith(Keys.hmacShaKeyFor(secret.getBytes()), SignatureAlgorithm.HS256)
.compact();
}

public Claims parseToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secret.getBytes()))
.build()
.parseClaimsJws(token)
.getBody();
}
}

RBAC 权限模型

Spring Security 权限校验
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}

@PreAuthorize("hasAuthority('order:export')")
@GetMapping("/orders/export")
public void exportOrders(HttpServletResponse response) {
orderService.export(response);
}

网关统一鉴权

Spring Cloud Gateway 鉴权过滤器
@Component
public class AuthFilter implements GlobalFilter, Ordered {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");

// 白名单路径不鉴权
if (isWhiteListed(exchange.getRequest().getPath().toString())) {
return chain.filter(exchange);
}

if (token == null || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}

try {
Claims claims = jwtService.parseToken(token.substring(7));
// 将用户信息放入请求头,传递给下游服务
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-User-Id", claims.getSubject())
.header("X-User-Roles", String.join(",", claims.get("roles", List.class)))
.build();
return chain.filter(exchange.mutate().request(request).build());
} catch (Exception e) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}

@Override
public int getOrder() { return -100; }
}

常见面试问题

Q1: JWT 如何实现主动失效(踢人下线)?

答案

  • 黑名单:将失效的 JWT 放入 Redis 黑名单,每次校验时查黑名单
  • 版本号:Redis 存用户 Token 版本号,JWT 中带版本号,不匹配则拒绝
  • Access Token 短有效期(30 分钟)可减少黑名单数量

Q2: 如何防止 Token 泄露?

答案

  • Access Token 短有效期
  • Refresh Token 仅在刷新接口使用,且绑定设备指纹
  • HTTPS 传输
  • HttpOnly Cookie 存储(防 XSS 窃取)

Q3: 微服务间如何传递用户信息?

答案

网关验证 Token 后,将用户 ID、角色等信息放入请求头(X-User-Id),下游服务直接从请求头获取,无需重复验签。

相关链接