统一网关GateWay
通俗理解:当微服务内部需要相互调用时,直接通过Feign即可完成调用;但是当外部的服务想要调用内部的微服务时,肯定不能直接调用,也为了保证安全,因此先通过网关,再由网关完成调用。
在Spring Cloud中网关的实现有两种,一种是zuul,它是基于Servlet实现的,属于阻塞式编程,也是早期的版本;另一种是gateway,是spring5中提供的WebFlux,是spring cloud自己研发的产品,属于响应式编程的实现,也具有更好的性能,gateway是后期版本。
一、搭建网关服务
- 创建新的module,引入SpringCloudGateway的依赖和nacos依赖:
<!-- nacos服务注册发现依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.1</version>
</dependency>
<!-- 网关gateway依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.0.5</version>
</dependency>
<!-- spring-cloud-starter-gateway取消了loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>3.0.0</version>
</dependency>
- 配置gateway配置文件
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: nacos:8848 #nacos地址
gateway:
routes:
- id: user-service #路由标示,必须唯一
uri: lb://user-service #路由的目标地址 lb:loadBalance缩写
predicates: #断言(就是路由转发要满足的条件)
- Path=/user/** #路径断言,判断路径是否是以/user开头,如果是则符合
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
#此处可以配置多个
- 访问:
http://localhost:10010/user/1
注意:此处容易踩坑,正常配置过后启动gateway,通过gateway的配置访问user服务,会报如下错误:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CLXaXPYI-1667887532352)(C:\Users\FULING~1\AppData\Local\Temp\WeChat Files\ba79221c557ed6f37fb894570bacc3c.png)]
提示报错401,无权限访问,这是由于跨域问题,解决:在gateway模块中新建CorsConfig.java
package cn.itcast.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
然后重启gateway即可正常访问!
4.总结
刚才用户的访问步骤如下图所示:
二、路由配置文件配置内容项
- 路由id:路由唯一标识
- uri:路由目的地,支持lb和http两种(http为直接写ip地址)
- predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地
- filters:路由过滤器,处理请求或响应
对于路由,Spring提供了11种基本的Predicate工厂:
三、路由过滤器GatewayFilter
官方有30几种不同的路由过滤器工厂,后期需要用到的时候根据过滤器名字确定使用哪种过滤器。
四、全局过滤器Global Filters
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。
与GatewayFilter区别:
- GatewayFilter通过配置定义,处理逻辑是固定的;
- GlobalFilters的逻辑需要自己写代码实现;
示例:实现GlobalFilter接口:
AuthorizeFilter.java
/*
@Order(-1): 顺序值,当定义不同的配置时,根据这个值的大小来决定执行顺序,值越小执行顺序越高,
这个值默认是2147483647,执行顺序最低,最高顺序值是-1
即数值区间[-1, 2147483647],执行顺序依次递减
**/
@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取请求参数
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> params = request.getQueryParams();
//2.获取参数中的 authorization 参数
String auth = params.getFirst("authorization");
//3.判断参数值是否等于 admin
if ("admin".equals(auth)) {
//4.是,放行
return chain.filter(exchange);
}
//5.否,拦截
//5.1.设置状态码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//5.2.拦截请求
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -1;
}
}
判断请求参数中是否有authorization参数,并且值为amdin,是则允许访问,不是则拒绝访问!
无参数时测试:
http://localhost:10010/user/1
有参数时测试:
http://localhost:10010/user/1?authorization=admin
五、过滤器的执行顺序
请求进入网关过后会碰到三类过滤器:当前路由的过滤器(即前面提到的30几种过滤器)、DefaultFilter、GlobalFilter。请求路由后,会将这三类过滤器合并到一个过滤器链中,排序后依次执行每个过滤器。
- 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
- GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定。
- 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
如下图所示顺序: - 当过滤器的Order值一样时,会按照defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
六、跨域问题
跨域理解:域名不一致就是跨域,主要包括:
- 域名不同:www.taobao.com和www.taobao.org和www.jd.com
- 域名相同,端口不同:localhost:8080 和 localhost:8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题。
解决方案:cors
方式一:gateway配置文件
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848 #nacos地址
gateway:
routes:
- id: user-service #路由标示,必须唯一
uri: lb://user-service #在服务注册中心找服务名为 user-service的服务,服务集群自动负载均衡 lb:loadBalance缩写
predicates: #路由断言,判断请求是否符合规则
- Path=/user/** #路径断言,判断路径是否是以/user开头,如果是则符合
# filters:
# - AddRequestHeader=xxxxxxx #顺序 1
# - AddxxxHeader=yyy #顺序 2
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
# #########################全局跨域处理##############################
globalcors:
add-to-simple-url-handler-mapping: true #解决options请求拦截问题
cors-configurations:
'[/**]':
allowedOrigins: #允许哪些网站的跨域请求
- "http://www.xxx.8080"
- "http://www.yyy.8080"
allowedMethods: #允许哪些跨域ajax的跨域请求
- "GET"
- "POST"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" #允许在请求中携带的头部信息
allowCredentials: true #是否允许携带cookie
maxAge: 360000 #这次跨域的有效期
方式二:CorsConfig配置类
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*"); #允许哪些跨域ajax的跨域请求
config.addAllowedOrigin("*"); #允许哪些网站的跨域请求
config.addAllowedHeader("*"); #允许在请求中携带的头部信息
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}