Spring Cloud - 如何利用Zuul进行网关开发

Zuul介绍

Zuul是Spring Cloud全家桶一员,用于微服务网关开发,实现对外服务请求的白黑名单控制、代理转发、限流、权鉴认证、灰度测试等。

流程图

Zuul POM引入

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!-- spring-boot版本 -->
<version>2.0.5.RELEASE</version>
<relativePath/>
</parent>

<properties>
<java.version>1.8</java.version>
<!-- Spring Cloud版本 -->
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</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>

开启Zuul

Application.java

1
2
3
4
5
6
7
8
9
10
11
//开启Zuul
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class Application {

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

}

Zuul配置

bootstrap.yml

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
#路由方式是serviceId
ribbon:
#对所有操作请求都进行重试
OkToRetryOnAllOperations: true
#切换实例的重试次数
MaxAutoRetriesNextServer: 2
#对当前实例的重试次数
MaxAutoRetries: 1
#请求链接的超时时间
ConnectTimeout: 6000
#请求处理的超时时间
ReadTimeout: 8000
eureka:
enable: false

zuul:
sensitive-headers:
#自动重试
retryable: true
routes:
client-api:
#过滤headers,不进入下游
sensitiveHeaders: Cookie,Set-Cookie,Authorization
#服务名称
a:
#转发匹配规则
path: /api/a/**
#直接转发,不过滤前缀
stripPrefix: false
#转发到服务ID
serviceId: a-service
b:
path: /api/b/**
stripPrefix: false
serviceId: b-server

Zuul开发

在网关统一完成请求过滤和用户认证

AccessFilter.java

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

@Component
//实现ZuulFilter
public class AccessFilter extends ZuulFilter {

//实现run逻辑
@Override
public Object run() {
return exec();
}

private Object exec() {
RequestContext ctx = RequestContext.getCurrentContext();

//获取HttpServletRequest
HttpServletRequest request = ctx.getRequest();

//解析ip
String ip = getIpAddr(request);
//补充到header,可以带入下游
ctx.addZuulRequestHeader("ip", ip);

//接口的白名单和黑名单
String url = request.getRequestURI();

//白名单可以直接进入下游
if (isWhite(url)) {
return null;
}

//黑名单禁止访问
if (isBlack(url)) {
setBlack(ctx, url);
return null;
}

//token解析
validToken(ctx, url);

return null;
}

/**
* 获取用户id,检验token真实性
*
* @param ctx
* @param url
* @return
*/
void validToken(RequestContext ctx, String url) {

// 获取请求的参数
String token = ctx.getRequest().getHeader(Constant.TOKEN);

//解析token业务,获取用户信息
String userId = parseToken(token);

//传入下游
ctx.addZuulRequestHeader("userId", userId);
}

public static boolean setBlack(RequestContext ctx, String requestUrl) {
logger.error("无权限请求:{}", requestUrl);
setStatus(ctx, EnumStatus.ERROR, false);
return true;
}

public static void setStatus(RequestContext ctx, EnumStatus enumStatus, boolean flag) {
ctx.getResponse().setContentType("application/json; charset=utf-8");
//令zuul过滤该请求,不对其进行路由,直接返回客户端错误信息,
ctx.setSendZuulResponse(false);
ctx.setResponseBody("{\"msg\":\"" + enumStatus.getInfo() + "\",\"code\":" + enumStatus.getValue() + "}");
ctx.set("isSuccess", flag);
}

/**
* 是否是白名单
*
* @param url
* @return
*/
boolean isWhite(String url) {
return Arrays.stream(Constant.WHITE_LIST).anyMatch(s -> url.contains(s));
}

/**
* 是否是白名单
*
* @param url
* @return
*/
boolean isBlack(String url) {
return Arrays.stream(Constant.BLACK_LIST).anyMatch(s -> url.contains(s));
}

@Override
public boolean shouldFilter() {
// 是否执行该过滤器,此处为true,说明需要过滤
return true;
}

@Override
public int filterOrder() {
// 数字越大,优先级越低
return 0;
}

@Override
public String filterType() {
// 前置过滤器
return "pre";
}
}

在网关统一处理异常

ErrorFilter.java

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
@Component
public class ErrorFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(ErrorFilter.class);

@Override
public String filterType() {
//异常过滤器
return "error";
}

@Override
public int filterOrder() {
//优先级,数字越大,优先级越低
return 30;
}

@Override
public boolean shouldFilter() {
//是否执行该过滤器,true代表需要过滤
return true;
}

@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();

setError(ctx, ctx.getRequest().getRequestURL().toString());

return null;
}
}
------ 本文结束------

本文标题:Spring Cloud - 如何利用Zuul进行网关开发

文章作者:Perkins

发布时间:2019年04月23日

原始链接:https://perkins4j2.github.io/posts/33205/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。