API網關
gateway的作用類似於zuul,接口網關路由服務。
對後臺微服務接口進行統一暴露。還能做到“校驗,限流、熔斷”等作用
springCloud Gateway底層採用netty通訊,支持更高併發
特性:
- 基於Spring Framework 5、Project Reactor和Spring Boot 2.0構建
- 能夠在任意請求屬性上匹配路由
- predicates(謂詞) 和 filters(過濾器)是特定於路由的
- 集成了Hystrix斷路器
- 集成了Spring Cloud DiscoveryClient
- 易於編寫謂詞和過濾器
- 請求速率限制
- 路徑重寫
引入jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 集成eureka服務 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置
啓動類添加註解:
@SpringBootConfiguration
@EnableAutoConfiguration
public class Gateway {
public static void main(String[] args) {
SpringApplication.run(Gateway.class, args);
}
}
在application.properties中添加eureka配置
spring.main.allow-bean-definition-overriding=true
# eureka
eureka.client.healthcheck.enabled=true
eureka.client.eureka-connection-idle-timeout-seconds:10
eureka.instance.lease-renewal-interval-in-seconds:10
eureka.client.registry-fetch-interval-seconds:5
eureka.client.serviceUrl.defaultZone:http://localhost:8888/eureka/
eureka.instance.instance-id:${spring.cloud.client.ip-address}:${spring.application.name}:${spring.application.instance_id:${server.port}}
eureka.instance.prefer-ip-address: true
eureka.instance.hostname= ${spring.cloud.client.ip-address}
#是否在註冊中心註冊
eureka.client.register-with-eureka:false
在application.yml中添加路由規則配置
spring:
jmx:
enabled: false
cloud:
gateway:
default-filters:
#- PrefixPath=/httpbin
- AddResponseHeader=X-Response-Default-Foo, Default-Bar
routes:
# =====================================
- id: websocket_test
uri: ws://localhost:9000
order: 9000
predicates:
- Path=/echo
# =====================================
- id: api
uri: lb://api
predicates:
- Path=/api/open/**
- id: admin
uri: lb://admin
predicates:
- Path=/admin/**
- id: file
uri: lb://file
predicates:
- Path=/file/**
logging:
level:
org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
reactor.netty: DEBUG
management.endpoints.web.exposure.include: '*'
配置參數說明
- id : 規則id名字,自定義
- uri:規則匹配後,轉發到的目標地址,同一個註冊中心可以使用 lb://{serviceId} 格式轉發;其他可以使用http://xxx,ws://xxx等
- predicates:規則是否匹配的判斷標準,內置判斷規則非常多,還可以通過代碼自定義
- filters:過濾器,通過過濾器可以對請求進行簽名校驗,參數修改等操作
- order:規則優先級,數字大的先執行
Spring Cloud Gateway將過濾器執行邏輯分爲“pre”和“post”階段。優先級最高的過濾器將會是“pre”階段中的第一個過濾器,同時它也將是“post”階段中的最後一個過濾器。
//-----------過濾器執行順序示例-------------start//
@Bean
@Order(-1)
public GlobalFilter a() {
return (exchange, chain) -> {
log.info("first pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("third post filter");
}));
};
}
@Bean
@Order(0)
public GlobalFilter b() {
return (exchange, chain) -> {
log.info("second pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("second post filter");
}));
};
}
@Bean
@Order(1)
public GlobalFilter c() {
return (exchange, chain) -> {
log.info("third pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("first post filter");
}));
};
}
//-----------過濾器執行順序示例-------------end//
自定義配置
1、自定義全局過濾器
/**
* 全局過濾器示例
* @author wlddh
*
*/
public class MyGlobalFilter implements GlobalFilter {
private static Logger log = LoggerFactory.getLogger(MyGlobalFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String uri = request.getPath().toString();
String method = request.getMethod().name();
log.info("url :{} method: {} ", uri, method);
if (uri.startsWith("/demo")) {
//請求拒絕
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}
@Bean
public GlobalFilter myGlobalFilter() {
return new MyGlobalFilter();
}
2、自定義普通過濾器
/**
* 普通過濾器示例
*
* @author wlddh
*
*/
public class MyGatewayFilter implements GatewayFilter {
private static Logger log = LoggerFactory.getLogger(MyGatewayFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String uri = request.getPath().toString();
String method = request.getMethod().name();
log.info("url :{} method: {} ", uri, method);
String accessToken = request.getHeaders().getFirst("AccessToken");
if (StringUtils.isBlank(accessToken)) {
// 請求拒絕
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}
自定義過濾器配置
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes().route(
r -> r.order(-1).path("/api/open/**").filters(f -> f.filter(new MyGatewayFilter())).uri("lb://api"))
.build();
}
3、自定義過濾器工廠
public class TokenValidateGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenValidateGatewayFilterFactory.Config> {
private static Logger logger= LoggerFactory.getLogger(TokenValidateGatewayFilterFactory.class);
public TokenValidateGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
logger.info("TokenValidateGatewayFilterFactory...");
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
//校驗token的合法性
boolean tokenValidated= StringUtils.isNoneBlank(token);
if (tokenValidated) {
// 令牌合法,繼續訪問,可以進行一些處理,如:添加頭信息,參數等
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
//比如,根據令牌獲得用戶信息,userId
builder.header("ch-userId", Base64Utils.encodeToString("用戶Id".getBytes()));
return chain.filter(exchange.mutate().request(builder.build()).build());
}
//令牌不合法
ServerHttpResponse response = exchange.getResponse();
//設置headers
HttpHeaders httpHeaders = response.getHeaders();
httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
//過濾器中跨域需要自己處理
//設置body
String resultStr = "沒有訪問權限";
DataBuffer bodyDataBuffer = response.bufferFactory().wrap(resultStr.getBytes());
return response.writeWith(Mono.just(bodyDataBuffer));
// return chain.filter(exchange);
};
}
public static class Config{
}
}
過濾器工廠bean配置
@Bean
public TokenValidateGatewayFilterFactory tokenValidateGatewayFilterFactory(){
return new TokenValidateGatewayFilterFactory();
}
application.yml配置
spring:
cloud:
gateway:
default-filters:
# 只需要配置過濾類的前綴即可
- TokenValidate
注意,yml中配置的默認過濾器只適用於與yml配置的路由;如果是代碼實現的路由,不會觸發到默認過濾器;
但是全局過濾器都可以觸發
4、默認過濾器
application.yml
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Foo, Default-Bar
- PrefixPath=/httpbin
5、跨域訪問處理
方式一:
@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);
}
方式二:
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
if (CorsUtils.isCorsRequest(request)) {
HttpHeaders requestHeaders = request.getHeaders();
ServerHttpResponse response = ctx.getResponse();
HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
HttpHeaders headers = response.getHeaders();
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, PUT, OPTIONS, DELETE, PATCH");
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(ctx);
};
}
示例代碼地址:https://gitee.com/personal_practice/demo-spring-cloud-gateway.git
參考
- https://spring.io/projects/spring-cloud-gateway