如果下游服务以401

时间:2016-12-02 16:33:06

标签: spring-security spring-boot spring-cloud spring-security-oauth2 spring-cloud-netflix

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

  1. 端口9999上的身份验证服务器
  2. 在端口9008上具有安全和不安全端点的基本后端示例
  3. 一个基本的Zuul API网关,在端口9000的后端示例中有多条路径(安全和不安全)
  4. 后端样本引导应用程序注释为资源服务器(@EnableResourceServer),并使用ResourceServerConfigurerAdapter保护某些端点

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

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

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

    Backend-sample 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
    

1 个答案:

答案 0 :(得分:3)

我终于找到了一个对我有用的解决方案。我写了一个ZuulFilter,它只处理401个响应并重定向到登录。它还将拒绝的请求保存在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;
    }

}