Spring Cloud Zuul API 网关不会为无状态会话转发 JWT 令牌

Posted

技术标签:

【中文标题】Spring Cloud Zuul API 网关不会为无状态会话转发 JWT 令牌【英文标题】:Spring Cloud Zuul API gateway doesn't forward JWT token for stateless sessions 【发布时间】:2018-02-16 06:19:19 【问题描述】:

我正在尝试使用 Spring Boot 1.5.6.RELEASESpring Cloud Dalston.SR3 来实现微服务架构后端,这将被移动/网络端点使用。

API 网关应用程序

@SpringBootApplicatio
@EnableEurekaClient
@EnableZuulProxy
public class GatewayApplication 

    public static void main(String[] args) 
         SpringApplication.run(GatewayApplication.class, args);
    

API 安全

@Configuration
@EnableWebSecurity
@Order(ManagementServerProperties.ACCESS_OVERRIDE_ORDER)
@EnableOAuth2Sso
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Override
    public void configure(HttpSecurity http) throws Exception 

        // @formatter:off
        http
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
                .authorizeRequests()
                    .antMatchers("/sign-up", "/login")
                        .permitAll()
                .anyRequest()
                    .authenticated()
            .and()
                .csrf()
                    .ignoringAntMatchers("/sign-up", "/login")
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
        // @formatter:on
    

Gradle 安全相关依赖项

   // Spring OAuth2 security
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.springframework.security.oauth:spring-security-oauth2")
    compile("org.springframework.cloud:spring-cloud-starter-oauth2")
    compile("org.springframework.security:spring-security-jwt")

祖尔路线

zuul:
  ignoredServices: '*'
  routes:
    user-service:
      path: /user-service/**
      stripPrefix: false
      serviceId: user-webservice
      sensitiveHeaders:
    task-service:
      path: /task-service/**
      stripPrefix: false
      serviceId: task-webservice
      sensitiveHeaders:
    user:
      path: /userauth/**
      stripPrefix: false
      serviceId: auth-server
      sensitiveHeaders:

我能够从授权服务器获取访问令牌(无状态会话 - 无 JSESSIONID cookie)

curl -D - --request POST -u acme:acmesecret "http://localhost:8899/userauth/oauth/token?grant_type=password&username=<...>&password=<...>"

“ACCESS_TOKEN”:“eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDQ3ODg4NzgsInVzZXJfbmFtZSI6IjcyMTk2MTk2NDEiLCJhdXRob3JpdGllcyI6WyJST0xFX1BBVElFTlQiXSwianRpIjoiZThhMzBjNmQtZjA2MS00MWEzLWEyZGItYTZiN2ZjYTI5ODk1IiwiY2xpZW50X2lkIjoiYWNtZSIsInNjb3BlIjpbIm9wZW5pZCJdfQ.AhF_kqfsRYM1t1HVT ........ P>

我可以使用访问令牌向授权服务器或其他资源请求数据

curl -D - --request GET -H "授权:承载 eyJhbGciOiJSUzI1...." http://localhost:8899/userauth/me

"authorities":["authority":"ROLE_P.........

curl -D - --request GET -H "授权:承载 eyJhbGciOiJSUzI1NiIsInR5......." http://localhost:8081/user-service/

["firstName":"Anil".....]

但是,对于通过 API 网关路由的相同请求,它会在网关本身失败并被过滤为 AnonymousAuthenticationToken。

curl -D - --request GET -H "授权:承载 eyJhbGciOiJSUzI1...." http://localhost:8765/user-service/

HTTP/1.1 302 设置 Cookie: XSRF-TOKEN=b5a1c34e-e83c-47ea-86a6-13a237c027d4;路径=/ 位置: http://localhost:8765/login

我假设使用@EnableZuulProxy@EnableOAuth2Sso,Zuul 会小心地将不记名令牌转发给下游服务,但事实并非如此。我已经有一个使用 HTTP 会话和浏览器重定向来让 API 网关传递令牌的工作示例 - https://github.com/anilallewar/microservices-basics-spring-boot

但我很难让它与无状态会话一起工作,Zuul API 网关端可能缺少什么指针?

【问题讨论】:

你有这个工作吗?我被困在同一个问题上。你能解释一下吗? 【参考方案1】:

我假设使用@EnableZuulProxy 和@EnableOAuth2Sso,Zuul 会小心地将不记名令牌转发给下游服务,但事实并非如此。

我假设同样的事情,但在我(痛苦的)经验中,@EnableOAuth2Sso 使用 SSO 保护所有端点,甚至阻止带有承载令牌的请求进入下游服务。我必须更改我的网关以禁用通向我的资源的路由上的身份验证,以便带有不记名令牌的请求可以通过。

尝试将/user-service/**/task-service/** 添加到您的permitAll() 匹配器中:

@Override
public void configure(HttpSecurity http) throws Exception 

    // @formatter:off
    http
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
            .authorizeRequests()
                .antMatchers("/sign-up", "/login", "/task-service/**", "/user-service/**")
                    .permitAll()
            .anyRequest()
                .authenticated()
        .and()
            .csrf()
                .ignoringAntMatchers("/sign-up", "/login")
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    // @formatter:on

【讨论】:

谢谢亚历克斯。这拯救了我的一天。是的,我还注意到我必须使用 @EnableOAuth2Sso 授权对每个微服务的请求。我仍然可以在 Spring Boot 2.0 中注意到这一点。【参考方案2】:

Zuul 默认将 Authorization 标头视为敏感标头,不会将其传递给下游请求。要覆盖这一点,您可以在 Zuul 配置中全局修改 sensitiveHeaders(适用于所有路由):

zuul:
  # exclude Authorization from sensitive headers
  sensitiveHeaders: Cookie,Set-Cookie
  ignoredServices: '*'

或针对特定路线:

zuul:
  ignoredServices: '*'
  routes:
    user-service:
      path: /user-service/**
      stripPrefix: false
      serviceId: user-webservice
      # exclude Authorization from sensitive headers
      sensitiveHeaders: Cookie,Set-Cookie

要了解有关该问题的更多信息,请查看以下问题:

Authorization header not passed by ZuulProxy starting with Brixton.RC1

【讨论】:

如果您查看原始问题,敏感标题已设置为空(sensitiveHeaders:)。这绝对不是问题。

以上是关于Spring Cloud Zuul API 网关不会为无状态会话转发 JWT 令牌的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud:API网关服务——Spring Cloud Zuul

基于spring-cloud的微服务API网关zuul

Spring Cloud Zuul 作为 API 网关

干货分享微服务spring-cloud(6.Api网关服务zuul)

网关 zuul 与 spring-cloud gateway的区别

Spring Cloud入门教程 - Zuul实现API网关和请求过滤