赞
踩
引言:
一、搭建SpringCloud工程,请前往:保姆级教程构建SpringCloud工程(图文结合)
二、集成服务注册配置管理中心Nacos,请前往:SpringCloud集成服务注册配置管理中心Nacos
三、集成HTTP客户端工具OpenFeign,请前往:SpringCloud集成HTTP客户端工具OpenFeign
四、集成统一网关SpringCloud Gateway,请前往:SpringCloud集成微服务API网关Gateway(详解)
在SpringCloud中网关的实现有两种:
Zuul是基于Servlet的实现,属于阻塞式编程。
SpringCloud Gateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
创建新的 cloud-gateway Module,引入SpringCloud Gateway的依赖及Nacos的服务注册与发现依赖
<dependencies>
<!-- 网关依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 服务注册与发现依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
在Gateway 的 resources 下新建 bootstrap.yml,配置路由和Nacos 服务注册与发现中心
这里如果创建的是bootstrap.yml的话,一定要引入 bootstrap的依赖,不然是不会进行识别和加载的,也可以不引入 bootstrap依赖,直接创建application.yml也是可以的
<!--bootstrap 启动器加载上下文-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
网关路由配置的内容包括:
# 服务端口 server: port: 10717 spring: application: # 服务名称 name: gateway profiles: active: dev main: # Description: # Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway. # Action: # Please set spring.main.web-application-type=reactive or remove spring-boot-starter-web dependency. #上述错误表明 SpringBoot 与 SpringCloud Gateway 不兼容, 设置属性为reactive 告诉Spring Boot使用响应式Web应用程序类型,与Spring Cloud Gateway兼容。 web-application-type: reactive # cloud: nacos: # Nacos服务地址 server-addr: localhost:8848 # Nacos的服务注册与发现 discovery: # Nacos服务地址 # server-addr: localhost:8848 # 配置集群名称,也就是机房位置,例如:HZ 杭州 cluster-name: HZ # Nacos可视化命名空间ID namespace: cdd2a801-3280-4476-8362-d46318aac1a3 gateway: routes: # 网关路由配置 - id: orderservice # 路由ID,自定义填写,唯一即可 # uri 有两种配置方法,一种 http ,一种 lb:服务名称 # uri: http://127.0.0.1:18088/ # 路由目标地址 uri: lb://orderservice # 路由的目标地址 lb 就是负载均衡,后边跟服务名称 predicates: #路由断言 ,白话就是判断请求符不符合路由规则条件 - Path=/orderservice/** # 这个是按照路径匹配,只要以 /orderservice/ 开头就符合条件 - id: paymentservice uri: lb://paymentservice predicates: - Path=/paymentservice/**
新建GatewayApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author waves
* @date 2024/6/4 15:53
*/
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
System.out.println("Gateway网关服务启动成功 ヾ( ̄ー ̄)X(^▽^)ゞ");
}
}
启动网关服务,查看是否正常启动。
路由断言工厂 Route Predicate Factory
名称 | 说明 | 示例 |
---|---|---|
After | 是某个时间点后的请求 | - After=2024-01-20T19:41:47.789-07:00[America/Denver] |
Before | 是某个时间点之前的请求 | - Before=2024-04-13T19:14:47.433+08:00[Asia/Shanghai] |
Between | 是某两个时间点之前的请求 | - Between=2023-01-20T17:42:47.789-07:00[America/Denver], 2024-10-21T17:42:47.789-07:00[America/Denver] |
Cookie | 请求必须包含某些cookie | - Cookie=chocolate, ch.p |
Header | 请求必须包含某些header | - Header=X-Request-Id, \d+ |
Host | 请求必须是访问某个host(域名) | - Host=.somehost.org,.anotherhost.org |
Method | 请求方式必须是指定方式 | - Method=GET,POST |
Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
Query | 请求参数必须包含指定参数 | - Query=name, Jack或者- Query=name |
RemoteAddr | 请求者的ip必须是指定范围 | - RemoteAddr=192.168.1.1/24 |
Weight | 权重处理 |
GatewayFilter是SpringCloud Gateway 网关中提供的一种过滤器,对进入网关的请求和服务响应进行处理。
如下:
名称 | 说明 |
---|---|
AddRequestHeaderGatewayFilterFactory | 用于添加请求头。 |
AddResponseHeaderGatewayFilterFactory | 用于添加响应头。 |
AddRequestParameterGatewayFilterFactory | 用于添加请求参数。 |
AddRequestParameterGatewayFilterFactory | 用于添加响应参数。 |
AddResponseHeaderGatewayFilterFactory | 用于添加请求参数。 |
AddRequestParameterGatewayFilterFactory | 用于添加响应参数。 |
PrefixPathGatewayFilterFactory | 用于添加前缀路径。 |
PreserveHostHeaderGatewayFilterFactory | 用于保留主机头。 |
RemoveNonProxyHeadersGatewayFilterFactory | 用于删除非代理头。 |
RemoveRequestHeaderGatewayFilterFactory | 用于删除请求头。 |
RemoveResponseHeaderGatewayFilterFactory | 用于删除响应头。 |
RequestRateLimiterGatewayFilterFactory | 用于请求速率限制。 |
RetryGatewayFilterFactory | 用于请求重试。 |
SecureHeadersGatewayFilterFactory | 用于添加安全头。 |
SetPathGatewayFilterFactory | 用于设置路径。 |
SetPathGatewayFilterFactory | 用于设置路径。 |
SetRequestHeaderGatewayFilterFactory | 用于设置请求头。 |
SetResponseHeaderGatewayFilterFactory | 用于设置响应头。 |
SetStatusGatewayFilterFactory | 用于设置状态码。 |
RedirectToGatewayFilterFactory | 用于重定向。 |
RewritePathGatewayFilterFactory | 用于重写路径。 |
RewriteLocationResponseHeaderGatewayFilterFactory | 用于重写响应头中的位置。 |
SaveSessionGatewayFilterFactory | 用于保存会话。 |
MapRequestHeaderGatewayFilterFactory | 用于映射请求头。 |
MapResponseHeaderGatewayFilterFactory | 用于映射响应头。 |
MapRequestParameterGatewayFilterFactory | 用于映射请求参数。 |
MapResponseHeaderGatewayFilterFactory | 用于映射响应参数。 |
StripPrefixGatewayFilterFactory | 用于删除前缀。 |
AddResponseHeaderGatewayFilterFactory | 用于删除前缀。 |
AddRequestParameterGatewayFilterFactory | 用于删除前缀。 |
AddRequestParameterGatewayFilterFactory | 用于删除前缀。 |
以AddRequestHeaderGatewayFilterFactory 为例:给进入orderservice 的请求添加一个请求头 Authorization = token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA
在Gateway的 bootstrap.yml 文件中,在orderservice 的配置下添加路由过滤器
spring:
cloud:
gateway:
routes: # 网关路由配置
- id: orderservice # 路由ID,自定义填写,唯一即可
# uri 有两种配置方法,一种 http ,一种 lb:服务名称
# uri: http://127.0.0.1:18088/ # 路由目标地址
uri: lb://orderservice # 路由的目标地址 lb 就是负载均衡,后边跟服务名称
predicates: #路由断言 ,白话就是判断请求符不符合路由规则条件
- Path=/orderservice/** # 这个是按照路径匹配,只要以 /orderservice/ 开头就符合条件
# 路由过滤器
filters:
# 对进入 orderservice 的请求添加 请求头
- AddRequestHeader=Authorization,token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA
在代码中测试是否能获取到请求头
/** * 测试获取gateway中添加的 Authorization * @param request * @return */ @GetMapping("/getToken") public ResponseResult getToken(HttpServletRequest request){ return ResponseResult.success("操作成功",request.getHeader("Authorization")); } 返回结果,成功获取到token http://localhost:10717/orderservice/system/order/getToken { "code": 100, "data": "token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA", "message": "操作成功" }
如果要对所有的路由都添加过滤器,可以把过滤器工厂配置在default下。如下配置:
spring: application: # 服务名称 name: gateway profiles: active: dev cloud: gateway: routes: # 网关路由配置 - id: orderservice # 路由ID,自定义填写,唯一即可 # uri 有两种配置方法,一种 http ,一种 lb:服务名称 # uri: http://127.0.0.1:18088/ # 路由目标地址 uri: lb://orderservice # 路由的目标地址 lb 就是负载均衡,后边跟服务名称 predicates: #路由断言 ,白话就是判断请求符不符合路由规则条件 - Path=/orderservice/** # 这个是按照路径匹配,只要以 /orderservice/ 开头就符合条件 # 路由过滤器 filters: # 对进入 orderservice 的请求添加 请求头 - AddRequestHeader=Authorization,token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA - id: paymentservice uri: lb://paymentservice predicates: - Path=/paymentservice/** # 默认过滤器,会对所有的路由请求都生效 default-filters: - AddRequestHeader=Authorization,token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA
代码测试,paymentservice服务未单独配置添加请求头,进行测试
/** * 测试获取gateway中添加的 Authorization * @param request * @return */ @GetMapping("/getToken") public ResponseResult getToken(HttpServletRequest request){ return ResponseResult.success("操作成功",request.getHeader("Authorization")); } 返回结果,成功获取到token http://localhost:10717/paymentservice/payment/getToken { "code": 100, "data": "token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA", "message": "操作成功" }
全局过滤器 GlobalFilter 的作用 与 Gateway Filter 的作用一样。处理一切进入网关的请求和服务的响应。
两个的区别在于GatewayFilter是通过配置定义,处理逻辑是不可变的。而GlobalFilter的逻辑需要开发人员自己写代码进行实现。方式是实现GlobalFilter接口
public interface GlobalFilter { /** * Process the Web request and (optionally) delegate to the next {@code WebFilter} * through the given {@link GatewayFilterChain}. * @param exchange the current server exchange * @param chain provides a way to delegate to the next filter * @return {@code Mono<Void>} to indicate when request processing is complete * * 处理Web请求并(可选地)通过给定的GatewayFilterChain委托给下一个WebFilter。 * Params: exchange-请求上下文-提供了一种委托给下一个过滤器的方法 * 返回:Mono<Void>指示请求处理何时完成 * */ Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain); }
自定义过滤器实现代码:
import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * 模拟登陆 * //@Order(-1) // 优先级 配置方式有两种,一种直接加 @Order(-1) 注解,另一种实现Order接口 * @author waves * @date 2024/6/5 14:36 */ @Component public class AuthorizeFilter implements GlobalFilter, Ordered { private static final String STATIC_TOKEN = "token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1.获取请求中的参数 ServerHttpRequest request = exchange.getRequest(); MultiValueMap<String,String> params = request.getQueryParams(); //2.获取请求中的Authorization 参数值 String authToken = params.getFirst("Authorization"); //3.判断token是否合法 if (STATIC_TOKEN.equals(authToken)){ //4.合法,进行放行 return chain.filter(exchange); } //5.不合法,直接拦截,并返回状态码 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); //拦截请求 return exchange.getResponse().setComplete(); } @Override public int getOrder() { return -1; } }
测试:
错误请求:
http://localhost:10717/paymentservice/payment/getToken?Authorization=1234
返回:
This page isn’t workingIf the problem continues, contact the site owner.
HTTP ERROR 401
正确请求:
http://localhost:10717/paymentservice/payment/getToken?Authorization=token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA
返回:
{"code":100,"data":"token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA","message":"操作成功"}
当请求进入到网关会经过三类过滤器:针对配置的路由过滤器(filters:)、默认过滤器(default-filters:)、全局过滤器(GlobalFilter)
请求进入网关路由后,会将当前路由过滤器和默认过滤器,GlobalFilter合并到一个过滤器链(集合)中,进行排序后依次执行
当请求进入时
请求 --> 路由 --> 默认过滤器 --> 服务路由过滤器 --> 全局过滤器 --> 微服务
响应 --> 全局过滤器 --> 服务路由过滤器 --> 默认过滤器 --> 路由
1.自定义GlobalFilter 通过添加@Order注解指定order值,或者实现 Ordered接口 返回order的值。
2.每一个过滤器都必须指定一个int类型的order值,值越小,优先级就越高,执行顺序就越靠前。
3.路由过滤器和默认过滤器的order由spring指定,默认是按照声明顺序从1递增。
4.当过滤器的order值都一样的时候,会按照 默认过滤器 > 路由过滤器 > 全局过滤器 的顺序执行。
具体可以参考如下几个类的源码进行查看:
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法是先加载defaultFilters,然后再加载某个route的filters,然后合并。
org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链
什么是跨域:
当一个网页的源(origin)与所请求资源的源不一致时产生的问题。
跨域包括:
原因:
跨域时浏览器会执行同源策略(Same-Origin Policy),限制页面中的JavaScript代码只能与其同源的资源进行交互,否则的话请求就会被拦截
解决方案:
Access-Control-Allow-Origin
头部,服务器可以允许指定源的请求访问资源。<script>
标签实现跨域请求的一种方法。由于 <script>
标签的跨域特性,可以通过在请求 URL 中添加回调函数名来获取数据,并在响应中返回 JSON 数据。这里网关使用CORS解决 跨域问题,并且在Gateway中只需要简单配置即可实现:
spring: cloud: gateway: # 全局跨域处理 globalcors: # 解决options(预检请求)请求被拦截问题,默认是拦截 add-to-simple-url-handler-mapping: true cors-configurations: '[/**]': allowedOrigins: # 允许哪些网站的跨域请求 - "http://localhost:10717" - "http://localhost:8088" - "https://localhost:8099" allowedMethods: #允许跨越的Ajax请求 - "GET" - "POST" - "DELETE" - "PUT" - "OPTIONS" allowedHeaders: "*" # 允许在请求头中携带头信息 allowCredentials: true # 是否允许携带Cookie maxAge: 360000 # 跨域检测的有效时间
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。