Spring zuul 代理不接受不记名令牌

Spring zuul proxy not accepting bearer token

我有一个 zuul 代理 (http://localhost:8765) serving an angular web app (http://localhost:8080/app). Behind the zuul proxy there is also an oauth2 server (http://localhost:8899)。

网络资源在http://localhost:8765/web and the resources are proxied under http://localhost:8765/api下代理。 Zuul 代理无需身份验证即可提供静态 Web 资源。因此,第一次身份验证是通过 JSON 调用 (GET /api/user) 完成的,当然会因 401.

而失败

现在我将页面转发到“http://localhost:8899/uaa/oauth/authorize?response_type=token&client_id=web&redirect_uri=http://localhost:8765/web/index.html”以进行隐式授权 oauth 2 流程。我现在可以授权 Web 应用程序并将其转发回我的 Web 应用程序。令牌是 url 的一部分,我可以解析它。

恕我直言,我现在唯一要做的就是将此令牌添加为授权 header(例如 Authorization:Bearer 2829d5e2-4fbe-4f91-b74d-c99b2fe894a7)。但是 zuul 代理不会接受这个授权的请求。

我正在使用 spring boot 1.3.2 和 spring cloud Brixton.M4。 可以找到 Zuul 服务器应用程序 here and the security config here.

这是我的请求 headers:

Accept:application/json
Accept-Encoding:gzip, deflate, sdch
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Authorization:Bearer 2829d5e2-4fbe-4f91-b74d-c99b2fe894a7
Connection:keep-alive
Cookie:XSRF-TOKEN=a6ddea36-e3b7-4f22-b80c-b4c8b6fd7760; JSESSIONID=DAE4649D11386D586A0CF739148E505A; XSRF-TOKEN=3a7a57ad-68f6-4cc6-923b-4e8fe340fe1e
Host:localhost:8765
Referer:http://localhost:8765/web/index.html
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/48.0.2564.82 Chrome/48.0.2564.82 Safari/537.36
X-Auth-Token:2829d5e2-4fbe-4f91-b74d-c99b2fe894a7
X-Requested-With:XMLHttpRequest
X-XSRF-TOKEN:a6ddea36-e3b7-4f22-b80c-b4c8b6fd7760

我的 Zuul 配置是:

server:
  context-path: /

security:
  user:
    password: none
  oauth2:
    sso:
        loginPath: /login
    client:
      accessTokenUri: ${authserver.protocol}://${authserver.hostname}:${authserver.port}/${authserver.contextPath}/oauth/token
      userAuthorizationUri: ${authserver.protocol}://${authserver.hostname}:${authserver.port}/${authserver.contextPath}/oauth/authorize
      clientId: web
    resource:
      userInfoUri: ${authserver.protocol}://${authserver.hostname}:${authserver.port}/${authserver.contextPath}/user
      preferTokenInfo: false

zuul:
  routes:
    web-portal:
      path: /web/**
      url: http://localhost:8080/app
    user:
      path: /api/user/**
      url: ${authserver.protocol}://${authserver.hostname}:${authserver.port}/${authserver.contextPath}/user
    authentication-service:
      path: /uaa/**
      stripPrefix: false

---
spring:
  profiles: local

logging:
  level:
     org:
       springframework:
         security: DEBUG

authserver:
  protocol: http
  hostname: localhost
  port: 8899
  contextPath: uaa

zuul服务器日志为:

2016-02-11 17:11:02.958 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request '/api/user' matched by universal pattern '/**'
2016-02-11 17:11:02.958 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2016-02-11 17:11:02.958 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2016-02-11 17:11:02.958 DEBUG 3242 --- [nio-8765-exec-4] w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_SECURITY_CONTEXT
2016-02-11 17:11:02.958 DEBUG 3242 --- [nio-8765-exec-4] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@5571734d. A new one will be created.
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@42c144ce
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.util.matcher.AndRequestMatcher   : Trying to match using org.springframework.security.web.csrf.CsrfFilter$DefaultRequiresCsrfMatcher@4ad95822
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.util.matcher.AndRequestMatcher   : Did not match
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 5 of 13 in additional filter chain; firing Filter: ''
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 6 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /api/user' doesn't match 'POST /logout
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 7 of 13 in additional filter chain; firing Filter: 'OAuth2ClientAuthenticationProcessingFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/user'; against '/login'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 8 of 13 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 9 of 13 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 10 of 13 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@905571d8: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: DAE4649D11386D586A0CF739148E505A; Granted Authorities: ROLE_ANONYMOUS'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 11 of 13 in additional filter chain; firing Filter: 'SessionManagementFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 12 of 13 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.security.web.FilterChainProxy        : /api/user at position 13 of 13 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/user'; against '/index.html'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/user'; against '/home.html'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/user'; against '/web/**'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/user'; against '/uaa/oauth/**'
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /api/user; Attributes: [authenticated]
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@905571d8: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: DAE4649D11386D586A0CF739148E505A; Granted Authorities: ROLE_ANONYMOUS
2016-02-11 17:11:02.959 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@77816ac4, returned: -1
2016-02-11 17:11:02.960 DEBUG 3242 --- [nio-8765-exec-4] o.s.s.w.a.ExceptionTranslationFilter     : Access is denied (user is anonymous); redirecting to authentication entry point

org.springframework.security.access.AccessDeniedException: Access is denied

如何根据访问令牌在 zuul 代理上强制进行身份验证?

--- 编辑: 如果我通过删除静态 Web 资源的 http 安全例外来启用身份验证,我将被转发到授权页面。当请求被转发回来时,一切正常。 zuul 代理将其 /login URL 作为 return 地址转发到 oauth 服务器。这似乎是正确的方法。我想它会在 session 中保存一些信息,然后转发回初始请求页面(在我的例子中 /web/index.html)。

当我现在重新启动身份验证服务(如模拟过期令牌)时,来自 Web 应用程序的资源将得到服务,但对 /api/user(代理到身份验证服务器)的请求被拒绝。

当我来自手动构建的授权 URL 时,也会发生同样的情况 http://localhost:8899/uaa/oauth/authorize?response_type=token&client_id=web&redirect_uri=http://localhost:8765/web/index.html。首先,我进入 oauth 服务器的授权页面。这是对的。单击授权后,请求将转发到网络应用程序 (/web/index.html)。所有静态内容都没有问题,但访问 /api/user 再次被拒绝。这次在 oauth 服务器中记录了一个错误:无效的访问令牌:dff5121b-06e4-4bd7-b48e-08ad82d71404

您应该移动到 Spring Boot 1.3x。

然后,您可以使用 @EnableOAuath2Sso 注释对您的 Zulu Proxy 进行注释。 在 Zuul 的 application.yml 中,指定以下内容(对于 Spring Boot 1.3x):

security:
   user:
     password: none
   oauth2:
     client:
       accessTokenUri: ${oauthserver}:${oauthport}/oauth/token
       userAuthorizationUri: ${oauthserver}:${oauthport}/oauth/authorize
       clientId: acme
       clientSecret: acme secret

感谢@Dave 我可以回答我自己的问题: 要让 Zuul 代理接受 OAuth Bearer Token header,您必须将其配置为资源服务器而不是 SSO 服务器。 删除 @EnableOAuth2Sso 注释并改用 @EnableResourceServer 注释。首先,我的 WebSecurityConfigurerAdapter 还在。所以这是通过更改

解决的
@Configuration
@EnableOAuth2Sso
public class OAuthConfiguration extends WebSecurityConfigurerAdapter {

@Configuration
@EnableResourceServer
public class OAuthConfiguration extends ResourceServerConfigurerAdapter {

是否应该使用隐式身份验证是另一个话题(请参阅 Dave 的评论)。

zuul api 默认不转发 header 所以禁用它我们需要添加它

zuul:
  sensitive-headers: Cookie,Set-Cookie

默认情况下,Zuul 代理将删除某些 headers,因为它认为它们是敏感的,其他服务器不应接收它们。它有一个 cookie 列表和需要删除的 headers。这需要被覆盖。针对特定路线执行此操作的方法是:

zuul:
  routes:
    profile-service-chaining:
      sensitiveHeaders:
      stripPrefix: false
      serviceId: profile-service-chaining
      path: /services/profiles

行 'sensitiveHeaders:' 将清空 cookies header。这将导致所有 headers 都传递到请求将发送到的服务器。这将允许授权 header 发送到目标服务器。正如 Zuul 文档所说,确保始终删除敏感的 headers。为此,添加:

sensitiveHeaders: Cookie

以上将删除 Cookie header,然后将其传递到 Zuul 路由指定的下一个服务器。