一、前言
随着业务的扩展,微服务会不对增加,相应的其对外开放的 API 接口也势必增多,这不利于前端的调用以及不同场景下数据的返回,因此,我们通常都需要设计一个 API 网关作为一个统一的 API 入口,来组合一个或多个内部 API。
二、简单介绍
2.1 API 网关使用场景
黑白名单: 实现通过 IP 地址控制请求的访问 日志:实现访问日志的记录,进而实现日志分析,处理性能指标等 协议适配:实现通信协议的校验、适配转换的功能 身份认证:对请求进行身份认证 计流限流:可以设计限流规则,记录访问流量 路由:将请求进行内部(服务)转发
2.2 API 网关的实现
业界常用的 API 网关有很多方式,如:Spring Cloud Zuul、 Nginx、Tyk、Kong。本篇介绍的对象正是 Spring Cloud Zuul。
Zuul 是 Netflix 公司开源的一个 API 网关组件,提供了认证、鉴权、限流、动态路由、监控、弹性、安全、负载均衡、协助单点压测等边缘服务的框架。
Spring Cloud Zuul 是基于 Netflix Zuul 的微服务路由和过滤器的解决方案,也用于实现 API 网关。其中,路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入门的基础。而过滤功能是负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。
Spring Cloud Zuul 和 Eureka 进行整合时,Zuul 将自身注册到 Eureka 服务中,同时从 Eureka 中获取其他微服务信息,以便请求可以准确的通过 Zuul 转发到具体微服务上。
三、实战演练
本次测试案例基于之前发表的文章中介绍的案例进行演示,不清楚的读者请先转移至Spring Cloud 入门之 Hystrix 进行浏览。
服务实例 | 端口 | 描述 |
---|---|---|
common | - | 公用的,如:实体类 |
eureka-server | 9000 | 注册中心(Eureka 服务端) |
provider01-server | 8081 | 用户服务(Eureka 客户端) |
provider02-server | 8082 | 用户服务(Eureka 客户端) |
api | 80 | 消费服务(Eureka 客户端) |
创建一个为名 gateway-server 的 Spring Boot 项目。
3.1添加依赖
<!-- eureka 客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>2.1.0.RELEASE</version> </dependency> <!-- zuul 网关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> <version>2.1.0.RELEASE</version> </dependency>
3.2配置文件
server: port: 5555 spring: application: name: gateway eureka: instance: instance-id: gateway-5555 prefer-ip-address: true client: service-url: defaultZone: http://localhost:9000/eureka/ # 注册中心访问地址
3.3 启动 Zuul
在启动类上添加 @EnableZuulProxy 注解.
@EnableZuulProxy @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
依次启动上面项目
首先我们先调用api服务项目的地址:http://127.0.0.1/user/get/1
之后再修改成访问 gateway-server 项目的请求地址:http://127.0.0.1:5555/api/user/get/1
最终的相应结果都一样。
http://127.0.0.1:5555/api/user/get/1中的api是在Eureka 上注册的消费服务
3.4 zuul 常用配置
修改路由:
zuul: sensitive-headers: # 全局忽略敏感头,即允许接收 cookie 等请求头信息 routes: javaj: # 任意名字,保证唯一即可 path: /javaj/** # 自定义,真正用到的请求地址 service-id: api # 路由到的目标服务名称
将消费服务的路由名称改成 javaj。
使用 Postman 请求下单接口,运行结果:
请求成功
禁用路由:
zuul: ignored-patterns: - /api/user/**
http://127.0.0.1:5555/api/user/get/1 无法正常路由到消费服务,返回404错误
路由加前缀:
zuul: prefix: /open
所有请求中的path需要添加前缀/open;
如http://127.0.0.1:5555/javaj/user/get/1 要改为 http://127.0.0.1:5555/open/javaj/user/get/1
设置敏感头:
zuul: sensitive-headers: # 设置全局敏感头,如果为空,表示接收所有敏感头信息 或 zuul: routes: javaj: # 任意名字,保证唯一即可 path: /javaj/** # 自定义,真正用到的请求地址 service-id: api# 路由到的目标服务名称 sensitive-headers: # 针对 /javaj/ 的请求设置敏感头信息
四、Zuul 自定义过滤器
Zuul 的核心技术就是过滤器,该框架提供了 ZuulFilter 接口让开发者可以自定义过滤规则。
我们以身份检验为例,自定义 ZuulFilter 过滤器实现该功能。
4.1 创建用户服务
新建名为 user-server 的项目。
添加依赖:
<dependency> <groupId>com.common</groupId> <artifactId>common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- eureka 客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>2.1.0.RELEASE</version> </dependency>
application.yml:
server: port: 8100 spring: application: name: USER eureka: instance: instance-id: user-api-8100 prefer-ip-address: true # 访问路径可以显示 IP client: service-url: defaultZone: http://localhost:9000/eureka/ # 注册中心访问地址
登录接口:
@RestController @RequestMapping("/user") public class LoginController { @PostMapping("/login") public AjaxResult login(String username, String password, HttpServletResponse response) { if ("admin".equals(username) && "admin".equals(password)) { // 模拟生成 token,实际开发中 token 应存放在数据库或缓存中 String token = "123456"; Cookie cookie = new Cookie("token", token); cookie.setPath("/"); cookie.setMaxAge(60 * 10); response.addCookie(cookie); return AjaxResult.success(); } return AjaxResult.error(401, "账号或密码错误"); } }
user-server 启动类:
@EnableEurekaClient @SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } }
4.2 创建 ZuulFilter 过滤器
在 gateway-server 项目中,新建一个过滤器,需要继承 ZuulFilter 类:
@Component public class AuthenticationFilter extends ZuulFilter { /** * 是否开启过滤 */ @Override public boolean shouldFilter() { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); boolean flag = request.getRequestURI().contains("/login"); // 如果是登录请求不进行过滤 if (flag) { System.out.println("========不执行 zuul 过滤方法======="); } else { System.out.println("========执行 zuul 过滤方法======="); } return !flag; } /** * 过滤器执行内容 */ @Override public Object run() throws ZuulException { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); String token = request.getParameter("token"); // 此处模拟获取数据库或缓存中的 token String dbToken = "123456"; // 此处简单检验 token if (token == null || "".equals(token) || !dbToken.equals(token)) { context.setSendZuulResponse(false); context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); } return null; } /** * 过滤器类型 */ @Override public String filterType() { return "pre"; } /** * 过滤器执行顺序 */ @Override public int filterOrder() { return 0; } }
其中,filterType 有 4 种类型:
pre: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。 routing:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用 Apache HttpClient 或 Netfilx Ribbon 请求微服务。 post:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。 error:在其他阶段发生错误时执行该过滤器。
其过滤顺序如下图:
4.3 测试过滤器
运行所有项目,测试操作步骤如下:
请求用户服务的登录接口(http://127.0.0.1:5555/open/user/user/login?username=admin&password=admin),请求不执行 zuul 过滤方法,并且请求响应返回的 cookie 包含 token 请求消费服务的接口(http://127.0.0.1:5555/open/javaj/user/get/1),但不携带 token,请求需要执行 zuul 过滤方法,请求响应 401 权限不足 请求消费服务的接口(http://127.0.0.1:5555/open/javaj/user/get/1),携带之前登录接口返回的 token,请求需要执行 zuul 过滤方法,校验通过后路由到消费服务执行之后的操作
五、案例源码
评论 (0)