SpringCloud Gateway使用簡介

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章