SpringCloud智能网关Zuul-核心功能过滤器

简介: 在上一篇我们通过使用Spring Cloud Zuul构建了一个基础的API网关服务,同时也演示了Spring Cloud Zuul基于服务的自动路由功能。然而,目前的服务路由并没有限制权限这样的功能,所有请求都会被毫无保留地转发到具体的应用并返回结果,为了实现对客户端请求的安全校验和权限控制,需要为微服务实现一套用于校验签名和鉴别权限的过滤器或拦截器。由于网关服务的加入,外部客户端访问有统一入口,Zuul允许开发者在API网关服务通过定义过滤器来实现对请求的拦截与过滤,实现的方法非常简单,只需要继承ZuulFilter抽象类并实现它定义的四个抽象函数就可以完成对请求的拦截和过滤了。

过滤器


在上一篇我们通过使用Spring Cloud Zuul构建了一个基础的API网关服务,同时也演示了Spring Cloud Zuul基于服务的自动路由功能。然而,目前的服务路由并没有限制权限这样的功能,所有请求都会被毫无保留地转发到具体的应用并返回结果,为了实现对客户端请求的安全校验和权限控制,需要为微服务实现一套用于校验签名和鉴别权限的过滤器或拦截器。由于网关服务的加入,外部客户端访问有统一入口,Zuul允许开发者在API网关服务通过定义过滤器来实现对请求的拦截与过滤,实现的方法非常简单,只需要继承ZuulFilter抽象类并实现它定义的四个抽象函数就可以完成对请求的拦截和过滤了。

过滤器实现


定义一个简单的Zuul过滤器,它实现的功能是把请求被路由之前检查HttpServletRequest中是否有accessToken参数,如果有就进行路由,调用真正的服务。否则就拒绝访问,并返回401 Unauthorized错误。

通过继承ZuulFilter抽象类并重写了下面的四个方法来实现自定义的过滤器,代码如下:

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @author Yezhiwei
 * @date 17/12/13
 */
@Slf4j
@Component
public class AccessFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return "pre";
    }
    @Override
    public int filterOrder() {
        return 0;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
        Object accessToken = request.getParameter("accessToken");
        if(accessToken == null) {
            log.warn("access token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
//            ctx.set("error.status_code", HttpServletResponse.SC_UNAUTHORIZED);
//            ctx.set("error.message", "UNAUTHORIZED");
//            ctx.set("error.exception", "accessToken is null");                           throw new RuntimeException();
        }
        log.info("access token ok");
        return null;
    }
}
  • filterType:过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。在Zuul中默认定义了四种不同生命周期的过滤器类型,这里定义为pre,代表会在请求被路由之前执行。

pre:可以在请求被路由之前调用。

routing:在路由请求时候被调用。

post:在routing和error过滤器之后被调用。

error:处理请求时发生错误时被调用。


  • filterOrder:过滤器的执行顺序。当请求在一个阶段中存在多个过滤器时,需要根据该方法返回的值来依次执行。通过int值来定义过滤器的执行顺序,数值越小优先级越高。


  • shouldFilter:判断该过滤器是否需要被执行。这里我们直接返回了true,因此该过滤器对所有请求都会生效。实际运用中我们可以利用该函数来指定过滤器的有效范围。


  • run:过滤器的具体逻辑。这里我们通过 ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过 ctx.setResponseStatusCode(401)设置了其返回的错误码。

关键代码解释



  • 在实现了自定义过滤器之后,它并不会直接生效,我们还需要为其创建具体的Bean才能启动该过滤器,如 @Component


  • 核心过滤器中的 SendErrorFilter是用来处理异常信息的,详细看看SendErrorFilter源码的shouldFilter方法发现

public boolean shouldFilter() {
    RequestContext ctx = RequestContext.getCurrentContext();
    return ctx.getThrowable() != null && !ctx.getBoolean("sendErrorFilter.ran", false);
}

可以看到该方法的返回值中有一个重要的判断依据 ctx.getThrowable()

  • SendErrorFilter 源码中错误路径
@Value("${error.path:/error}")
private String errorPath;
  • 所以还需要对原来的统一异常处理类进行扩展,并且自定义异常信息。
@RestController
public class HttpNotFountConfig implements ErrorController {
    private final static String ERROR_PATH = "/error";
    @RequestMapping(value = ERROR_PATH)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResultData error(HttpServletRequest request) {
        RequestContext ctx = RequestContext.getCurrentContext();
        if (null != ctx) {
            System.out.println(ctx.getResponse().getStatus());
            return ResultData.builder().message(Message.builder().code(ctx.getResponse().getStatus()).message(HttpStatus.valueOf(ctx.getResponse().getStatus()).getReasonPhrase()).build()).build();
        }
        return ResultData.builder().message(Message.builder().code(HttpStatus.NOT_FOUND.value()).message(HttpStatus.NOT_FOUND.getReasonPhrase()).build()).build();
    }
    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }
}

测试

  • url中有规定的 accessToken参数,正常返回结果,如下图

  • url中无规定的 accessToken参数,返回401,如下图

相关文章
|
3天前
|
人工智能 安全 Java
AI 时代:从 Spring Cloud Alibaba 到 Spring AI Alibaba
本次分享由阿里云智能集团云原生微服务技术负责人李艳林主讲,主题为“AI时代:从Spring Cloud Alibaba到Spring AI Alibaba”。内容涵盖应用架构演进、AI agent框架发展趋势及Spring AI Alibaba的重磅发布。分享介绍了AI原生架构与传统架构的融合,强调了API优先、事件驱动和AI运维的重要性。同时,详细解析了Spring AI Alibaba的三层抽象设计,包括模型支持、工作流智能体编排及生产可用性构建能力,确保安全合规、高效部署与可观测性。最后,结合实际案例展示了如何利用私域数据优化AI应用,提升业务价值。
|
4月前
|
监控 负载均衡 安全
微服务(五)-服务网关zuul(一)
微服务(五)-服务网关zuul(一)
|
1月前
|
负载均衡 Java 开发者
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
167 5
|
3月前
|
XML Java 数据格式
如何使用 Spring Cloud 实现网关
如何使用 Spring Cloud 实现网关
58 3
|
4月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
219 5
|
3月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
72 0
|
4月前
|
测试技术 微服务
微服务(八)-服务网关zuul(四)
微服务(八)-服务网关zuul(四)
|
4月前
|
监控 前端开发 Java
微服务(七)-服务网关zuul(三)
微服务(七)-服务网关zuul(三)
|
4月前
|
负载均衡 前端开发 安全
微服务(六)-服务网关zuul(二)
微服务(六)-服务网关zuul(二)
|
5月前
|
Java Spring
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.