API网关是一个更为智能的应用服务器,它有点类似于我们微服务架构系统的门面,所有的外部访问都要先经过API网关,然后API网关来实现请求路由、负载均衡、权限验证等功能。

Spring Cloud中提供的Spring Cloud Zuul实现了API网关的功能。

构建网关

网关的构建我们通过下面三个步骤来实现。

1.创建cloud-gateway工程,并添加依赖。

pom.xml文件 主要内容如下:

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.4.3.RELEASE</version>
	<relativePath />
  </parent>
  <dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <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>Brixton.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement></project>

说明:spring-cloud-starter-zuul依赖中则包含了ribbon、hystrix、actuator等。

2,创建启动类,如下:

@SpringBootApplication
@EnableZuulProxy
public class AppGateway {

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

	@Bean
	PermisFilter permisFilter() {
	    return new PermisFilter();
	}}

3,配置路由规则,application.properties文件如下:

# 基础信息配置
spring.application.name=api-gateway
server.port=2006
# 路由规则配置
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=eureka.client.provider.test# API网关也将作为一个服务注册到eureka-server上
eureka.client.service-url.defaultZone=http://admin:123@localhost:8000/eureka/

说明:

我们在这里配置了路由规则所有符合/api-a/**的请求都将被转发到eureka.client.provider.test服务上,

至于feign-consumer服务的地址到底是什么则由eureka-server去分析,我们这里只需要写上服务名即可。

以上面的配置为例,如果我请求http://localhost:2006/api-a/testHello接口则相当于请求http://localhost:2005/testHello(我这里eureka.client.provider.test的地址为http://localhost:2005)。

我们在路由规则中配置的api-a是路由的名字,可以任意定义,但是一组path和serviceId映射关系的路由名要相同。

请求过滤

1.定义过滤器java类,如下:

public class PermisFilter 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();
          String login = request.getParameter("login");
          if (login == null) {
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(401);
                ctx.addZuulResponseHeader("content-type","text/html;charset=utf-8");
                ctx.setResponseBody("非法访问");
          }
          return null;
    }}

关于这个类我说如下几点:

1.filterType方法的返回值为过滤器的类型,过滤器的类型决定了过滤器在哪个生命周期执行,pre表示在路由之前执行过滤器,其他可选值还有post、error、route和static,当然也可以自定义。
2.filterOrder方法表示过滤器的执行顺序,当过滤器很多时,这个方法会有意义。
3.shouldFilter方法用来判断过滤器是否执行,true表示执行,false表示不执行,在实际开发中,我们可以根据当前请求地址来决定要不要对该地址进行过滤,这里我直接返回true。
4.run方法则表示过滤的具体逻辑,假设请求地址中携带了login参数的话,则认为是合法请求,否则就是非法请求,

如果是非法请求的话,首先设置ctx.setSendZuulResponse(false);表示不对该请求进行路由,然后设置响应码和响应值。这个run方法的返回值在当前版本(Dalston.SR3)中暂时没有任何意义,可以返回任意值。

2.配置过滤器Bean

然后在入口类中配置相关的Bean即可,如下:

@Bean
	PermisFilter permisFilter() {
	      return new PermisFilter();
	}

3,测试:

不加login参数,返回:非法访问

加上login参数,访问正常:http://192.168.20.211:2006/api-a/testHello?name="dragon2"&login=1

总结:

API网关作为系统的的统一入口,将微服务中的内部细节都屏蔽掉了,而且能够自动的维护服务实例,实现负载均衡的路由转发,

同时,它提供的过滤器为所有的微服务提供统一的权限校验机制,使得服务自身只需要关注业务逻辑即可。