16.Spring-Cloud-Zuul之请求过滤

      每个客户端用户请求微服务应用提供的接口时,它们权限往往都有一定的限制,系统并不会将所有的微服务接口都对它们开放。然而服务路由并没有限制权限这样的功能,所有请求被毫无保留的转发到具体应用并返回结果,为了实现对客户端请求的安全校验签名和权限控制,最简单和粗暴的方法就是为每个微服务应用都实现一套用于校验签名和鉴别权限的的过滤器或拦截器。不过这样会增加日后的维护难度,而且代码也变得冗余。所以最好的做法是将这些检验剥离出去,构建一个独立的鉴权服务。

       那么更好的做法是通过前置的网关服务来完成这些非业务性质的校验。由于网关服务的加入,外部客户端访问系统已经有了统一入口,既然这些检验与具体业务无关,那就在请求到达的时候完成检验和过滤,而不是转发后再过滤导致更长的请求延迟,同时,通过在网关中完成校验和过滤,微服务应用端就可以去除各种复杂的国旗和拦截器,这样使得微服务应用接口的开发和测试复杂度也降低。

   为了在API网关中实现对客户端请求的检验。Spring Cloud Zuul另外一个核心功能就是请求过滤。实现非常简单是许集成ZuulFilter抽象类并实现它定义的4个抽象函数就可以完成对请求拦截和过滤了。

   ZuulFilter是在zuul-core-1.3.0.jar。

4个抽象函数ZuuFilter里有两个,IZuulFilter里面有两个,具体源码如下。


ZuuFilter部分源码:
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {


    private final DynamicBooleanProperty filterDisabled =
            DynamicPropertyFactory.getInstance().getBooleanProperty(disablePropertyName(), false);


    /**
     * to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
     * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
     * We also support a "static" type for static responses see  StaticResponseFilter.
     * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
     *//抽象方法
     * @return A String representing that type
     */
    abstract public String filterType();


    /**
     * filterOrder() must also be defined for a filter. Filters may have the same  filterOrder if precedence is not
     * important for a filter. filterOrders do not need to be sequential.
     *  //抽象方法
     * @return the int order of a filter
     */
    abstract public int filterOrder();

IZuulFilter源码
public interface IZuulFilter {
    /**
     * a "true" return from this method means that the run() method should be invoked
     *
     * @return true if the run() method should be invoked. false will not invoke the run() method
     */
    boolean shouldFilter();


    /**
     * if shouldFilter() is true, this method will be invoked. this method is the core method of a ZuulFilter
     *
     * @return Some arbitrary artifact may be returned. Current implementation ignores it.
     */
    Object run();


}

 

Spring Cloud Zuul中实现的过滤器必选包含4个基本特征:过滤请求、执行顺序,执行条件、具体操作。这四个条件就是要继承ZuuFilter重写的四个方法。

 

  •   //在ZuulFilter抽象类中定义的
  •    1.abstract public String filterType();
  •    2.abstract public int filterOrder();
  •    //在IZuulFilter接口中定义的
  •    3. boolean shouldFilter();
  •    4. Object run();

具体各自的功能如下:

 

  •  filterType:该函数需要返回一个字符串代表过滤器的类型,而这个类型就是在HTTP请求过程的各个阶段(即整个HTTP的声明周期)。在zuul中默认定义了4种不同声明周期的过滤器类型:如下

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

             2.routing:在请求时被调用

             3.post:在routing和error过滤之后调用。

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

 

  •  filterOrder:通过int值来定义过滤器的执行顺序,数据值越小优先级越高。
  • shouldFilter:返回一个boolean值来判断过滤器是否要执行。可以通过此方法来指定过滤器的有效范围。
  • run:过滤的具体逻辑。在该函数中,可以实现自定义的过滤逻辑,来确定是否要拦截当前请求,不对其进行后续的路由,或是在请求路由返回结果之后,对处理结果做一些加工。

filterType 4个类型对于整个HTTP声明周期的控制图如下:

上面几个核心过滤器是在spring-cloud-netflix-core.jar文件下。

演示

pom.xml

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>905.spring-cloud</groupId>
<artifactId>zuul-filter</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud Maven Webapp</name>
<url>http://maven.apache.org</url>
<!--springboot采用1.5.x 对应springcloud版本为 Dalston -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath />
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!--引入zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>


</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 这样变成可执行的jar -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

 

启动类

 

package com.niugang;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import com.niugang.config.AccessFilter;
/**
 * zuul启动类
 * 
 * @author niugang
 *
 */ 
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
//可以直接用@SpringCloudApplication注解,该注解包含上面三个注解
/*
 *设置一个Zuul服务器端点并在其中安装一些反向代理过滤器,这样它就可以将请求转发给后端服务器
 */
@EnableZuulProxy
public class Application {


public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

//配置filter
@Bean
public AccessFilter accessFilter(){
return  new AccessFilter();
}
}

 


ZuulFilter

package com.niugang.config;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
public class AccessFilter extends ZuulFilter {
private final Logger logger = LoggerFactory.getLogger(AccessFilter.class);
@Override
public boolean shouldFilter() {
return true;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public Object run() {
     RequestContext ctx = RequestContext.getCurrentContext();
     HttpServletRequest request = ctx.getRequest();
     logger.info("send  {} request to {} ",request.getMethod(),request.getRequestURL().toString());
     String accessToken = request.getParameter("accessToken");
     if(accessToken==null) {
    logger.warn("access token is empty");
    ctx.setSendZuulResponse(false);
    ctx.setResponseStatusCode(401);
ctx.setResponseBody("no power");
    return  null;
     }
     logger.info("access token ok");
return null;
}
  
}

 


启动注册中心,服务提供者,

服务消费者,api网关

输入:http://localhost:8777/api-a/feign-consumer 显示

输入:http://localhost:8777/api-a/feign-consumer?accessToken=11 显示

​  服务调用成功

     

 微信公众号

 

 

posted @ 2020-01-14 17:22  盲目的拾荒者  阅读(632)  评论(0编辑  收藏  举报