🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 1. 创建gateway服务 gateway需要从nacos获取服务列表和配置 **1. 创建gateway模块** ![](https://img.kancloud.cn/69/ab/69abf076301c8b3c8b36721b5d5e61d6_397x256.png) **2. pom** ~~~ <!--需要引入该jar才能使bootstrap配置文件生效--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-context</artifactId> </dependency> <!--服务发现--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--服务配置--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!--服务网关--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> ~~~ 3. 启动类 ~~~ @SpringBootApplication @EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ~~~ 4. bootstrap.yml ~~~ application: # 应用名称 name: gateway profiles: # 环境配置 active: dev cloud: nacos: config: # 配置中心地址 server-addr: 192.168.56.10:8848 # 配置文件格式 file-extension: yml group: DEFAULT_GROUP namespace: 1b2b82fd-96d4-4c10-ac42-80264b0cc2a4 ~~~ 4. gateway-dev.yml 配置nacos服务注册中心和配置中心 ``` server: port: 8002 spring: application: name: tuna-gateway cloud: nacos: discovery: # 服务注册地址 server-addr: 192.168.56.10:8848 namespace: 1b2b82fd-96d4-4c10-ac42-80264b0cc2a4 gateway: discovery: locator: lowerCaseServiceId: true enabled: true routes: # 认证中心 - id: provider-router uri: lb://provider predicates: - Path=/test/** ``` 所有 到gateway上的请求,都会路由到服务名`provider`上 ![](https://img.kancloud.cn/0c/7d/0c7d33cfa59ab7767fcc2b6d97c8bf9e_886x316.png) ![](https://img.kancloud.cn/59/bd/59bddf4c4ebf0ba1e5c3090eebb428e5_373x153.png) # 2. 路径重写 RewritePath GatewayFilter Factory The RewritePath GatewayFilter Factory takes a path regexp parameter and a replacement parameter. This uses Java regular expressions for a flexible way to rewrite the request path. application.yml.  ``` spring: cloud: gateway: routes: # ===================================== - id: rewritepath_route uri: http://example.org predicates: - Path=/foo/** filters: - RewritePath=/foo/(?<segment>.*), /$\{segment} ``` For a request path of /foo/bar, this will set the path to /bar before making the downstream request. Notice the $\\ which is replaced with $ because of the YAML spec. 修改 ``` routes: # 认证中心 - id: provider-router uri: lb://provider predicates: - Path=/gateway/** filters: - RewritePath=/gateway/(?<segment>.*), /$\{segment} ``` RewritePath要与 predicates: 中的path相同 ![](https://img.kancloud.cn/08/cf/08cf04b624fbdc577db3815502ad08f7_510x148.png) # 3. 加入filter ## 3.1 普通filter 1. 定义filter,继承 ~~~ @Component public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object> { private final static String AUTH_URL = "/login"; private static final String CODE = "code"; @Override public GatewayFilter apply(Object config) { HashMap<Object, Object> res = new HashMap<>(); return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); // 非登录请求,不处理 if (!StringUtils.containsIgnoreCase(request.getURI().getPath(), AUTH_URL)) { return chain.filter(exchange); } try { List<String> code = exchange.getRequest().getQueryParams().get("code"); if (code.size() == 0) throw new RuntimeException("非法用户"); } catch (Exception e) { ServerHttpResponse response = exchange.getResponse(); response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); return exchange.getResponse().writeWith( Mono.just(response.bufferFactory().wrap(JSON.toJSONBytes(res.put("message", "请求失败"))))); } return chain.filter(exchange); }; } } ~~~ 2. 修改配置,加入filter ![](https://img.kancloud.cn/8f/37/8f3758261df36b3686e9473d99382a46_713x519.png) 如图配置,只对/auth/开头的请求router进行过滤 请求成功 ![](https://img.kancloud.cn/d2/26/d226e14e2005e6f2357867c3c9d17793_493x185.png) 请求失败 ![](https://img.kancloud.cn/ac/e9/ace92afa99d4cf011d50e5ef978f2694_532x137.png) ## 3.2 自定义全局filter **无需配置,对所有的route都有效,可进行流量控制,权限等校验** ~~~ @Component public class TokenFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getQueryParams().getFirst("token"); if (token == null || token.isEmpty()) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return -100; } } ~~~ ![](https://img.kancloud.cn/b3/fb/b3fb40624122b46eb4e5a12c94fe6d67_711x373.png) ![](https://img.kancloud.cn/0b/bc/0bbc11e32d493a19ede881c0d59b70e4_694x182.png) ![](https://img.kancloud.cn/42/8a/428adc796540de2508f712930991fc26_721x332.png) ![](https://img.kancloud.cn/90/bf/90bf0807fdd1fc1f2d3b6f9aaebe2438_462x147.png) 对auth和gateway两个route都进行了拦截 ## 3.3 自定义局部过滤器 局部过滤器,**需要在配置文件中配置,如果配置,则该过滤器才会生效**。 **主要实现GatewayFilter, Ordered接口,并通过AbstractGatewayFilterFactory的子类注册到spring容器中,当然也可以直接继承AbstractGatewayFilterFactory,在里面写过滤器逻辑,还可以从配置文件中读取外部数据。** 只需要在亲请求处理之前和之后标记时间即可。注意此处演示的是使用配置类的形式: ~~~ package com.yefengyu.gateway.globalFilter; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import reactor.core.publisher.Mono; //全局过滤器,使用配置类形式,直接构造bean,使用注解完成Ordered接口功能,统计接口调用时间 @Configuration public class GlobalGatewayFilterConfig { @Bean @Order(-100) public GlobalFilter elapsedGlobalFilter() { return (exchange, chain) -> { //调用请求之前统计时间 Long startTime = System.currentTimeMillis(); return chain.filter(exchange).then().then(Mono.fromRunnable(() -> { //调用请求之后统计时间 Long endTime = System.currentTimeMillis(); System.out.println( exchange.getRequest().getURI().getRawPath() + ", cost time : " + (endTime - startTime) + "ms"); })); }; } } ~~~ 局部过滤器-简单的权限检查** * * * 权限检查一般把信息存储在某处,请求到来之后进行核对,有权限的请求将真正执行。 1、首先编写一个工具类,对权限做管理。 ~~~ package com.yefengyu.gateway.utitls; import java.util.HashMap; import java.util.Map; public final class AuthUtil { private static Map<String, String> map = new HashMap<>(); private AuthUtil() { } //程序启动的时候加载权限的信息,比如从文件、数据库中加载 public static void init() { map.put("tom", "123456"); } //简单判断 public static boolean isPermitted(String name, String password) { return map.containsKey(name) && map.get(name).equals(password); } } ~~~ 我们简单的将权限信息放到map中保管,init方法是初始化方法,isPermitted是对外提供一个判断是否有权限的方法。 2、服务启动的时候,需要初始化权限map,因此主启动类进行了修改: ~~~ package com.yefengyu.gateway; import com.yefengyu.gateway.utitls.AuthUtil; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationListener; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(GatewayApplication.class); springApplication.addListeners(new ApplicationListenerStarted());//增加监听器 springApplication.run(args); } private static class ApplicationListenerStarted implements ApplicationListener<ApplicationStartedEvent> { @Override public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) { //权限初始化数据 AuthUtil.init(); } } } ~~~ 3、编写一个局部过滤器,需要实现GatewayFilter, Ordered,实现相关的方法 ~~~ package com.yefengyu.gateway.localFilter; import com.yefengyu.gateway.utitls.AuthUtil; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; public class AuthGatewayFilter implements GatewayFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //获取header的参数 String name = exchange.getRequest().getHeaders().getFirst("name"); String password = exchange.getRequest().getHeaders().getFirst("password"); boolean permitted = AuthUtil.isPermitted(name, password);//权限比较 if (permitted) { return chain.filter(exchange); } else { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } } @Override public int getOrder() { return 10; } } ~~~ 4、接着需要把上面自定义的局部过滤器加入到过滤器工厂,并且注册到spring容器中。 ~~~ package com.yefengyu.gateway.localFilter; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.stereotype.Component; @Component public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> { @Override public GatewayFilter apply(Object config) { return new AuthGatewayFilter(); } } ~~~ 5、在配置文件中进行配置,如果不配置则不启用此过滤器规则。 ![](https://img.kancloud.cn/01/40/0140079f6e3a2902fad2505c397620ab_496x371.png)