Spring Cloud API 网关服务Zuul

构建网关

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

1.创建Spring Boot工程并添加依赖

首先我们创建一个普通的Spring Boot工程名为api-gateway,然后添加相关依赖,这里我们主要添加两个依赖spring-cloud-starter-zuul和spring-cloud-starter-eureka,spring-cloud-starter-zuul依赖中则包含了ribbon、hystrix、actuator等,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</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.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</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>

2.添加注解

然后在入口类上添加@EnableZuulProxy注解表示开启Zuul的API网关服务功能,如下:

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {

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

3.配置路由规则

application.properties文件中的配置可以分为两部分,一部分是Zuul应用的基础信息,还有一部分则是路由规则,如下:

1
2
3
4
5
6
7
8
9
# 基础信息配置
spring.application.name=api-gateway
server.port=2006
# 路由规则配置
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=feign-consumer

# API网关也将作为一个服务注册到eureka-server上
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/

我们在这里配置了路由规则所有符合/api-a/**的请求都将被转发到feign-consumer服务上,至于feign-consumer服务的地址到底是什么则由eureka-server去分析,我们这里只需要写上服务名即可。以上面的配置为例,如果我请求http://localhost:2006/api-a/hello1接口则相当于请求http://localhost:2005/hello1(我这里feign-consumer的地址为http://localhost:2005),我们在路由规则中配置的api-a是路由的名字,可以任意定义,但是一组path和serviceId映射关系的路由名要相同。

OK,做好这些之后,我们依次启动我们的eureka-server、provider和feign-consumer,然后访问如下地址http://localhost:2006/api-a/hello1,访问结果如下:

图片

看到这个效果说明我们的API网关服务已经构建成功了,我们发送的符合路由规则的请求自动被转发到相应的服务上去处理了。

请求过滤

构建好了网关,接下来我们就来看看如何利用网关来实现一个简单的权限验证。这里就涉及到了Spring Cloud Zuul中的另外一个核心功能:请求过滤。请求过滤有点类似于Java中Filter过滤器,先将所有的请求拦截下来,然后根据现场情况做出不同的处理,这里我们就来看看Zuul中的过滤器要如何使用。很简单,两个步骤:

1.定义过滤器

首先我们定义一个过滤器继承自ZuulFilter,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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即可,如下:

1
2
3
4
@Bean
PermisFilter permisFilter() {
return new PermisFilter();
}

此时,如果我们访问http://localhost:2006/api-a/hello1,结果如下:

图片

如果给请求地址加上login参数,则结果如下:

图片