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


端口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

最佳答案

我终于找到了对我有用的解决方案。我写了一个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;
    }

}

09-28 02:15