史上最强项目实战(六)——网关服务黑白名单拦截

坚强是说给别人听的谎言 提交于 2020-01-06 19:47:23

我们现在已经成功搭建服务注册中心、分布式配置中心,以及网关服务。但是我们只是简单的把分布式配置中心以及网关服务注册到服务注册中心,然后简单的从分布式配置中心读取配置,并没有真正体会到他们各自的特性。今天我们就以网关拦截器为例,通过定义黑白路径体验网关服务的拦截器功能以及分布式配置中心的自动刷新功能。

1. 路径拦截配置类

我们准备把黑白路径做成可配置的,这样一来,后续若有需求变更,直接改配置即可。其中@Data注解时lombok依赖的,用于声明这是一个实体类,将自动为成员变量allowPathsforbidPaths生成gettersetter方法;@RefreshScope用来指定配置刷新范围,自动刷新时会扫描到该类。

@Data
@RefreshScope
@ConfigurationProperties(prefix = "leyou.gateway.filter")
public class FilterProperties {

    /**
     * 允许访问,直接通过的请求路径
     */
    private List<String> allowPaths;

    /**
     * 禁止访问请求路径
     */
    private List<String> forbidPaths;
}

2. 路径拦截器

@Slf4j
@Component
public class PathFilter extends ZuulFilter {
    @Autowired
    private FilterProperties filterProperties;

