五、网关微服务搭建(可选)
- 完整工程源码:https://gitee.com/forwardxiang/spring-cloud-demo.git
5.1 创建网关服务子工程
5.1.1 引入依赖
-
创建子工程同其他子工程一样,这里不赘述。
-
除了继承父工程依赖外,还需引入gateway依赖和Eureka客户端:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
编写启动类,注意需要开启Eureka客户端服务发现功能:
@SpringBootApplication @EnableDiscoveryClient public class ApiGatewayApplication { //省略 }
5.2 配置网关服务
5.2.1 服务及注册部分
-
和其他注册到Eureka服务里的服务一样:
server: port: 8086 spring: application: name: api-gateway eureka: client: service-url: defaultZone: HTTP://127.0.0.1:10086/eureka instance: prefer-ip-address: true
5.2.2 Gateway配置
-
这部分配置是Spring Cloud Gateway特有的:用于配置路由,把predicates规定的请求路由到指定的id服务上,路由前还能将请求进行处理,例如StripPrefix=2表示去除uri中前缀个数为2。例如:
http://localhost:10010/api/user/8 -->http://localhost:9091/8
server: port: 8086 spring: application: name: api-gateway cloud: gateway: routes: # 路由id,可以随意写 - id: bill-service-route # 代理的服务地址(lb协议会通过Eureka给出具体的ip地址) uri: lb://bill-manager # uri: http://127.0.0.1:9091 #注意这里有坑,http只是是小写 # 路由断言,可以配置映射路径 predicates: - Path=/api/** filters: - StripPrefix=1
5.2.3 Gateway继承的ribbon和hystrix配置
-
如果不进行配置,则会采取缺省配置:
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 6000 #服务降级超时时间,默认1S ribbon: ConnectTimeout: 1000 # 连接超时时长 ReadTimeout: 2000 # 数据通信超时时长 MaxAutoRetries: 0 # 当前服务器的重试次数 MaxAutoRetriesNextServer: 0 # 重试多少次服务
5.2.4 Gateway自带过滤器
-
网关除了可以进行路由管理外,还可以进行鉴权等过滤行为,Gateway自带过滤器无需手动编写,只需要在配置中配置即可,若配置在routes具体的id路由下的filters则为局部默认过滤,如上面举例中的StripPrefix过滤器,而配置在default-filters中的为全局默认过滤器。
-
这里以自带的AddResponseHeader过滤器举例,并在所有路由下生效,实现在响应头里新增字段的目的:
spring: application: name: api-gateway cloud: gateway: default-filters: - AddResponseHeader=yourKey, yourValue
-
请求效果如下:
5.2.5 自定义过滤器
-
自定义全局过滤器无需配置,只需要编写实现
implements GlobalFilter, Ordered
即可:@Component public class MyGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("----------全局过滤器MyGlobalFilter------"); //该过滤器检查请求头中是否带有token,若有则放行。 String token = exchange.getRequest().getHeaders().getFirst("token"); if(StringUtils.isBlank(token)){ //设置响应状态码为未授权 exchange.getResponse().setStatusCode(HTTPStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { //值越小越先执行 return 1; } }
-
自定义局部过滤器,继承AbstractGatewayFilterFactory抽象类,且需要指定在配置文件里配置过滤器名称,注意yml文件中需要省略掉GatewayFilterFactory:本例为MyParam过滤器。
@Component public class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> { static final String PARAM_NAME = "param"; public MyParamGatewayFilterFactory() { super(Config.class); } public List<String> shortcutFieldOrder() { return Arrays.asList(PARAM_NAME); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { //获取请求参数中param对应的参数名 的参数值 ServerHTTPRequest request = exchange.getRequest(); if(request.getQueryParams().containsKey(config.param)){ request.getQueryParams().get(config.param). forEach(value -> System.out.printf("局部过滤器%s=%s", config.param, value)); } return chain.filter(exchange); }; } public static class Config{ //对应在配置过滤器的时候指定的参数名 private String param; public String getParam() { return param; } public void setParam(String param) { this.param = param; } } }
-
在配置文件里配置自定义过滤器需要的参数:
server: port: 8086 spring: application: name: api-gateway cloud: gateway: routes: # 路由id,可以随意写 - id: bill-service-route # 代理的服务地址(lb协议会通过Eureka给出具体的ip地址) uri: lb://bill-manager # uri: http://127.0.0.1:9091 #注意这里有坑,http只是是小写 # 路由断言,可以配置映射路径 predicates: - Path=/api/** filters: - StripPrefix=1 - MyParam=name
-
在自定义重写的apply方法里,可以获取配置文件里传给过滤器的参数名称为name,那么就可以在访问者访问时携带的所有参数中找到该参数名对应的值,以此达到通过配置文件更改动态获取访问请求里的不同参数的值。
5.3 网关服务的跨域配置
5.3.1 跨域问题(浏览器问题)
-
**同源:**如果两个页面(接口)的协议,端口和域名都相同,那么两个页面就有相同的源。
-
一个页面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击。因此,跨域问题 是针对ajax的一种限制。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。
-
同源策略限制了以下行为:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 JS 对象无法获取
- Ajax请求发送不出去
5.3.2 Gateway配置跨域
-
在配置文件里新增:
spring: application: name: api-gateway cloud: gateway: default-filters: #Greenwich.SR2以上版本才有 为了去重复的头 测试发现RETAIN_UNIQUE无效 - DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_FIRST globalcors: cors-configurations: '[/api/bill/types]': #也可以是全路径/** allowedOrigins: '*' #允许所有的域 allowedHeaders: "*" #允许所有头 allowCredentials: true #服务端开启运行携带认证 allowedMethods: #默认运行GET和OPTIONS(预检请求) - POST - GET - PUT - DELETE '[/api/bill/test]': allowedOrigins: '*' #允许所有的域 allowedHeaders: "*" allowCredentials: true allowedMethods: '*'
-
注意该配置方法仅适用于SpringCloudGateway,本案例中的业务微服务需要其他方法配置。