Skip to main content
 首页 » 编程设计

spring-boot之使用 Zuul 作为认证网关

2024年11月24日14jackei

背景

我想实现本 article 中提出的设计.

可以用下图来概括:

  • 客户端首先通过 IDP (OpenID Connect/OAuth2)
  • 进行身份验证
  • IDP 返回访问 token (没有用户信息的不透明 token )
  • 客户端通过 API 网关使用 Authorization header 中的访问 token 进行调用
  • API 网关使用访问 token
  • 向 IDP 发出请求
  • IDP验证Access Token是否有效,返回JSON格式的用户信息
  • API 网关将用户信息存储在 JWT 中并使用私钥对其进行签名。然后将 JWT 传递给下游服务,该服务使用公钥
  • 验证 JWT
  • 如果一个服务必须调用另一个服务来完成请求,它会传递 JWT,作为请求的身份验证和授权

  • 我到目前为止所拥有的

    我已经完成了大部分工作:
  • Spring Cloud 作为全局框架
  • Spring boot 启动个别服务
  • Netflix Zuul 作为 API 网关

  • 我还编写了一个 Zuul PRE 过滤器,用于检查访问 token 、联系 IDP 并创建 JWT。然后将 JWT 添加到转发到下游服务的请求的 header 中。

    问题

    现在我的问题是针对 Zuul 及其过滤器的。如果由于任何原因在 API 网关中身份验证失败,我如何才能停止路由并直接使用 401 响应,而无需继续过滤器链并转发调用?

    目前,如果身份验证失败,过滤器不会将 JWT 添加到 header 中,并且 401 将来自下游服务。我希望我的网关可以防止这种不必要的调用。

    我试着看看如何使用 com.netflix.zuul.context.RequestContext这样做,但文档很差,我找不到办法。

    请您参考如下方法:

    我知道我很晚才回答。
    您可以使用zuul的前置过滤器。下面给出了您必须遵循的步骤。

     //1. create filter with type pre 
     //2. Set the order of filter to greater than 5 because we need to run our filter after preDecoration filter of zuul. 
     @Component 
     public class CustomPreZuulFilter extends ZuulFilter { 
     
      private final Logger logger = LoggerFactory.getLogger(this.getClass()); 
     
    @Override 
    public Object run() { 
        final RequestContext requestContext = RequestContext.getCurrentContext(); 
        logger.info("in zuul filter " + requestContext.getRequest().getRequestURI()); 
        byte[] encoded; 
        try { 
            encoded = Base64.encode("fooClientIdPassword:secret".getBytes("UTF-8")); 
            requestContext.addZuulRequestHeader("Authorization", "Basic " + new String(encoded)); 
     
            final HttpServletRequest req = requestContext.getRequest(); 
            if (requestContext.getRequest().getHeader("Authorization") == null 
                    && !req.getContextPath().contains("login")) { 
                requestContext.unset(); 
                requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); 
     
            } else { 
                  //next logic 
                } 
            } 
     
        } catch (final UnsupportedEncodingException e) { 
            logger.error("Error occured in pre filter", e); 
        } 
     
        return null; 
    } 
     
     
     
    @Override 
    public boolean shouldFilter() { 
        return true; 
    } 
     
    @Override 
    public int filterOrder() { 
        return 6; 
    } 
     
    @Override 
    public String filterType() { 
        return "pre"; 
    } 
     
    } 
    
    requestContext.unset() 将为当前线程事件请求重置 RequestContext,您可以提供响应状态代码。