
本文详解如何在 Spring Cloud 微服务架构中正确配置 API 网关与微服务两级安全策略,解决 /public/users/** 等端点被错误拦截导致 401 错误的问题,核心在于分离认证(网关层)与授权(微服务层)职责。
本文详解如何在 spring cloud 微服务架构中正确配置 api 网关与微服务两级安全策略,解决 `/public/users/**` 等端点被错误拦截导致 401 错误的问题,核心在于分离认证(网关层)与授权(微服务层)职责。
在基于 Spring Cloud 的微服务系统中,常见误区是将认证(Authentication)与授权(Authorization)混在同一层级处理,导致公共接口因网关未放行或微服务重复校验而返回 401 Unauthorized。根本解法在于遵循网关负责认证、微服务专注授权的设计原则。
✅ 正确分层安全策略
-
API 网关层(认证入口):仅验证 JWT 有效性(如签名校验、过期时间),对明确标记为“无需登录”的路径(如
/user-service/v0.1-dev/public/**)直接放行,不触发 OAuth2 Resource Server 校验。 -
微服务层(细粒度授权):信任网关已认证的请求,仅依据角色(如
ADMIN、SUBSCRIBER)或路径规则控制资源访问权限,/public/users/**在此层通过.permitAll()显式豁免。
? 关键配置修正
▶️ API 网关(Reactive Security)
@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity security) {
return security
.csrf().disable()
.authorizeExchange(exchange -> exchange
.pathMatchers("/eureka/**").permitAll() // Eureka 健康检查
.pathMatchers("/user-service/v0.1-dev/**").permitAll() // 全量放行用户服务路径
.anyExchange().authenticated()) // 其他路径需认证
.oauth2ResourceServer(spec -> spec.jwt()) // 仅对非 permitAll 路径校验 JWT
.build();
}
}⚠️ 注意:
/user-service/v0.1-dev/**必须精确匹配路由前缀(含版本号),且置于anyExchange().authenticated()之前,否则后续规则会覆盖放行逻辑。
▶️ 用户微服务(Servlet Security)
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfiguration {
private final AuthenticationConverter authenticationConverter;
public SecurityConfiguration(AuthenticationConverter authenticationConverter) {
this.authenticationConverter = authenticationConverter;
}
@Bean
public SecurityFilterChain securedSecurityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable() // REST API 通常禁用 CSRF
.authorizeHttpRequests(request -> request
.requestMatchers("/public/users/**").permitAll() // ✅ 公共写操作:PUT/POST/DELETE 均放行
.requestMatchers("/auth/admins/users/**").hasRole("ADMIN")
.requestMatchers("/auth/subscribers/users/**").hasRole("SUBSCRIBER")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwtAuthenticationConverter(authenticationConverter))); // 仅作用于 authenticated 请求
return http.build();
}
}? 提示:
.permitAll()已隐式允许所有 HTTP 方法(GET/POST/PUT/DELETE 等),无需额外配置HttpMethod列表。
? 常见错误排查清单
- ❌ 网关
pathMatchers顺序错误:permitAll()规则必须在anyExchange().authenticated()之前声明,否则被兜底规则拦截。 - ❌ 微服务路径与网关路由不一致:确保网关路由
/user-service/v0.1-dev/public/users/**与微服务内实际处理路径/public/users/**语义对齐(微服务接收时已剥离前缀)。 - ❌ Keycloak Token 未正确传递:确认网关转发请求时保留
Authorization: Bearer <token></token>头;若网关做了 token 解析再重写,需确保Authorization头未被意外移除。 - ❌ 混淆
@PermitAll注解:Spring Security 的@PermitAll(来自jakarta.annotation)不适用于方法级安全(需配合@EnableMethodSecurity),但本场景应优先使用 URL 级配置,避免注解与过滤链冲突。
✅ 最佳实践总结
| 层级 | 职责 | 推荐实现方式 |
|---|---|---|
| API 网关 | 全局认证、流量入口 |
permitAll() 放行公共路径;JWT 校验其余路径 |
| 微服务 | 业务级授权、角色控制 |
requestMatchers().permitAll() / .hasRole() 细粒度控制 |
| 关键原则 | 认证与授权分离 | 网关不解析角色,微服务不重复校验 token 签名 |
通过上述配置,/user-service/v0.1-dev/public/users/register 等端点将绕过网关认证,并在微服务层被 permitAll() 直接放行,彻底解决 401 Unauthorized 问题。

















