如果下游服务响应 401,如何在 Zuul API 网关中触发 OAuth2 身份验证

Posted

技术标签:

【中文标题】如果下游服务响应 401,如何在 Zuul API 网关中触发 OAuth2 身份验证【英文标题】:How to trigger OAuth2 authentication in Zuul API gateway if a downstream service responds with a 401 【发布时间】:2017-04-17 15:23:18 【问题描述】:

我正在试验三个 Spring 云(引导)应用程序。

    9999 端口上的身份验证服务器 在端口 9008 上具有安全和非安全端点的基本后端示例 一个基本的 Zuul API 网关,有多个路由(安全和不安全)到端口 9000 上的后端示例

后端示例启动应用程序被注释为资源服务器 (@EnableResourceServer) 并使用 ResourceServerConfigurerAdapter 保护一些端点

当我第一次调用 Zuul API 网关上受保护的路由之一时,我被重定向到身份验证服务器的登录页面。在那里登录后,我被重定向到我最初请求的安全路线。受保护的后端样本端点的行为符合预期,这意味着后端样本确实获得了所提供令牌的授予角色。如果我点击了我没有适当角色的后端样本端点,我会收到 OAuth 403 响应。在这种情况下一切正常。

我们还需要将遗留服务置于 API 网关之后。这些渲染 html 并且应该能够在用户点击那里的安全区域时触发登录。我们无法在 API 网关路由级别保护这些区域,因为旧后端对许多不同的子 URL 具有复杂(增长)的权限模型。

有没有人知道在这种下游 401 响应情况下让 Spring-cloud API 网关重定向到身份验证服务器登录的好方法?我在“post”类型的 ZuulFilter 中尝试了一个简单的重定向,但由于响应已经在那里提交而失败了。

后端示例application.yml;

server:
    port: 9008
    
security:
  oauth2:
    resource:
      userInfoUri: http://localhost:9999/uaa/user

API 网关 application.yml:

server:
    port: 9008
zuul:
   proxy:
      addProxyHeaders: true
   sensitive-headers: 
   routes:
      unsecured-backend-sample:
         path: /unsecured-backend-sample/**
         url: http://localhost:9008
      authorized-backend-sample:
         path: /authorized-backend-sample/**
         url: http://localhost:9008/
      user-role-secured-backend-sample:
         path: /user-role-secured-backend-sample/**
         url: http://localhost:9008/
      xxx-role-secured-backend-sample:
         path: /xxx-role-secured-backend-sample/**
         url: http://localhost:9008/
security:
  oauth2:
    client:
      accessTokenUri: http://localhost:9999/uaa/oauth/token
      userAuthorizationUri: http://localhost:9999/uaa/oauth/authorize
      clientId: acme
      clientSecret: acmesecret
    resource:
      userInfoUri: http://localhost:9999/uaa/user

【问题讨论】:

您的后置过滤器是在 SendResponseFilter 之前还是之后运行的? 感谢您观看斯宾塞。我认为我最初的尝试是在 SendResponseFilter 之后为时已晚。我找到了一个 ZuulFilter 解决方案,并将其发布在这里作为答案。如果你能评论它会很酷。如果你喜欢它并认为它与 spring-cloud 的架构概念兼容,我很乐意提交它。 【参考方案1】:

我终于找到了一个非常适合我的解决方案。我写了一个只处理 401 响应并重定向到登录的 ZuulFilter。它还将被拒绝的请求保存在 HTTP 会话请求缓存中,以便 SavedRequestAwareAuthenticationSuccessHandler 可以将您重定向回最初请求的下游服务 URL。

@Component
public class LoginOnDownstreamUnauthorizedResponseFilter extends ZuulFilter 

    private Logger logger = LoggerFactory.getLogger(getClass());

    private AuthenticationEntryPoint authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint("/login");

    private RequestCache requestCache = new HttpSessionRequestCache();

    @Override
    public boolean shouldFilter() 
        // Only handle downstream 401s
        return RequestContext.getCurrentContext().getResponse().getStatus() == HttpStatus.SC_UNAUTHORIZED;
    

    @Override
    public Object run() 

        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        HttpServletResponse response = ctx.getResponse();

        // We need to put the rejected request in the request cache for SavedRequestAwareAuthenticationSuccessHandler
        // to find it's way back to the initial request URI after successful authentication.
        requestCache.saveRequest(request, response);

        String text = String.format("Downstream service %s responded with a status code 401.", request.getRequestURI());
        logger.debug(text + " Calling Authentication entry point.");
        try 
            authenticationEntryPoint.commence(request, response, new InsufficientAuthenticationException(text));
         catch (IOException | ServletException e) 
            logger.error("Failed to redirect to Authentication entry point", e);
        

        return null;
    

    @Override
    public String filterType() 
        return "post";
    

    @Override
    public int filterOrder() 
        // make sure to run before SendResponseFilter
        return 500;
    


【讨论】:

以上是关于如果下游服务响应 401,如何在 Zuul API 网关中触发 OAuth2 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

API网关(Zuul)合并两个微服务响应

如何在全球范围内以最佳方式处理来自服务器的 401/403 响应

zuul:不接受承载认证

从 Zuul Server API Gateway 调用多个服务

如何将 CORS 标头添加到 401/403 响应?

使用zuul、eureka、feign时如何减少延迟