多来源 token (前端、各个三方)的信任配置、统一验证、身份映射,以及 Spring Security 如何衔接授权检查。
HTTP 请求
→ Servlet 容器
→ DelegatingFilterProxy
→ FilterChainProxy:选择匹配的 SecurityFilterChain
→ BearerTokenAuthenticationFilter:提取 Bearer Token
→ AuthenticationManagerResolver:选择可信认证策略
→ AuthenticationManager(ProviderManager)
→ JwtAuthenticationProvider
→ JwtDecoder:验签并校验声明
→ 自定义身份转换器:状态检查、身份映射、权限计算
← 返回已认证的 Authentication
→ 将 Authentication 写入 SecurityContextHolder
→ AuthorizationFilter
→ AuthorizationManager:检查认证状态及接口权限
→ DispatcherServlet
→ Controller
→ Service:加载真实资源并检查租户、归属等权限
→ 业务处理
请求结束 → 框架清理 SecurityContext
spring security:
@Bean
SecurityFilterChain securityFilterChain(
HttpSecurity http,
AuthenticationManagerResolver<HttpServletRequest> resolver,
AuthorizationManager<RequestAuthorizationContext> authorizationManager) {
// 1、注册安全过滤器链,框架自动添加 Bearer Token 认证过滤器和授权过滤器
http.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.oauth2ResourceServer(resourceServer ->
resourceServer.authenticationManagerResolver(resolver));
http.authorizeHttpRequests(authorize ->
authorize.anyRequest().access(authorizationManager));
return http.build();
}
@Bean
AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver() {
Map<String, AuthenticationManager> managers = new HashMap<>();
// 启动时为已登记的可信策略创建认证管理器
for (TrustPolicy policy : trustedPolicies.loadAll()) {
managers.put(policy.id(), buildAuthenticationManager(policy));
}
return request -> {
// 2、认证过滤器调用此处,未验签信息仅用于选择已登记的可信策略
String token = bearerTokenResolver.resolve(request);
TokenHint hint = tokenReader.peekBounded(token);
TrustPolicy policy = trustedPolicies.requireMatch(request, hint);
return managers.get(policy.id());
};
}
AuthenticationManager buildAuthenticationManager(TrustPolicy policy) {
// 3、Provider 调用 Decoder,使用可信密钥验签并校验算法、iss、aud、类型、必要声明、exp 和 nbf
JwtDecoder decoder = token -> jwtVerifier.verify(token, policy);
JwtAuthenticationProvider provider = new JwtAuthenticationProvider(decoder);
provider.setJwtAuthenticationConverter(jwt -> {
// 4、验签通过后按策略检查撤销状态,凭证失效时抛出认证异常
tokenStatusValidator.validate(jwt, policy);
// 5、将已验证声明映射为统一身份,保留用户或服务类型、签发方、主体、租户及委托关系
Identity identity = identityMapper.mapVerified(jwt, policy);
identityValidator.validate(identity, policy);
// 6、计算有效权限,同时满足 Token 授权范围与主体当前权限约束
Set<GrantedAuthority> authorities = permissionResolver.intersect(
policy.mapGrantedAuthorities(jwt),
permissionService.loadAllowedAuthorities(identity));
// 7、返回已认证结果,由框架过滤器写入 SecurityContextHolder
return identityTokens.authenticated(identity, authorities);
});
return new ProviderManager(provider);
}
@Bean
AuthorizationManager<RequestAuthorizationContext> authorizationManager() {
return (authenticationSupplier, context) -> {
// 8、授权过滤器调用此处,从安全上下文取得身份并检查接口权限,匿名身份及未配置操作默认拒绝
Authentication authentication = authenticationSupplier.get();
boolean allowed = authorizationService.allowEndpoint(
authentication, context.getRequest());
return new AuthorizationDecision(allowed);
};
}
securityFilterChain() 在启动时完成注册;请求到达后,由框架过滤器调用 Resolver,再调用 Manager、Provider、Decoder 和身份转换器,Controller 无需手动启动这条链路。
认证失败由框架转换为 401,已认证但权限不足返回 403。依赖真实业务对象的资源权限,在 Service 加载资源后继续检查。token验证+身份解析:
void handleRequest(Request request, Response response, FilterChain chain) {
try {
// 第一步:从 Authorization: Bearer <token> 提取凭证
// 同时检查格式和长度,缺失或非法时抛出认证异常
String token = tokenExtractor.requireBearerToken(request);
// 第二步:从预先登记的可信配置中选择验证策略
// 未验签的 issuer、tokenType 仅用于策略查找,不能作为身份依据
TokenHint hint = tokenReader.peek(token);
TrustPolicy policy = trustedPolicies.requireMatch(request, hint);
// 第三步:验证签名及声明,全部通过后才能信任 Token 内容
// 包括:允许的算法、可信密钥、issuer、audience、类型、exp、nbf
VerifiedClaims claims = tokenVerifier.verify(token, policy);
// 第四步:按策略检查 Token 撤销状态
// 必需的状态检查无法完成时,终止请求
tokenStatusValidator.validate(claims, policy);
// 第五步:将不同 Token 的声明映射为统一身份,并检查主体状态
// 保留主体类型、issuer + subject、调用应用、租户和委托关系
Identity identity = identityMapper.map(claims, policy);
identityValidator.validate(identity, policy);
// 第六步:计算有效权限,同时满足 Token 授权与主体权限约束
Set<Permission> permissions = permissionResolver.intersect(
policy.mapGrantedPermissions(claims),
permissionService.loadAllowedPermissions(identity)
);
// 第七步:创建认证结果,写入请求级安全上下文
Authentication authentication =
authenticationFactory.authenticated(identity, permissions);
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
// 第八步:执行授权检查,通过后进入业务处理
// 检查接口操作权限、资源归属和租户边界
authorizationService.check(identity, permissions, request);
audit.record("AUTHENTICATION_SUCCEEDED");
chain.doFilter(request, response);
} catch (AuthenticationException e) {
audit.record("AUTHENTICATION_FAILED");
response.setHeader("WWW-Authenticate", "Bearer");
response.setStatus(401);
} catch (AccessDeniedException e) {
audit.record("AUTHORIZATION_DENIED");
response.setStatus(403);
} finally {
// 请求结束后清理身份上下文,避免线程复用导致身份串用
SecurityContextHolder.clearContext();
}
}