    /**
     * 过滤器类型,包括以下4种类型:<p>
     * 1、pre:请求在被路由之前执行。<br>
     * 2、route:在路由请求时执行。<br>
     * 3、post:在route和error过滤器之后执行。<br>
     * 4、error:处理请求时发生错误执行。
     *
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 定义过滤器的执行顺序,数字越小优先级越高。
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 判断该过滤器是否需要执行。返回true执行,返回false不执行。
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        // 1、获取请求上下文
        RequestContext requestContext = RequestContext.getCurrentContext();
        // 2、获取request对象
        HttpServletRequest request = requestContext.getRequest();
        // 3、获取请求路径
        String requestURI = request.getRequestURI();
        // 4、判断白名单
        for (String allowPath : filterProperties.getAllowPaths()) {
            if (requestURI.startsWith(allowPath)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 过滤器的具体业务逻辑
     *
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        // 1、获取请求上下文
        RequestContext requestContext = RequestContext.getCurrentContext();
        // 2、获取request对象
        HttpServletRequest request = requestContext.getRequest();
        // 3、获取请求路径
        String requestURI = request.getRequestURI();
        // 4、判断黑名单
        for (String forbidPath : filterProperties.getForbidPaths()) {
            if (requestURI.startsWith(forbidPath)) {
                requestContext.setSendZuulResponse(false);
                requestContext.setResponseStatusCode(403);
                log.error("非法访问,地址:{}", request.getRemoteHost());
                break;
            }
        }
        return null;
    }
}

3. 测试类

定义一个leyou.gateway.configValue属性配置,默认值为Hi,在后面的例子,我们就将修改该值,然后看分布式配置中心是否能自动将属性值刷新。

@RefreshScope
@RestController
@RequestMapping("/gateway")
public class GatewayTestController {

    @Value("${leyou.gateway.configValue:Hi}")
    private String configValue;

    @GetMapping("/allowPath")
    public String allowPathsTest() {
        return configValue + ",see you again.";
    }

    @GetMapping("/forbidPath")
    public void forbidPathsTest() {
        // do nothing
    }
}

上面路径拦截配置类、路径拦截器以及测试类,我们都在网关服务写完了。但是要测试黑白路径以及配置自动刷新,我们还需要引入相关依赖、添加些配置才可以。

4. 引入依赖

在分布式配置中心和网关服务的模块pom.xml添加如下依赖:

<!--实现配置自动刷新-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

在网关服务再添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

5. 添加配置

5.1 分布式配置中心配置

server:
  port: 9015
spring:
  application:
    name: ly-env-config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/anbang713/config-center.git # 指向git仓库地址,如果是私有仓库,则需要配置用户名和密码
          search-paths: leyou # 指定服务配置所在的目录
    bus:
      trace:
        enabled: true
      enabled: true
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
eureka:
  client:
    service-url:
      defaultZone: http://Anbang713:pwd713@localhost:9010/eureka/
  instance:
    lease-renewal-interval-in-seconds: 5 # 5秒钟发送一次心跳
    lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期
management:
  endpoints:
    web:
      exposure:
        include: bus-refresh

5.2 网关服务配置

eureka:
  client:
    service-url:
      defaultZone: http://Anbang713:pwd713@localhost:9010/eureka/
  instance:
    lease-expiration-duration-in-seconds: 10
    lease-renewal-interval-in-seconds: 5
leyou:
  gateway:
    configValue: Hello
    filter:
      allowPaths:
      - /api/gateway/allowPath
      forbidPaths:
      - /api/gateway/forbidPath
server:
  port: 9020
spring:
  application:
    name: ly-env-gateway-server
  cloud:
    bus:
      enabled: true
      trace:
        enabled: true
  rabbitmq:
    host: localhost
    password: guest
    port: 5672
    username: guest
    virtual-host: /
zuul:
  prefix: /api
  routes:
    ly-env-gateway-server:
      path: /gateway/** # 以/api/gateway/**开头的请求,全部由网关服务处理
      strip-prefix: false # 不截断/gateway

6. 测试

现在是这样的,我们在网关服务配置了/api/gateway/allowPath为允许正常访问的路径,而/api/gateway/forbidPath是不允许访问的路径,将被拦截器PathFilter直接拦截。然后我们通过修改配置configValue的值,在不重启服务的情况下看下日志输出值是否已经更新。

(1)启动服务,可以看到分布式配置中心和网关服务已成功注册到服务注册中心。
在这里插入图片描述

(2)访问http://localhost:9020/api/gateway/forbidPath,访问被拒绝。
在这里插入图片描述

(3)访问http://localhost:9020/api/gateway/allowPath,访问成功,并读取到分布式配置中心的属性值。
在这里插入图片描述

(4)更改分布式配置中心的属性值,我们将原来的hello值修改成hello123

leyou:
  gateway:
    filter: 
      allowPaths:
        - /api/gateway/allowPath
      forbidPaths:
        - /api/gateway/forbidPath
    configValue: Hello123

(5)是否现在再直接访问http://localhost:9020/api/gateway/allowPath就能获取到更改后的值呢?答案是不行的。
在这里插入图片描述

(6)那要怎样才能自动更新值呢?还记得我们引入的依赖吗?在分布式配置中心和网关服务我们都引入了spring-cloud-busspring-cloud-stream-binder-rabbit的依赖,以及在网关服务引入了spring-boot-starter-actuator依赖。同时在分布式配置中心开放了bus-refresh端点。其实仔细的朋友可以在分布式服务启动时输出了这样的日志信息:

2019-12-23 21:50:19.747  INFO 4036 --- [           main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/bus-refresh],methods=[POST]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)

以及服务启动成功后,rabbitmq自动创建了名称为springCloudBus类型为topic的交换器和以springCloudBus.anonymous.**开头的队列,并且它们的绑定队列是这样的。
在这里插入图片描述

好了,那我们大概知道配置自动刷新的策略是怎样的。其流程大致为:

  • 刷新消息总线。
  • 消息总线发送一条MQ消息。
  • 监听MQ队列的消费者消费消息完成配置更新。

按照上述的流程,我们先刷新消息总线,响应成功。
在这里插入图片描述

同时,MQ也确实接收到了消息。
在这里插入图片描述
此时再访问http://localhost:9020/api/gateway/allowPath可以看到,值已经更新到最新了。
在这里插入图片描述

到了这里,我只想说:好极了。在这之前,我们修改配置值,还要重启服务才能生效。但是现在,一切都变得如此美妙。

——End——
更多详情,可扫码关注微信公众号哦。

在这里插入图片描述

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!