Vue+Spring Cloud Gateway+Keycloak架构请求401问题求助
解决Spring Cloud Gateway作为OAuth2资源服务器时的401问题
以下是几个排查方向和具体解决步骤:
1. 核对网关的OAuth2资源服务器配置
先检查网关的application.yml配置,确保Keycloak的端点、受众与实际一致:
spring: security: oauth2: resourceserver: jwt: issuer-uri: http://你的Keycloak地址/realms/你的领域名 jwk-set-uri: http://你的Keycloak地址/realms/你的领域名/protocol/openid-connect/certs audience: 你的SPA客户端ID # 必须和Keycloak里的客户端ID完全匹配
注意Keycloak 22的issuer URI已移除/auth前缀,别用旧格式,否则会导致令牌校验失败。
2. 让网关转发令牌给微服务
网关校验完令牌后,必须把原始令牌转发给下游微服务,否则微服务拿不到合法凭证。写一个全局过滤器即可:
import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.http.HttpHeaders; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class TokenForwardFilter implements GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return ReactiveSecurityContextHolder.getContext() .map(ctx -> ctx.getAuthentication()) .filter(auth -> auth != null && auth.getCredentials() instanceof Jwt) .map(Authentication::getCredentials) .cast(Jwt.class) .doOnNext(jwt -> { // 将原始Bearer令牌添加到请求头转发给微服务 exchange.getRequest().mutate() .header(HttpHeaders.AUTHORIZATION, "Bearer " + jwt.getTokenValue()) .build(); }) .then(chain.filter(exchange)); } }
微服务端无需添加任何Keycloak或OAuth2依赖,直接处理请求即可。
3. 检查网关路由配置
确保路由规则未修改或移除Authorization头,示例配置如下:
spring: cloud: gateway: routes: - id: 微服务路由ID uri: lb://你的微服务名称 # 或直接填写微服务HTTP地址 predicates: - Path=/你的微服务路径/** filters: - StripPrefix=1 # 按需配置,不需要可删除
如果路由中有自定义过滤器,检查是否误删了Authorization头。
4. 修复跨域(CORS)配置
Vue是跨域请求,网关必须允许携带Authorization头,否则浏览器会拦截请求。添加CORS配置类:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import java.util.List; @Configuration public class CorsConfig { @Bean public CorsWebFilter corsWebFilter() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(List.of("http://你的Vue应用地址:端口")); config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowedHeaders(List.of("*")); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } }
注意AllowedOrigins不要用*,否则AllowCredentials设为true会无效,必须指定具体的Vue地址。
5. 检查Keycloak客户端配置
确认Keycloak里的SPA客户端设置正确:
- 访问类型选择
public(SPA无法存储密钥) - 有效重定向URI、Web Origins需包含Vue的地址
- 启用
Authorization Code Flow with PKCE(SPA推荐使用该流程,避免隐式流) - 令牌的受众(aud)需包含网关配置里的
audience值
6. 校验令牌本身
用Keycloak的令牌 introspect 端点检查令牌有效性:
发送POST请求到http://你的Keycloak地址/realms/你的领域名/protocol/openid-connect/token/introspect,参数携带token=你的Bearer令牌和client_id=你的网关客户端ID(若网关为confidential客户端,还需携带client_secret),查看返回的active是否为true,以及aud、iss等字段是否与网关配置匹配。
内容的提问来源于stack exchange,提问作者Nicenick
相关产品推荐
相关产品推荐

