Merge microservices governance to main branch (#3066)

* feature:microservices governance modules
pull/3079/head
Steve Rao 2 years ago committed by GitHub
parent 19a477a13a
commit e0636ecfb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -100,6 +100,12 @@
<flatten-maven-plugin.version>1.1.0</flatten-maven-plugin.version>
<gmavenplus-plugin.version>1.6</gmavenplus-plugin.version>
<jacoco.version>0.8.3</jacoco.version>
<!-- Envoy Api Versions -->
<envoy-api.version>1.0.36</envoy-api.version>
<!-- Jose4j Versions -->
<jose4j.version>0.8.0</jose4j.version>
<!-- OpenSergo Versions -->
<opensergo.version>0.1.0-beta2</opensergo.version>
</properties>
<modules>
@ -108,7 +114,7 @@
<module>spring-cloud-alibaba-docs</module>
<module>spring-cloud-alibaba-starters</module>
<module>spring-cloud-alibaba-coverage</module>
</modules>
</modules>
<dependencyManagement>
<dependencies>
@ -148,6 +154,7 @@
<artifactId>rocketmq-acl</artifactId>
<version>${rocketmq.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

@ -231,7 +231,26 @@
<artifactId>spring-cloud-starter-alibaba-appactive</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-opensergo</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-commons</artifactId>

@ -0,0 +1,123 @@
== Spring Cloud Alibaba Governance
image::pic/governance-module.png[]
Spring Cloud Alibaba Governance模块是Spring Cloud Alibaba推出的微服务治理子模块提供了多种类型的微服务治理能力包括标签路由服务鉴权等。并且对接了多种控制面比如IstioOpenSergo让用户无需改造Spring Cloud应用也能实时感知到Istio等治理控制面下发的治理规则并将此规则应用到Spring Cloud应用上从而完成对Spring Cloud应用的治理。
== 如何使用
=== 配置转换
image::pic/resource-transform.png[]
Spring Cloud Alibaba Governance的resource-transform模块会将不同控制面下发的配置进行统一的转换将来自IstioOpenSergo等控制面下发的配置统一转换为Spring Cloud Alibaba统一抽象出的数据结构以供后续使用
如果在您的项目中使用Istio来实现配置转换需要使用group ID 为 `com.alibaba.cloud` 和artifact ID 为 `spring-cloud-starter-alibaba-controlplane-istio` 的starter
[source,xml,indent=0]
----
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
</dependency>
----
之后在application.yml配置文件中配置如下配置
[source,yaml,indent=0]
----
server:
port: ${SERVER_PORT:80}
spring:
cloud:
governance:
auth:
enabled: ${ISTIO_AUTH_ENABLE:true}
istio:
config:
enabled: ${ISTIO_CONFIG_ENABLE:true}
host: ${ISTIOD_ADDR:127.0.0.1}
port: ${ISTIOD_PORT:15010}
polling-pool-size: ${POLLING_POOL_SIZE:10}
polling-time: ${POLLING_TIMEOUT:10}
istiod-token: ${ISTIOD_TOKEN:}
log-xds: ${LOG_XDS:true}
----
各字段的含义如下:
|===
|配置项|key|默认值|说明
|是否开启鉴权| spring.cloud.governance.auth.enabled|true|
|是否连接Istio获取鉴权配置| spring.cloud.istio.config.enabled|true|
|Istiod的地址| spring.cloud.istio.config.host|127.0.0.1|
|Istiod的端口| spring.cloud.istio.config.port|15012|注连接15010端口无需TLS连接15012端口需TLS认证
|SCA去Istio拉取配置的线程池大小| spring.cloud.istio.config.polling-pool-size|10|
|SCA去Istio拉取配置的间隔时间| spring.cloud.istio.config.polling-time|30|单位为秒
|连接Istio 15012端口时使用的JWT token| spring.cloud.istio.config.istiod-token|应用所在pod的 `/var/run/secrets/tokens/istio-token` 文件的内容|
|是否打印xDS相关日志| spring.cloud.istio.config.log-xds|true|
|===
=== 运行应用
需要将应用运行在K8s环境中并给运行的应用将K8s的一些元信息注入以下环境变量中:
|===
|环境变量名|K8s pod metadata name
|POD_NAME|metadata.name
|NAMESPACE_NAME|metadata.namespace
|===
=== 使用标签路由
在引入配置转换模块后我们就能获取到相应的治理规则来对Spring Cloud应用赋予相应的治理能力。标签路由模块可以实现对Spring Cloud应用根据请求头请求参数等标签来路由到不同的服务
如果在您的项目中使用Spring Cloud Alibaba Governance 标签路由需要使用需要使用group ID 为 `com.alibaba.cloud` 和artifact ID 为 `spring-cloud-starter-alibaba-governance-routing` 的starter
=== 运行应用
需要将应用运行在K8s环境中并给运行的应用将K8s的一些元信息注入以下环境变量中:
|===
|环境变量名|K8s pod metadata name
|POD_NAME|metadata.name
|NAMESPACE_NAME|metadata.namespace
|===
=== 使用标签路由
在引入配置转换模块后我们就能获取到相应的治理规则来对Spring Cloud应用赋予相应的治理能力。标签路由模块可以实现对Spring Cloud应用根据请求头请求参数等标签来路由到不同的服务
如果在您的项目中使用Spring Cloud Alibaba Governance 标签路由需要使用需要使用group ID 为 `com.alibaba.cloud` 和artifact ID 为 `spring-cloud-starter-alibaba-governance-routing` 的starter
[source,xml,indent=0]
----
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
----
在引入Istio配置转换模块的前提下标签路由模块支持对以下几种请求的元信息做路由
* 请求路径
* 请求头
* 请求参数
我们使用Istio下发对应的 `DestinationRule` 以及 `VirtualService` ,即可配置对应的标签路由规则,具体的配置方法请参考以下文档与示例
* https://istio.io/latest/zh/docs/reference/config/networking/virtual-service/#VirtualService
* https://istio.io/latest/zh/docs/concepts/traffic-management/#destination-rules
* spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-label-routing-consumer-example
=== 使用服务鉴权
image::pic/auth-process.png[]
在引入配置转换模块后我们就能获取到相应的治理规则来对Spring Cloud应用赋予相应的治理能力。服务鉴权模块给Spring Cloud应用提供多种鉴权方式如IP黑白名单JWT鉴权等
如果在您的项目中使用Spring Cloud Alibaba Governance 标签路由需要使用需要使用group ID 为 `com.alibaba.cloud` 和artifact ID 为 `spring-cloud-starter-alibaba-governance-auth` 的starter
[source,xml,indent=0]
----
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
</dependency>
----
我们使用Istio下发对应的 `AuthorizationPolicy` 以及 `RequestAuthentication` ,即可配置对应的鉴权规则,具体的配置方法请参考以下文档与示例
* https://istio.io/latest/zh/docs/reference/config/security/request_authentication/
* https://istio.io/latest/zh/docs/reference/config/security/authorization-policy/
* spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example
* spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

@ -0,0 +1,111 @@
== Spring Cloud Alibaba Governance
image::pic/governance-module.png[]
Spring Cloud Alibaba Governance module is a micro-service governance sub-module launched by Spring Cloud Alibaba, which provides various types of micro-service governance capabilities, including label routing, service authentication, etc. Moreover, it supports various control planes, such as Istio and OpenSergo, so that users can get the governance rules in real time without modifying Spring Cloud applications, and apply these rules to Spring Cloud applications to govern the Spring Cloud application.
== How to use
=== Resource-Transform
image::pic/resource-transform.png[]
The resource-transform module of Spring Cloud Alibaba Governance will uniformly transform the configurations published by different control planes, like Istio and OperSergo, into the unified abstract data structure of Spring Cloud Alibaba for subsequent use.
If you use Istio in your project to transform the configuration, you need to use a starter with a group ID of `com.alibaba.cloud` and an artifact ID of `spring-cloud-starter-alibaba-controlplane-istio`.
[source,xml,indent=0]
----
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
</dependency>
----
After that, configure the following configuration in the application.yml:
[source,yaml,indent=0]
----
server:
port: ${SERVER_PORT:80}
spring:
cloud:
governance:
auth:
enabled: ${ISTIO_AUTH_ENABLE:true}
istio:
config:
enabled: ${ISTIO_CONFIG_ENABLE:true}
host: ${ISTIOD_ADDR:127.0.0.1}
port: ${ISTIOD_PORT:15010}
polling-pool-size: ${POLLING_POOL_SIZE:10}
polling-time: ${POLLING_TIMEOUT:10}
istiod-token: ${ISTIOD_TOKEN:}
log-xds: ${LOG_XDS:true}
----
Here's an explanation of each field:
|===
|Configuration Item|key|Default Value|Description
|Whether to enable authentication| spring.cloud.governance.auth.enabled|true|
|Whether to connect to Istio to obtain authentication configuration| spring.cloud.istio.config.enabled|true|
|Host of Istiod| spring.cloud.istio.config.host|127.0.0.1|
|Port of Istiod| spring.cloud.istio.config.port|15012|15010 port does not need TLSbut 15012 does
|Thread pool size for SCA to pull the config| spring.cloud.istio.config.polling-pool-size|10|
|Time interval for SCA to pull the config| spring.cloud.istio.config.polling-time|30|The unit is second
|JWT token for SCA to connect to 15012 port| spring.cloud.istio.config.istiod-token|Content of file `/var/run/secrets/tokens/istio-token` in the pod of application|
|Whether to print logs about xDS| spring.cloud.istio.config.log-xds|true|
|===
### Run the application
You need to run the application in the K8s environment and inject some meta information about K8s into the following environment variables for the running application.
|===
|Environment variable name|K8s pod metadata name
|POD_NAME|metadata.name
|NAMESPACE_NAME|metadata.namespace
|===
=== Use Label Routing
With the introduction of the configuration transformation module, we can obtain the governance rules to give the Spring Cloud application some governance capabilities. Label routing module can route the Spring Cloud application according to the request header, request parameters and other tags to route to different services.
If you use Spring Cloud Alibaba Governance Label Routing in your project, You need to use a starter with a group ID of `com.alibaba.cloud` and an artifact ID of `spring-cloud-starter-alibaba-governance-routing`.
[source,xml,indent=0]
----
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
----
With the introduction of Istio Resource Transform module, the label routing module supports routing of the following types of request meta-information:
* Request Path
* Request Header
* Request Param
We use Istio to publish corresponding `DestinationRule` and `VirtualService` to configure corresponding labeled routing rules. For details, see the following documents and examples:
* https://istio.io/latest/zh/docs/reference/config/networking/virtual-service/#VirtualService
* https://istio.io/latest/zh/docs/concepts/traffic-management/#destination-rules
* spring-cloud-alibaba-examples/governance-example/label-routing-example/istio-label-routing-consumer-example
=== Use Authentication
image::pic/auth-process.png[]
With the introduction of the configuration transformation module, we can obtain the governance rules to give the Spring Cloud application some governance capabilities. The Authentication module provides various authentication modes for Spring Cloud applications, such as IP blacklist and whitelist and JWT authentication.
If you use Istio in your project to transform the configuration, you need to use a starter with a group ID of `com.alibaba.cloud` and an artifact ID of `spring-cloud-starter-alibaba-governance-auth`.
[source,xml,indent=0]
----
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
</dependency>
----
We use Istio to publish corresponding `AuthorizationPolicy` and `RequestAuthentication` to configure corresponding Authentication rules. For details, see the following documents and examples
* https://istio.io/latest/zh/docs/reference/config/security/request_authentication/
* https://istio.io/latest/zh/docs/reference/config/security/authorization-policy/
* spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-mvc-example
* spring-cloud-alibaba-examples/governance-example/authentication-example/istio-authentication-provider-webflux-example

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>${revision}</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>istio-authentication-provider-mvc-example</artifactId>
<name>Spring Cloud Starter Alibaba Authentication Provider MVC Example</name>
<description>Example demonstrating how to use authentication on spring mvc application</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,29 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}

@ -0,0 +1,38 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.examples;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class AuthMvcController {
@RequestMapping("/auth")
public String auth(HttpServletRequest request) {
String resp = "received request from " + request.getRemoteHost()
+ ", local addr is " + request.getLocalAddr() + ", local host is "
+ request.getLocalName() + ", request path is" + request.getRequestURI();
return resp;
}
}

@ -0,0 +1,15 @@
server:
port: ${SERVER_PORT:80}
spring:
cloud:
governance:
auth:
enabled: ${ISTIO_AUTH_ENABLE:true}
istio:
config:
enabled: ${ISTIO_CONFIG_ENABLE:true}
host: ${ISTIOD_ADDR:127.0.0.1}
port: ${ISTIOD_PORT:15010}
polling-pool-size: ${POLLING_POOL_SIZE:10}
polling-time: ${POLLING_TIME:10}
istiod-token: ${ISTIOD_TOKEN:}

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>${revision}</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>istio-authentication-provider-webflux-example</artifactId>
<name>Spring Cloud Starter Alibaba Authentication Provider WebFlux Example</name>
<description>Example demonstrating how to use authentication on spring webflux application</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
<exclusions>
<exclusion>
<groupId>spring-web</groupId>
<artifactId>5.2.15.RELEASE</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
<exclusions>
<exclusion>
<groupId>spring-web</groupId>
<artifactId>5.2.15.RELEASE</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

@ -0,0 +1,40 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.examples;
import reactor.core.publisher.Mono;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
@RestController
public class AuthWebFluxController {
@RequestMapping("/auth")
public Mono<String> auth(ServerWebExchange request) {
String resp = "received request from "
+ request.getRequest().getRemoteAddress().getAddress().getHostAddress()
+ ", local addr is "
+ request.getRequest().getLocalAddress().getAddress().getHostAddress()
+ ", local host is "
+ request.getRequest().getLocalAddress().getAddress().getHostName()
+ ", request path is" + request.getRequest().getURI().getPath();
return Mono.just(resp);
}
}

@ -0,0 +1,29 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ScaAuthReactiveApplication {
public static void main(String[] args) {
SpringApplication.run(ScaAuthReactiveApplication.class, args);
}
}

@ -0,0 +1,15 @@
server:
port: ${SERVER_PORT:80}
spring:
cloud:
governance:
auth:
enabled: ${ISTIO_AUTH_ENABLE:true}
istio:
config:
enabled: ${ISTIO_CONFIG_ENABLE:true}
host: ${ISTIOD_ADDR:127.0.0.1}
port: ${ISTIOD_PORT:15010}
polling-pool-size: ${POLLING_POOL_SIZE:10}
polling-time: ${POLLING_TIME:10}
istiod-token: ${ISTIOD_TOKEN:}

@ -0,0 +1,207 @@
# Istio Authentication Example
## 项目说明
本项目演示如何使用 Istio 下发鉴权配置到Spring Cloud Alibaba并对应用做鉴权。Spring Cloud Alibaba鉴权模块同时支持对Spring MVC以及Spring WebFlux应用做鉴权。
## 准备
### 安装K8s环境
请参考K8s的[安装工具](https://kubernetes.io/zh-cn/docs/tasks/tools/)小节。
### 在K8s上安装并启用Istio
请参考Istio官方文档的[安装](https://istio.io/latest/zh/docs/setup/install/)小节。
## Istio鉴权规则介绍
- [授权概述](https://istio.io/latest/zh/docs/concepts/security/#authorization)
- [具体配置方法](https://istio.io/latest/zh/docs/reference/config/security/)
## 示例
### 如何接入
在启动示例进行演示之前,我们先了解一下 Spring Cloud 应用如何接入Istio并提供鉴权功能。 注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。
1. 修改`pom.xml`文件引入Istio资源转换以及Spring Cloud Alibaba鉴权模块:
```xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
</dependency>
```
2. 在应用的 `src/main/resources/application.yml` 配置文件中配置Istio相关元数据:
```yml
server:
port: ${SERVER_PORT:80}
spring:
cloud:
governance:
auth:
enabled: ${ISTIO_AUTH_ENABLE:true}
istio:
config:
enabled: ${ISTIO_CONFIG_ENABLE:true}
host: ${ISTIOD_ADDR:127.0.0.1}
port: ${ISTIOD_PORT:15010}
polling-pool-size: ${POLLING_POOL_SIZE:10}
polling-time: ${POLLING_TIMEOUT:10}
istiod-token: ${ISTIOD_TOKEN:}
log-xds: ${LOG_XDS:true}
```
下面解释一下各字段的含义:
|配置项|key|默认值|说明
|--|--|--|--|
|是否开启鉴权| spring.cloud.governance.auth.enabled|true|
|是否连接Istio获取鉴权配置| spring.cloud.istio.config.enabled|true|
|Istiod的地址| spring.cloud.istio.config.host|127.0.0.1|
|Istiod的端口| spring.cloud.istio.config.port|15012|注连接15010端口无需TLS连接15012端口需TLS认证|
|SCA去Istio拉取配置的线程池大小| spring.cloud.istio.config.polling-pool-size|10|
|SCA去Istio拉取配置的间隔时间| spring.cloud.istio.config.polling-time|30|单位为秒
|连接Istio<br>15012端口时使用的JWT token| spring.cloud.istio.config.istiod-token|应用所在pod的`/var/run/secrets/tokens/istio-token`文件的内容|
|是否打印xDS相关日志| spring.cloud.istio.config.log-xds|true|
### 运行应用
需要将应用运行在K8s环境中并给运行的应用将K8s的一些元信息注入以下环境变量中:
|环境变量名|K8s pod metadata name|
|--|--|
|POD_NAME|metadata.name|
|NAMESPACE_NAME|metadata.namespace|
**注您部署的应用所在的pod不需要被Istio执行自动注入因为Spring Cloud Alibaba的各个治理模块将会被用来替代Envoy Proxy的各种功能。**
### 效果演示
下面给出几个简单的鉴权规则配置的示例:
#### IP黑白名单
我们在使用如下命令通过Istio下发一条鉴权规则至demo应用这条规则的限制了访问该应用的来源IP:
```
kubectl apply -f - << EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: from-ip-allow
namespace: ${namespace_name}
spec:
selector:
matchLabels:
app: ${app_name}
action: DENY
rules:
- from:
- source:
ipBlocks: ["127.0.0.1"]
EOF
```
可以通过请求本demo的auth接口来验证规则是否生效:
```
curl --location --request GET '${demo_ip}/auth'
```
在本例中若请求的来源IP为`127.0.0.1`,则本应用返回:
```
Auth failed, please check the request and auth rule
```
说明此请求被拒绝。<br>
若请求的来源IP不为`127.0.0.1`,则本应用返回:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```
说明通过了SCA的鉴权将会返回此请求的一些元信息。
在此之后我们删除这条IP黑白名单的鉴权规则:
```shell
kubectl delete AuthorizationPolicy from-ip-allow -n ${namespace_name}
```
之后再次请求本demo的auth接口可以发现因为鉴权规则已被删除所以本应用将会返回:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```
#### 请求头认证
我们在使用如下命令通过Istio下发一条鉴权规则至demo应用这条规则的限制了访问该应用的请求header:
```
kubectl apply -f - << EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: http-headers-allow
namespace: ${namespace_name}
spec:
selector:
matchLabels:
app: ${app_name}
action: ALLOW
rules:
- when:
- key: request.headers[User-Agent]
values: ["PostmanRuntime/*"]
EOF
```
之后发送一个带User-Agent头部的HTTP请求来验证规则是否生效:
```
curl --location --request GET '${demo_ip}/auth' \
--header 'User-Agent: PostmanRuntime/7.29.2'
```
由于此请求由于携带了正确的HTTP Header信息将会返回:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```
之后发送一个不带User-Agent头部的HTTP请求来验证规则是否生效:
```
curl --location --request GET '${demo_ip}/auth'
```
由于此请求没有携带正确的HTTP Header信息将会返回:
```
Auth failed, please check the request and auth rule
```
在此之后,我们删除这条请求头认证的规则:
```shell
kubectl delete AuthorizationPolicy http-headers-allow -n ${namespace_name}
```
之后再次请求本demo的auth接口可以发现因为鉴权规则已被删除所以本应用将会返回:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```
#### JWT认证
我们使用如下命令通过Istio下发一条鉴权规则至demo应用这条规则限制了访问该应用需要携带的JWT token value:
```
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: jwt-jwks-uri
namespace: ${namespace_name}
spec:
selector:
matchLabels:
app: ${app_name}
jwtRules:
- issuer: testing@secure.istio.io
jwksUri: https://raw.githubusercontent.com/istio/istio/release-1.5/security/tools/jwt/samples/jwks.json
EOF
```
之后发送一个带正确JWT token的HTTP请求来验证规则是否生效:
```
curl --location --request GET '${demo_ip}/auth' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg'
```
由于此请求由于携带了正确的JWT token信息将会返回:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```
之后再发送一个带错误JWT token的HTTP请求:
```
curl --location --request GET '${demo_ip}/auth' \
--header 'Authorization: Bearer invalid token'
```
由于此请求没有携带正确的JWT token信息将会返回:
```
Auth failed, please check the request and auth rule
```
在此之后我们删除这条JWT认证的规则:
```shell
kubectl delete RequestAuthentication jwt-jwks-uri -n ${namespace_name}
```
之后再次请求本demo的auth接口可以发现因为鉴权规则已被删除所以本应用将会返回:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```

@ -0,0 +1,209 @@
# Istio Authentication Example
## Project Instruction
This project demonstrates how to use Istio to publish authentication config to Spring Cloud Alibaba application and use the config to do authentication. The Spring Cloud Alibaba authentication module supports authentication of Spring MVC and Spring WebFlux applications.
## Preparation
### Install K8s
Please refer to [tools](https://kubernetes.io/zh-cn/docs/tasks/tools/) chapter of K8s document.
### Enable Istio on K8s
Please refer to [install](https://istio.io/latest/zh/docs/setup/install/) chapter of Istio document.
## Introduction to Istio authentication rules
- [overview](https://istio.io/latest/zh/docs/concepts/security/#authorization)
- [detail](https://istio.io/latest/zh/docs/reference/config/security/)
## Demo
### Connect to Istio
Before launching the example for demonstration, let's look at how a Spring Cloud application accesses Istio and provides authentication. This section is only for you to understand how to use it. The config has been filled in this example and you may not need to modify it.
1. Modify `pom.xml` to introduce Istio resource transform and Spring Cloud Alibaba authentication module:
```xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
</dependency>
```
2. Configure Istio related metadata in the `src/main/resources/application` yml configuration file:
```yml
server:
port: ${SERVER_PORT:80}
spring:
cloud:
governance:
auth:
enabled: ${ISTIO_AUTH_ENABLE:true}
istio:
config:
enabled: ${ISTIO_CONFIG_ENABLE:true}
host: ${ISTIOD_ADDR:127.0.0.1}
port: ${ISTIOD_PORT:15010}
polling-pool-size: ${POLLING_POOL_SIZE:10}
polling-time: ${POLLING_TIMEOUT:10}
istiod-token: ${ISTIOD_TOKEN:}
log-xds: ${LOG_XDS:true}
```
Here's an explanation of each field:
|Configuration Item|key|Default Value|Description
|--|--|--|--|
|Whether to enable authentication| spring.cloud.governance.auth.enabled|true|
|Whether to connect to Istio to obtain authentication configuration| spring.cloud.istio.config.enabled|true|
|Host of Istiod| spring.cloud.istio.config.host|127.0.0.1|
|Port of Istiod| spring.cloud.istio.config.port|15012|15010 port does not need TLSbut 15012 does
|Thread pool size for SCA to pull the config| spring.cloud.istio.config.polling-pool-size|10|
|Time interval for SCA to pull the config| spring.cloud.istio.config.polling-time|30|The unit is second|
|JWT token for SCA to connect to 15012 port| spring.cloud.istio.config.istiod-token|Content of file `/var/run/secrets/tokens/istio-token` in the pod of application|
|Whether to print logs about xDS| spring.cloud.istio.config.log-xds|true|
### Run the application
You need to run the application in the K8s environment and inject some meta information about K8s into the following environment variables for the running application:
|Environment variable name|K8s pod metadata name|
|--|--|
|POD_NAME|metadata.name|
|NAMESPACE_NAME|metadata.namespace|
**HINTThe POD in which your deployed application does not need to be automatically injected by Istio because the various governance modules of Spring Cloud Alibaba will be used to replace the functions of the Envoy Proxy.**
### Demostration
The following are some simple examples of authentication rule configurations:
#### IP Blocks
The following command is used to deliver an authentication rule to the demo application through Istio. This rule restricts the source IP addresses that can access the application:
```
kubectl apply -f - << EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: from-ip-allow
namespace: ${namespace_name}
spec:
selector:
matchLabels:
app: ${app_name}
action: DENY
rules:
- from:
- source:
ipBlocks: ["127.0.0.1"]
EOF
```
You can validate the rules by sending request to the auth interface of this demo:
```
curl --location --request GET '${demo_ip}/auth'
```
In this example, if the source IP of the request is 127.0.0.1, then the application returns:
```
Auth failed, please check the request and auth rule
```
This indicates that the request is denied.<br>
If the source IP of the request is not '127.0.0.1', then the application returns:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```
It indicates that the request has been authenticated by SCA and some meta data of the request will be returned.
After that, we delete the authentication rule for the IP Blocks:
```shell
kubectl delete AuthorizationPolicy from-ip-allow -n ${namespace_name}
```
Then request the auth interface of this demo again, we can find that the application will return the following message because the authentication rule has been deleted:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```
#### Request Header Authentication
We use the following command to deliver an authentication rule to the demo application through Istio. This rule restricts the request header for accessing the application:
```
kubectl apply -f - << EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: http-headers-allow
namespace: ${namespace_name}
spec:
selector:
matchLabels:
app: ${app_name}
action: ALLOW
rules:
- when:
- key: request.headers[User-Agent]
values: ["PostmanRuntime/*"]
EOF
```
Then send a HTTP request with a user-agent header to verify whether the rule is valid:
```
curl --location --request GET '${demo_ip}/auth' \
--header 'User-Agent: PostmanRuntime/7.29.2'
```
Since this request carries a correct HTTP Header, it will return:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```
Then send a HTTP request without a user-agent header to verify whether the rule is valid:
```
curl --location --request GET '${demo_ip}/auth'
```
Since this request don't carry a correct HTTP Header, it will return:
```
Auth failed, please check the request and auth rule
```
After that, we remove the rule for requests header authentication:
```shell
kubectl delete AuthorizationPolicy http-headers-allow -n ${namespace_name}
```
Then request the auth interface of this demo again, we can find that the application will return the following message because the authentication rule has been deleted:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```
#### JWT Authentication
We use the following command to deliver an authentication rule to the demo application through Istio. This rule restricts the JWT token value that must be carried to access the application:
```
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: jwt-jwks-uri
namespace: ${namespace_name}
spec:
selector:
matchLabels:
app: ${app_name}
jwtRules:
- issuer: testing@secure.istio.io
jwksUri: https://raw.githubusercontent.com/istio/istio/release-1.5/security/tools/jwt/samples/jwks.json
EOF
```
An Http request with a correct JWT token is then sent to verify that the rule is valid:
```
curl --location --request GET '${demo_ip}/auth' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg'
```
Since this request carries a correct JWT token, it will return:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```
An Http request with a invalid JWT token is then sent to verify that the rule is valid:
```
curl --location --request GET '${demo_ip}/auth' \
--header 'Authorization: Bearer invalid token'
```
Since this request carries a invalid JWT token, it will return:
```
Auth failed, please check the request and auth rule
```
After that, we remove the rule for JWT authentication:
```shell
kubectl delete RequestAuthentication jwt-jwks-uri -n ${namespace_name}
```
Then request the auth interface of this demo again, we can find that the application will return the following message because the authentication rule has been deleted:
```
received request from ${from_ip}, local addr is ${local_ip}, local host is ${local_host}, request path is/auth
```

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>${revision}</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>consumer-example</artifactId>
<name>Spring Cloud Starter Alibaba Label Routing Consumer Example</name>
<description>Example demonstrating how to use label routing consumer</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,178 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.examples;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.cloud.commons.governance.event.LabelRoutingDataChangedEvent;
import com.alibaba.cloud.commons.governance.labelrouting.LabelRouteRule;
import com.alibaba.cloud.commons.governance.labelrouting.MatchService;
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule;
import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule;
import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author HH
*/
@SpringBootApplication
@EnableDiscoveryClient(autoRegister = true)
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@FeignClient(name = "service-provider")
public interface FeignService {
/**
* Feign test.
* @return String
*/
@GetMapping("/test")
String test();
}
@RestController
public class Controller implements ApplicationContextAware {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private ConsumerApplication.FeignService feignService;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
@GetMapping("/router-test")
public String notFound() {
return feignService.test();
}
@GetMapping("/add")
public void getDataFromControlPlaneTest() {
List<RouteRule> routeRules = new ArrayList<>();
List<MatchService> matchServices = new ArrayList<>();
UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure();
unifiedRouteDataStructure.setTargetService("service-provider");
LabelRouteRule labelRouteData = new LabelRouteRule();
labelRouteData.setDefaultRouteVersion("v1");
RouteRule routeRule = new HeaderRule();
routeRule.setType("header");
routeRule.setCondition("=");
routeRule.setKey("tag");
routeRule.setValue("gray");
RouteRule routeRule1 = new UrlRule.Parameter();
routeRule1.setType("parameter");
routeRule1.setCondition(">");
routeRule1.setKey("id");
routeRule1.setValue("10");
RouteRule routeRule2 = new UrlRule.Path();
routeRule2.setType("path");
routeRule2.setCondition("=");
routeRule2.setValue("/router-test");
routeRules.add(routeRule);
routeRules.add(routeRule1);
routeRules.add(routeRule2);
MatchService matchService = new MatchService();
matchService.setVersion("v2");
matchService.setWeight(100);
matchService.setRuleList(routeRules);
matchServices.add(matchService);
labelRouteData.setMatchRouteList(matchServices);
unifiedRouteDataStructure.setLabelRouteRule(labelRouteData);
List<UnifiedRouteDataStructure> unifiedRouteDataStructureList = new ArrayList<>();
unifiedRouteDataStructureList.add(unifiedRouteDataStructure);
applicationContext.publishEvent(new LabelRoutingDataChangedEvent(this,
unifiedRouteDataStructureList));
}
@GetMapping("/update")
public void updateDataFromControlPlaneTest() {
List<RouteRule> routeRules = new ArrayList<>();
List<MatchService> matchServices = new ArrayList<>();
UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure();
unifiedRouteDataStructure.setTargetService("service-provider");
LabelRouteRule labelRouteData = new LabelRouteRule();
labelRouteData.setDefaultRouteVersion("v1");
RouteRule routeRule = new HeaderRule();
routeRule.setType("header");
routeRule.setCondition("=");
routeRule.setKey("tag");
routeRule.setValue("gray");
RouteRule routeRule1 = new UrlRule.Parameter();
routeRule1.setType("parameter");
routeRule1.setCondition(">");
routeRule1.setKey("id");
routeRule1.setValue("10");
RouteRule routeRule2 = new UrlRule.Path();
routeRule2.setType("path");
routeRule2.setCondition("=");
routeRule2.setValue("/router-test");
routeRules.add(routeRule);
routeRules.add(routeRule1);
routeRules.add(routeRule2);
MatchService matchService = new MatchService();
matchService.setVersion("v2");
matchService.setWeight(50);
matchService.setRuleList(routeRules);
matchServices.add(matchService);
labelRouteData.setMatchRouteList(matchServices);
unifiedRouteDataStructure.setLabelRouteRule(labelRouteData);
List<UnifiedRouteDataStructure> unifiedRouteDataStructureList = new ArrayList<>();
unifiedRouteDataStructureList.add(unifiedRouteDataStructure);
applicationContext.publishEvent(new LabelRoutingDataChangedEvent(this,
unifiedRouteDataStructureList));
}
}
}

@ -0,0 +1,8 @@
spring.application.name=service-consumer
server.port=18083
management.endpoints.web.exposure.include=*
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.fail-fast=true
spring.cloud.nacos.username=nacos
spring.cloud.nacos.password=nacos
#spring.cloud.governance.router.rule=RandomRule

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>${revision}</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>default-provider-version-example</artifactId>
<name>Spring Cloud Starter Alibaba Label Routing Provider Example</name>
<description>Example demonstrating how to use label routing provider</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,55 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.examples;
import com.alibaba.cloud.nacos.registry.NacosRegistration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author HH
*/
@EnableDiscoveryClient
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
@Autowired
NacosRegistration nacosRegistration;
@RestController
class Controller {
@GetMapping("/test")
public String test() {
String host = nacosRegistration.getHost();
int port = nacosRegistration.getPort();
String version = nacosRegistration.getMetadata().get("version");
return "Route in " + host + ": " + port + ",version is " + version + ".";
}
}
}

@ -0,0 +1,12 @@
server.port=18081
spring.application.name=service-provider
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.enabled=true
spring.cloud.nacos.discovery.metadata.version=v1
spring.cloud.nacos.username=nacos
spring.cloud.nacos.password=nacos
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>${revision}</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>label-routing-istio-consumer-example</artifactId>
<name>Spring Cloud Starter Alibaba Istio Label Routing Consumer Example</name>
<description>Example demonstrating how to use Istio label routing consumer</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,65 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.examples;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author HH
*/
@SpringBootApplication
@EnableDiscoveryClient(autoRegister = true)
@EnableFeignClients
public class IstioConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(IstioConsumerApplication.class, args);
}
@FeignClient(name = "service-provider")
public interface FeignService {
/**
* Feign test.
* @return String
*/
@GetMapping("/test")
String test();
}
@RestController
public class Controller {
@Autowired
private IstioConsumerApplication.FeignService feignService;
@GetMapping("/istio-label-routing")
public String labelRouting() {
return feignService.test();
}
}
}

@ -0,0 +1,25 @@
server:
port: 18084
spring:
main:
allow-bean-definition-overriding: true
application:
name: service-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
fail-fast: true
username: nacos
password: nacos
governance:
auth:
enabled: ${ISTIO_AUTH_ENABLE:false}
istio:
config:
enabled: ${ISTIO_CONFIG_ENABLE:true}
host: ${ISTIOD_ADDR:127.0.0.1}
port: ${ISTIOD_PORT:15010}
polling-pool-size: ${POLLING_POOL_SIZE:10}
polling-time: ${POLLING_TIME:10}
istiod-token: ${ISTIOD_TOKEN:}

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>${revision}</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>opensergo-consumer-example</artifactId>
<name>Spring Cloud Starter Alibaba OpenSergo Label Routing Consumer Example</name>
<description>Example demonstrating how to use OpenSergo label routing consumer</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-opensergo</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,65 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.examples;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author HH
*/
@SpringBootApplication
@EnableDiscoveryClient(autoRegister = true)
@EnableFeignClients
public class OpenSergoConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(OpenSergoConsumerApplication.class, args);
}
@FeignClient(name = "service-provider")
public interface FeignService {
/**
* Feign test.
* @return String
*/
@GetMapping("/test")
String test();
}
@RestController
public class Controller {
@Autowired
private OpenSergoConsumerApplication.FeignService feignService;
@GetMapping("/router-test")
public String labelRouting() {
return feignService.test();
}
}
}

@ -0,0 +1,8 @@
spring.application.name=service-consumer
server.port=18083
management.endpoints.web.exposure.include=*
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.fail-fast=true
spring.cloud.nacos.username=nacos
spring.cloud.nacos.password=nacos
spring.cloud.opensergo.endpoint=127.0.0.1:10246

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>${revision}</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>provider-version-example</artifactId>
<name>Spring Cloud Starter Alibaba Label Routing Provider Example</name>
<description>Example demonstrating how to use label routing provider</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,55 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.examples;
import com.alibaba.cloud.nacos.registry.NacosRegistration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author HH
*/
@EnableDiscoveryClient
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
@Autowired
NacosRegistration nacosRegistration;
@RestController
class Controller {
@GetMapping("/test")
public String test() {
String host = nacosRegistration.getHost();
int port = nacosRegistration.getPort();
String version = nacosRegistration.getMetadata().get("version");
return "Route in " + host + ": " + port + ",version is " + version + ".";
}
}
}

@ -0,0 +1,11 @@
server.port=18082
spring.application.name=service-provider
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.enabled=true
spring.cloud.nacos.discovery.metadata.version=v2
spring.cloud.nacos.username=nacos
spring.cloud.nacos.password=nacos
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

@ -0,0 +1,440 @@
# label route example
## 项目说明
本项目演示如何使用 spring cloud ailbaba governance labelrouting 模块完成标签路由功能。
## 模块结构
本模块包括一个消费者实例和一个提供者集群,该集群包含着两个实例。
## 示例
### 如何接入
**注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。**
1. 首先,修改需要进行路由服务的`pom.xml` 文件,引入 spring cloud ailbaba governance labelrouting依赖。
```xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
```
### 应用启动
启动一个三个模块的启动类分别为ConsumerApplication两个ProviderApplication将其注入到Nacos注册中心中。
### 效果演示
#### 规则说明
实例中设置的规则如下:
```java
@GetMapping("/add")
public void getDataFromControlPlaneTest() {
List<RouteRule> routeRules = new ArrayList<>();
List<MatchService> matchServices = new ArrayList<>();
UnifiedRouteDataStructure unifiedRouteDataStructure = new UntiedRouteDataStructure();
unifiedRouteDataStructure.setTargetService("service-provider");
LabelRouteRule labelRouteData = new LabelRouteRule();
labelRouteData.setDefaultRouteVersion("v1");
RouteRule routeRule = new HeaderRule();
routeRule.setType("header");
routeRule.setCondition("=");
routeRule.setKey("tag");
routeRule.setValue("gray");
RouteRule routeRule1 = new UrlRule.Parameter();
routeRule1.setType("parameter");
routeRule1.setCondition(">");
routeRule1.setKey("id");
routeRule1.setValue("10");
RouteRule routeRule2 = new UrlRule.Path();
routeRule2.setType("path");
routeRule2.setCondition("=");
routeRule2.setValue("/router-test");
routeRules.add(routeRule);
routeRules.add(routeRule1);
routeRules.add(routeRule2);
MatchService matchService = new MatchService();
matchService.setVersion("v2");
matchService.setWeight(100);
matchService.setRuleList(routeRules);
matchServices.add(matchService);
labelRouteData.setMatchRouteList(matchServices);
unifiedRouteDataStructure.setLabelRouteRule(labelRouteData);
List<UntiedRouteDataStructure> unifiedRouteDataStructureList = new ArrayList<>();
unifiedRouteDataStructureList.add(unifiedRouteDataStructure);
controlPlaneConnection.pushRouteData(unifiedRouteDataStructureList);
}
```
代码对应的规则如下:
若同时满足请求参数中含有tag=gray请求头中含有id且值小于10uri为/router-test则流量全部路由到v2版本中若有一条不满足则流量路由到v1版本中。
规则也支持动态修改,测试动态修改的规则如下:
```java
@GetMapping("/add")
public void getDataFromControlPlaneTest() {
List<RouteRule> routeRules = new ArrayList<>();
List<MatchService> matchServices = new ArrayList<>();
UntiedRouteDataStructure unifiedRouteDataStructure = new UntiedRouteDataStructure();
unifiedRouteDataStructure.setTargetService("service-provider");
LabelRouteRule labelRouteData = new LabelRouteRule();
labelRouteData.setDefaultRouteVersion("v1");
RouteRule routeRule = new HeaderRule();
routeRule.setType("header");
routeRule.setCondition("=");
routeRule.setKey("tag");
routeRule.setValue("gray");
RouteRule routeRule1 = new UrlRule.Parameter();
routeRule1.setType("parameter");
routeRule1.setCondition(">");
routeRule1.setKey("id");
routeRule1.setValue("10");
RouteRule routeRule2 = new UrlRule.Path();
routeRule2.setType("path");
routeRule2.setCondition("=");
routeRule2.setValue("/router-test");
routeRules.add(routeRule);
routeRules.add(routeRule1);
routeRules.add(routeRule2);
MatchService matchService = new MatchService();
matchService.setVersion("v2");
matchService.setWeight(50);
matchService.setRuleList(routeRules);
matchServices.add(matchService);
labelRouteData.setMatchRouteList(matchServices);
unifiedRouteDataStructure.setLabelRouteRule(labelRouteData);
List<UntiedRouteDataStructure> unifiedRouteDataStructureList = new ArrayList<>();
unifiedRouteDataStructureList.add(unifiedRouteDataStructure);
controlPlaneConnection.pushRouteData(unifiedRouteDataStructureList);
}
```
代码对应的规则如下:
若同时满足请求参数中含有tag=gray请求头中含有id且值小于10uri为/router-test则50%流量路由到v2版本中剩下的流量路由到v1版本中若有一条不满足则流量路由到v1版本中。
##### 演示步骤
1. 访问 http://localhost:18083/add 将路由规则由控制面接口推入路由规则仓库中。
访问 http://localhost:18083/router-test 不满足路由规则路由到v1版本中v1版本实例打印返回如下结果
```
Route in 30.221.132.228: 18081,version is v1.
```
访问 http://localhost:18083/router-test?id=11 且请求头设置test值为gray 满足路由规则路由到v2版本中v2版本实例打印返回如下结果
```
Route in 30.221.132.228: 18082,version is v2.
```
2. 访问 http://localhost:18083/update 模拟动态修改路由规则。
访问 http://localhost:18083/router-test 不满足路由规则路由到v1版本中v1版本实例打印返回如下结果
```
Route in 30.221.132.228: 18081,version is v1.
```
访问 http://localhost:18083/router-test?id=11 且请求头设置test值为gray 满足路由规则50%路由到v2版本中v2版本实例打印返回如下结果
```
Route in 30.221.132.228: 18082,version is v2.
```
50%路由到v1版本中v1版本实例打印返回如下结果
```
Route in 30.221.132.228: 18081,version is v1.
```
3. 如果不推送规则,走正常路由
## 集成Istio
**注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。**
### 安装K8s环境
请参考K8s的[安装工具](https://kubernetes.io/zh-cn/docs/tasks/tools/)小节。
### 在K8s上安装并启用Istio
请参考Istio官方文档的[安装](https://istio.io/latest/zh/docs/setup/install/)小节。
### Istio流量治理规则介绍
- [VirtualService](https://istio.io/latest/zh/docs/reference/config/networking/virtual-service/)
- [DestinationRule](https://istio.io/latest/zh/docs/reference/config/networking/destination-rule/)
### 配置
1. 首先修改pom.xml 文件,引入`spring-cloud-starter-alibaba-governance-routing`依赖。同时引入Spring Cloud Alibaba的`spring-cloud-starter-alibaba-controlplane-istio`模块
```xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
</dependency>
```
2. 在`src/main/resources/application.yml`配置文件中配置Istio控制面的相关信息:
```
server:
port: 18084
spring:
main:
allow-bean-definition-overriding: true
application:
name: service-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
fail-fast: true
username: nacos
password: nacos
governance:
auth:
# 是否开启鉴权
enabled: ${ISTIO_AUTH_ENABLE:false}
istio:
config:
# 是否开启Istio配置转换
enabled: ${ISTIO_CONFIG_ENABLE:true}
# Istiod ip
host: ${ISTIOD_ADDR:127.0.0.1}
# Istiod 端口
port: ${ISTIOD_PORT:15010}
# 轮询Istio线程池大小
polling-pool-size: ${POLLING_POOL_SIZE:10}
# 轮询Istio时间间隔
polling-time: ${POLLING_TIME:10}
# Istiod鉴权token(访问Istiod 15012端口时可用)
istiod-token: ${ISTIOD_TOKEN:}
# 是否打印xds相关日志
log-xds: ${LOG_XDS:true}
```
### 应用启动
启动三个模块的启动类分别为IstioConsumerApplication两个ProviderApplication将其注入到Nacos注册中心中。
### 下发配置
我们通过Istio控制面下发标签路由规则首先下发DestinationRule规则:
```
kubectl apply -f - << EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-destination-rule
spec:
host: sca-virtual-service
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
EOF
```
此规则将后端服务拆分为两个版本label为v1的pod被分到v1版本label为v2的pod被分到v2版本
之后我们下发VirtualService规则:
```
kubectl apply -f - << EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: sca-virtual-service
spec:
hosts:
- service-provider
http:
- match:
- headers:
tag:
exact: gray
uri:
exact: /istio-label-routing
route:
- destination:
host: service-provider
subset: v2
- route:
- destination:
host: service-provider
subset: v1
EOF
```
这条VirtualService指定了一条最简单的标签路由规则将请求头tag为gray请求路径为/istio-label-routing的HTTP请求路由到v2版本其余的流量都路由到v1版本:
### 效果演示
我们发送一条不带请求头的HTTP请求至IstioConsumerApplication:
```
curl --location --request GET '127.0.0.1:18084/istio-label-routing'
```
因为请求头不为gray所以请求将会被路由到v1版本返回如下:
```
Route in 30.221.132.228: 18081,version is v1.
```
之后我们发送一条请求头tag为gray且请求路径为/istio-label-routing的HTTP请求:
```
curl --location --request GET '127.0.0.1:18084/istio-label-routing' --header 'tag: gray'
```
因为满足路由规则所以请求会被路由至v2版本:
```
Route in 30.221.132.228: 18082,version is v2.
```
最后我们删除这条标签路由规则:
```shell
kubectl delete VirtualService sca-virtual-service
kubectl delete DestinationRule my-destination-rule
```
删除规则后,可以看到路由的策略将不由请求头的携带与否来决定,而是完全遵从于负载均衡器的实现。
## 集成OpenSergo
**注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。**
1. 首先,修改`pom.xml` 文件,引入`spring-cloud-starter-alibaba-governance-routing`依赖。同时引入Spring Cloud Alibaba的`spring-cloud-starter-alibaba-controlplane-opensergo`模块
```
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-opensergo</artifactId>
</dependency>
```
2. 在`application.properties`配置文件中配置OpenSergo控制面的相关信息
```
# OpenSergo 控制面 endpoint
spring.cloud.opensergo.endpoint=127.0.0.1:10246
```
### 应用启动
启动三个模块的启动类分别为OpenSergoConsumerApplication两个ProviderApplication将其注入到Nacos注册中心中。
### 下发配置
[启动 OpenSergo 控制面](https://opensergo.io/zh-cn/docs/quick-start/opensergo-control-plane/) ,并通过 OpenSergo 控制面下发流量路由规则
```
kubectl apply -f - << EOF
apiVersion: traffic.opensergo.io/v1alpha1
kind: TrafficRouter
metadata:
name: service-provider
namespace: default
labels:
app: service-provider
spec:
hosts:
- service-provider
http:
- match:
- headers:
tag:
exact: v2
route:
- destination:
host: service-provider
subset: v2
fallback:
host: service-provider
subset: v1
- route:
- destination:
host: service-provider
subset: v1
EOF
```
这条TrafficRouter指定了一条最简单的流量路由规则将请求头tag为v2的HTTP请求路由到v2版本其余的流量都路由到v1版本。
如果v2版本没有对应的节点则将流量fallback至v1版本。
### 效果演示
我们发送一条不带请求头的HTTP请求至IstioConsumerApplication:
```
curl --location --request GET '127.0.0.1:18083/router-test'
```
因为请求头不为gray所以请求将会被路由到v1版本返回如下:
```
Route in 30.221.132.228: 18081,version is v1.
```
之后我们发送一条请求头tag为gray的HTTP请求
```
curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: v2'
```
因为满足路由规则所以请求会被路由至v2版本:
```
Route in 30.221.132.228: 18082,version is v2.
```
最后我们删除这条标签路由规则:
```shell
kubectl delete VirtualService sca-virtual-service
kubectl delete DestinationRule my-destination-rule
```
删除规则后,可以看到路由的策略将不由请求头的携带与否来决定,而是完全遵从于负载均衡器的实现。
## 集成OpenSergo
**注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。**
1. 首先修改pom.xml 文件,引入`spring-cloud-starter-alibaba-governance-routing`依赖。同时引入Spring Cloud Alibaba的`spring-cloud-starter-alibaba-controlplane-opensergo`模块
```
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-opensergo</artifactId>
</dependency>
```
2. 在application.properties配置文件中配置OpenSergo控制面的相关信息
```
# OpenSergo 控制面 endpoint
spring.cloud.opensergo.endpoint=127.0.0.1:10246
```
### 应用启动
启动三个模块的启动类分别为OpenSergoConsumerApplication两个ProviderApplication将其注入到Nacos注册中心中。
### 下发配置
[启动 OpenSergo 控制面](https://opensergo.io/zh-cn/docs/quick-start/opensergo-control-plane/) ,并通过 OpenSergo 控制面下发流量路由规则
```
kubectl apply -f - << EOF
apiVersion: traffic.opensergo.io/v1alpha1
kind: TrafficRouter
metadata:
name: service-provider
namespace: default
labels:
app: service-provider
spec:
hosts:
- service-provider
http:
- match:
- headers:
tag:
exact: v2
route:
- destination:
host: service-provider
subset: v2
fallback:
host: service-provider
subset: v1
- route:
- destination:
host: service-provider
subset: v1
EOF
```
这条TrafficRouter指定了一条最简单的流量路由规则将请求头tag为v2的HTTP请求路由到v2版本其余的流量都路由到v1版本。
如果v2版本没有对应的节点则将流量fallback至v1版本。
### 效果演示
我们发送一条不带请求头的HTTP请求至IstioConsumerApplication
```
curl --location --request GET '127.0.0.1:18083/router-test'
```
因为请求头不为gray所以请求将会被路由到v1版本返回如下
```
Route in 30.221.132.228: 18081,version is v1.
```
之后我们发送一条请求头tag为gray的HTTP请求
```
curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: v2'
```
因为满足路由规则所以请求会被路由至v2版本
```
Route in 30.221.132.228: 18082,version is v2.
```
我们停止v2版本的ProviderApplication后继续发送一条请求头tag为gray的HTTP请求
```
curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: v2'
```
因为v2版本没有服务提供者因此流量被fallback至v1版本。
```
Route in 30.221.132.228: 18081,version is v1.
```

@ -0,0 +1,355 @@
# label route example
## Project Description
This project demonstrates how to use the spring cloud ailbaba governance labelrouting module to complete the label routing function.
## module structure
This module includes a consumer instance and a provider cluster, which contains two instances.
## Example
### How to access
**Note that this section is only for your convenience in understanding the access method. The access work has been completed in this sample code, and you do not need to modify it.**
1. First, modify the pom XML file, which introduces the spring cloud ailbaba governance labelrouting dependency.
```xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
```
### Application Start
Start a startup class of three modules, ConsumerApplication and two ProviderApplications, and inject them into the Nacos registry.
### Effect demonstration
#### Rule Description
The rules set in the instance are as follows:
```java
@GetMapping("/add")
public void getDataFromControlPlaneTest() {
List<RouteRule> routeRules = new ArrayList<>();
List<MatchService> matchServices = new ArrayList<>();
UnifiedRouteDataStructure unifiedRouteDataStructure = new UntiedRouteDataStructure();
unifiedRouteDataStructure.setTargetService("service-provider");
LabelRouteRule labelRouteData = new LabelRouteRule();
labelRouteData.setDefaultRouteVersion("v1");
RouteRule routeRule = new HeaderRule();
routeRule.setType("header");
routeRule.setCondition("=");
routeRule.setKey("tag");
routeRule.setValue("gray");
RouteRule routeRule1 = new UrlRule.Parameter();
routeRule1.setType("parameter");
routeRule1.setCondition(">");
routeRule1.setKey("id");
routeRule1.setValue("10");
RouteRule routeRule2 = new UrlRule.Path();
routeRule2.setType("path");
routeRule2.setCondition("=");
routeRule2.setValue("/router-test");
routeRules.add(routeRule);
routeRules.add(routeRule1);
routeRules.add(routeRule2);
MatchService matchService = new MatchService();
matchService.setVersion("v2");
matchService.setWeight(100);
matchService.setRuleList(routeRules);
matchServices.add(matchService);
labelRouteData.setMatchRouteList(matchServices);
unifiedRouteDataStructure.setLabelRouteRule(labelRouteData);
List<UntiedRouteDataStructure> unifiedRouteDataStructureList = new ArrayList<>();
unifiedRouteDataStructureList.add(unifiedRouteDataStructure);
controlPlaneConnection.pushRouteData(unifiedRouteDataStructureList);
}
```
The rules corresponding to the code are as follows:
If the request parameter contains tag=gray and the request header contains id and the value is greater than 10, uri is /router-test at the same time, the traffic is routed to the v2 version. If one of the request parameters does not meet the requirement, the traffic is routed to the v1 version.
Rules also support dynamic modification. The rules for testing dynamic modification are as follows:
```java
@GetMapping("/add")
public void getDataFromControlPlaneTest() {
List<RouteRule> routeRules = new ArrayList<>();
List<MatchService> matchServices = new ArrayList<>();
UntiedRouteDataStructure unifiedRouteDataStructure = new UntiedRouteDataStructure();
unifiedRouteDataStructure.setTargetService("service-provider");
LabelRouteRule labelRouteData = new LabelRouteRule();
labelRouteData.setDefaultRouteVersion("v1");
RouteRule routeRule = new HeaderRule();
routeRule.setType("header");
routeRule.setCondition("=");
routeRule.setKey("tag");
routeRule.setValue("gray");
RouteRule routeRule1 = new UrlRule.Parameter();
routeRule1.setType("parameter");
routeRule1.setCondition(">");
routeRule1.setKey("id");
routeRule1.setValue("10");
RouteRule routeRule2 = new UrlRule.Path();
routeRule2.setType("path");
routeRule2.setCondition("=");
routeRule2.setValue("/router-test");
routeRules.add(routeRule);
routeRules.add(routeRule1);
routeRules.add(routeRule2);
MatchService matchService = new MatchService();
matchService.setVersion("v2");
matchService.setWeight(50);
matchService.setRuleList(routeRules);
matchServices.add(matchService);
labelRouteData.setMatchRouteList(matchServices);
unifiedRouteDataStructure.setLabelRouteRule(labelRouteData);
List<UntiedRouteDataStructure> unifiedRouteDataStructureList = new ArrayList<>();
unifiedRouteDataStructureList.add(unifiedRouteDataStructure);
controlPlaneConnection.pushRouteData(unifiedRouteDataStructureList);
}
```
The rules corresponding to the code are as follows:
If the request parameter contains tag=gray, and the request header contains id and the value is greater than 10,uri is /router-test, 50% of the traffic is routed to the v2 version, and the rest is routed to the v1 version. If one of the traffic does not meet the requirements, the traffic is routed to the v1 version.
##### demonstrate Steps
1. visit http://localhost:18083/add Push the routing rules from the control surface interface to the routing rule warehouse
visit http://localhost:18083/router -The test does not meet the routing rules. When the test is routed to the v1 version, the v1 version instance prints and returns the following results:
```
Route in 30.221.132.228: 18081,version is v1.
```
visit http://localhost:18083/router-test?id=11 and the test value set in the request header is gray, which meets the routing rules. The route is to the v2 version. The v2 version instance prints and returns the following results:
```
Route in 30.221.132.228: 18082,version is v2.
```
2. visit http://localhost:18083/update Simulate dynamic modification of routing rules.
visit http://localhost:18083/router The test does not meet the routing rules. When the test is routed to the v1 version, the v1 version instance prints and returns the following results:
```
Route in 30.221.132.228: 18081,version is v1.
```
visit http://localhost:18083/router-test?id=11 and the test value set in the request header is gray, which meets the routing rules. 50% of the routes are routed to the v2 version. The v2 version instance prints the following results:
```
Route in 30.221.132.228: 18082,version is v2.
```
50% of them are routed to the v1 version, and the following results are returned when the v1 version instance is printed:
```
Route in 30.221.132.228: 18081,version is v1.
```
3. If you don't push rule,it will load balance by common rule you set.
## Integrating Istio
**Note that this section is only for your convenience in understanding the access method. The access work has been completed in this sample code, and you do not need to modify it.**
## Preparation
### Install K8s
Please refer to [tools](https://kubernetes.io/zh-cn/docs/tasks/tools/) chapter of K8s document.
### Enable Istio on K8s
Please refer to [install](https://istio.io/latest/zh/docs/setup/install/) chapter of Istio document.
### Introduction to Istio traffic control rules
- [overview](https://istio.io/latest/zh/docs/concepts/security/#authorization)
- [detail](https://istio.io/latest/zh/docs/reference/config/security/)
1. First, modify the pom.xml file to introduce the `spring-cloud-starter-alibaba-governance-routing` and `spring-cloud-starter-alibaba-controlplane-istio` dependency
```xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
</dependency>
```
2. Configure application.yml for Istio control plane:
```
server:
port: 18084
spring:
main:
allow-bean-definition-overriding: true
application:
name: service-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
fail-fast: true
username: nacos
password: nacos
governance:
auth:
# Is authentication enabled
enabled: ${ISTIO_AUTH_ENABLE:false}
istio:
config:
# Is Istio resource transform enabled
enabled: ${ISTIO_CONFIG_ENABLE:true}
# Istiod ip
host: ${ISTIOD_ADDR:127.0.0.1}
# Istiod port
port: ${ISTIOD_PORT:15010}
# Istiod thread-pool size
polling-pool-size: ${POLLING_POOL_SIZE:10}
# Istiod polling gap
polling-time: ${POLLING_TIME:10}
# Istiod token(For Istio 15012 port)
istiod-token: ${ISTIOD_TOKEN:}
# Whether to print xds log
log-xds: ${LOG_XDS:true}
```
### Startup Application
Start IstioConsumerApplication and two ProviderApplications, and inject it into the Nacos registry center.
### Publish Configuration
We publish the label routing rules through the Istio control plane. We publish a DestinationRule rule first:
```
kubectl apply -f - << EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-destination-rule
spec:
host: sca-virtual-service
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
EOF
```
This rule splits the back-end service into two versions. Pod with label v1 is assigned to v1, and pod with label v2 is assigned to v2
After that, we publish the VirtualService rule:
```
kubectl apply -f - << EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: sca-virtual-service
spec:
hosts:
- service-provider
http:
- match:
- headers:
tag:
exact: gray
uri:
exact: /istio-label-routing
route:
- destination:
host: service-provider
subset: v2
- route:
- destination:
host: service-provider
subset: v1
EOF
```
This VirtualService specifies the simplest label routing rule. HTTP requests with a gray header and /istio-label-routing path are routed to v2, and the rest of the traffic is routed to v1:
### Demonstrate effect
We send an HTTP request without a request header to IstioConsumerApplication:
```
curl --location --request GET '127.0.0.1:18084/istio-label-routing'
```
Since the request header is not gray, the request will be routed to version v1 with the following result:
```
Route in 30.221.132.228: 18081,version is v1.
```
We then send an HTTP request with a gray tag in its header and the request path is /istio-label-routing:
```
curl --location --request GET '127.0.0.1:18084/istio-label-routing' --header 'tag: gray'
```
The request is routed to version v2 because the routing rule is matched by the request:
```
Route in 30.221.132.228: 18082,version is v2.
```
Finally, we delete this label routing rule::
```shell
kubectl delete VirtualService sca-virtual-service
kubectl delete DestinationRule my-destination-rule
```
After the rule is deleted, the routing policy is not determined by whether the request header is carried or not, but completely depends on the implementation of the loadbalancer。
## Integrating OpenSergo
**Note that this section is only for your convenience in understanding the access method. The access work has been completed in this sample code, and you do not need to modify it.**
### Configure
1. First, modify the pom.xml file to introduce the `spring-cloud-starter-alibaba-governance-routing` and `spring-cloud-starter-alibaba-controlplane-opensergo` dependency
```
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-controlplane-opensergo</artifactId>
</dependency>
```
2. Configure application.yml for OpenSergo control plane
```
# The endpoint of OpenSergo ControlPlane
spring.cloud.opensergo.endpoint=127.0.0.1:10246
```
### Startup Application
Start OpenSergoConsumerApplication and two ProviderApplications, and inject it into the Nacos registry center.
### Publish Configuration
[First start OpenSergo control plan](https://opensergo.io/docs/quick-start/opensergo-control-plane/) , Then we publish the label routing rules through the OpenSergo control plane. We publish a TrafficRouter rule.
```
kubectl apply -f - << EOF
apiVersion: traffic.opensergo.io/v1alpha1
kind: TrafficRouter
metadata:
name: service-provider
namespace: default
labels:
app: service-provider
spec:
hosts:
- service-provider
http:
- match:
- headers:
tag:
exact: v2
route:
- destination:
host: service-provider
subset: v2
fallback:
host: service-provider
subset: v1
- route:
- destination:
host: service-provider
subset: v1
EOF
```
This TrafficRouter specifies the simplest label routing rule. HTTP requests with a gray header are routed to v2, and the rest of the traffic is routed to v1.
If the version v2 does not have a corresponding instance, the HTTP request will fall back to the version v1.
### Demonstrate effect
We send an HTTP request without a request header to IstioConsumerApplication
```
curl --location --request GET '127.0.0.1:18083/router-test'
```
Since the request header is not gray, the request will be routed to version v1 with the following result
```
Route in 30.221.132.228: 18081,version is v1.
```
We then send an HTTP request with a gray tag in its header and the request path is /istio-label-routing
```
curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: gray'
```
The request is routed to version v2 because the routing rule is matched by the request.
```
Route in 30.221.132.228: 18082,version is v2.
```
After we stop the ProviderApplication of the version v2, we send an HTTP request with the request header tag gray.
```
curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: v2'
```
because the version v2 does not have a corresponding instance, so the Http requesr is fallback to the version v1.
```
Route in 30.221.132.228: 18081,version is v1.
```

@ -58,7 +58,13 @@
<module>integrated-example/integrated-praise-consumer</module>
<module>integrated-example/integrated-common</module>
<module>integrated-example/integrated-frontend</module>
</modules>
<module>governance-example/label-routing-example/istio-consumer-example</module>
<module>governance-example/label-routing-example/opensergo-consumer-example</module>
<module>governance-example/label-routing-example/consumer-example</module>
<module>governance-example/label-routing-example/default-provider-version-example</module>
<module>governance-example/label-routing-example/provider-version-example</module>
</modules>
<build>
<plugins>

@ -26,6 +26,10 @@
<module>spring-cloud-alibaba-sentinel-datasource</module>
<module>spring-cloud-alibaba-sentinel-gateway</module>
<module>spring-cloud-starter-alibaba-appactive</module>
<module>spring-cloud-starter-alibaba-controlplane-istio</module>
<module>spring-cloud-starter-alibaba-controlplane-opensergo</module>
<module>spring-cloud-starter-alibaba-governance-auth</module>
<module>spring-cloud-starter-alibaba-governance-routing</module>
<module>spring-cloud-alibaba-commons</module>
</modules>

@ -13,6 +13,18 @@
<artifactId>spring-cloud-alibaba-commons</artifactId>
<name>Spring Cloud Alibaba Commons</name>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,80 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.auth.condition;
import com.alibaba.cloud.commons.matcher.Matcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class AuthCondition {
public enum ValidationType {
/**
* All types of auth validation.
*/
HEADER, SOURCE_IP, REMOTE_IP, DEST_IP, REQUEST_PRINCIPALS, AUTH_AUDIENCES, AUTH_CLAIMS, AUTH_PRESENTERS, HOSTS, PATHS, PORTS, METHODS, IDENTITY
}
private static final Logger log = LoggerFactory.getLogger(AuthCondition.class);
private ValidationType type;
private String key;
private Matcher matcher;
public AuthCondition(ValidationType type, Matcher matcher) {
this.type = type;
this.matcher = matcher;
}
public AuthCondition(ValidationType type, String key, Matcher matcher) {
this(type, matcher);
this.key = key;
}
public ValidationType getType() {
return type;
}
public void setType(ValidationType type) {
this.type = type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Matcher getMatcher() {
return matcher;
}
public void setMatcher(Matcher matcher) {
this.matcher = matcher;
}
}

@ -0,0 +1,112 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.auth.rule;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.cloud.commons.governance.auth.condition.AuthCondition;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class AuthRule {
public enum RuleOperation {
/**
* In what way are subrules connected.
*/
UNKNOWN, AND, OR
}
private RuleOperation op = RuleOperation.UNKNOWN;
private List<AuthRule> children = new ArrayList<>();
private AuthCondition condition;
private boolean isNot;
public AuthRule(RuleOperation op) {
this.op = op;
}
public AuthRule(RuleOperation op, boolean isNot) {
this(op);
this.isNot = isNot;
}
public AuthRule(AuthCondition condition) {
this.condition = condition;
}
public AuthRule(AuthCondition condition, boolean isNot) {
this(condition);
this.isNot = isNot;
}
public void addChildren(AuthRule rule) {
children.add(rule);
}
public boolean isEmpty() {
if (children.isEmpty()) {
return condition == null;
}
return false;
}
public boolean isLeaf() {
return condition != null;
}
public RuleOperation getOp() {
return op;
}
public void setOp(RuleOperation op) {
this.op = op;
}
public List<AuthRule> getChildren() {
return children;
}
public void setChildren(List<AuthRule> children) {
this.children = children;
}
public AuthCondition getCondition() {
return condition;
}
public void setCondition(AuthCondition condition) {
this.condition = condition;
}
public boolean isNot() {
return isNot;
}
public void setNot(boolean not) {
isNot = not;
}
}

@ -0,0 +1,52 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.auth.rule;
import java.util.Map;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class AuthRules {
private final Map<String, AuthRule> allowAuthRules;
private final Map<String, AuthRule> denyAuthRules;
private final Map<String, JwtRule> jwtRules;
public AuthRules(Map<String, AuthRule> allowAuthRules,
Map<String, AuthRule> denyAuthRules, Map<String, JwtRule> jwtRules) {
this.allowAuthRules = allowAuthRules;
this.denyAuthRules = denyAuthRules;
this.jwtRules = jwtRules;
}
public Map<String, AuthRule> getAllowAuthRules() {
return allowAuthRules;
}
public Map<String, AuthRule> getDenyAuthRules() {
return denyAuthRules;
}
public Map<String, JwtRule> getJwtRules() {
return jwtRules;
}
}

@ -0,0 +1,89 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.auth.rule;
import java.util.List;
import java.util.Map;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class JwtRule {
private String name;
private Map<String, String> fromHeaders;
private String issuer;
private List<String> audiences;
private String jwks;
private List<String> fromParams;
private String outputPayloadToHeader;
private boolean forwardOriginalToken;
public JwtRule(String name, Map<String, String> fromHeaders, String issuer,
List<String> audiences, String jwks, List<String> fromParams,
String outputPayloadToHeader, boolean forwardOriginalToken) {
this.name = name;
this.fromHeaders = fromHeaders;
this.issuer = issuer;
this.audiences = audiences;
this.jwks = jwks;
this.fromParams = fromParams;
this.outputPayloadToHeader = outputPayloadToHeader;
this.forwardOriginalToken = forwardOriginalToken;
}
public String getName() {
return name;
}
public Map<String, String> getFromHeaders() {
return fromHeaders;
}
public String getIssuer() {
return issuer;
}
public List<String> getAudiences() {
return audiences;
}
public String getJwks() {
return jwks;
}
public List<String> getFromParams() {
return fromParams;
}
public String getOutputPayloadToHeader() {
return outputPayloadToHeader;
}
public boolean isForwardOriginalToken() {
return forwardOriginalToken;
}
}

@ -0,0 +1,41 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.event;
import com.alibaba.cloud.commons.governance.auth.rule.AuthRules;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class AuthDataChangedEvent extends GovernanceEvent {
/**
* Configuration for authentication.
*/
private final AuthRules authRules;
public AuthDataChangedEvent(Object source, AuthRules authRules) {
super(source);
this.authRules = authRules;
}
public AuthRules getAuthRules() {
return authRules;
}
}

@ -0,0 +1,36 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.event;
import org.springframework.context.ApplicationEvent;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class GovernanceEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
* @param source the object on which the event initially occurred or with which the
* event is associated (never {@code null})
*/
public GovernanceEvent(Object source) {
super(source);
}
}

@ -0,0 +1,44 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.event;
import java.util.Collection;
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class LabelRoutingDataChangedEvent extends GovernanceEvent {
/**
* Configuration for Label Routing.
*/
private final Collection<UnifiedRouteDataStructure> untiedRouterDataStructureList;
public LabelRoutingDataChangedEvent(Object source,
Collection<UnifiedRouteDataStructure> untiedRouterDataStructureList) {
super(source);
this.untiedRouterDataStructureList = untiedRouterDataStructureList;
}
public Collection<UnifiedRouteDataStructure> getUntiedRouterDataStructureList() {
return untiedRouterDataStructureList;
}
}

@ -0,0 +1,27 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.event;
import org.springframework.context.ApplicationEvent;
public class TargetServiceChangedEvent extends ApplicationEvent {
public TargetServiceChangedEvent(Object source) {
super(source);
}
}

@ -0,0 +1,71 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.labelrouting;
import java.util.List;
import java.util.Objects;
/**
* @author HH
*/
public class LabelRouteRule {
private String defaultRouteVersion;
private List<MatchService> matchRouteList;
public String getDefaultRouteVersion() {
return defaultRouteVersion;
}
public void setDefaultRouteVersion(String defaultRouteVersion) {
this.defaultRouteVersion = defaultRouteVersion;
}
public List<MatchService> getMatchRouteList() {
return matchRouteList;
}
public void setMatchRouteList(List<MatchService> matchRouteList) {
this.matchRouteList = matchRouteList;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LabelRouteRule that = (LabelRouteRule) o;
return Objects.equals(defaultRouteVersion, that.defaultRouteVersion)
&& Objects.equals(getMatchRouteList(), that.getMatchRouteList());
}
@Override
public int hashCode() {
return Objects.hash(defaultRouteVersion, getMatchRouteList());
}
@Override
public String toString() {
return "LabelRouteData{" + "defaultRouteVersion='" + defaultRouteVersion + '\''
+ ", matchRouteList=" + matchRouteList + '}';
}
}

@ -0,0 +1,95 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.labelrouting;
import java.util.List;
import java.util.Objects;
import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule;
/**
* @author HH
*/
public class MatchService {
private List<RouteRule> ruleList;
private String version;
private Integer weight;
private String fallbackVersion;
public String getFallback() {
return fallbackVersion;
}
public void setFallback(String fallbackVersion) {
this.fallbackVersion = fallbackVersion;
}
public List<RouteRule> getRuleList() {
return ruleList;
}
public void setRuleList(List<RouteRule> ruleList) {
this.ruleList = ruleList;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MatchService that = (MatchService) o;
return getWeight().equals(that.getWeight())
&& Objects.equals(getRuleList(), that.getRuleList())
&& Objects.equals(getFallback(), that.getFallback())
&& Objects.equals(getVersion(), that.getVersion());
}
@Override
public int hashCode() {
return Objects.hash(getRuleList(), getVersion(), getWeight(), getFallback());
}
@Override
public String toString() {
return "MatchService{" + "ruleList=" + ruleList + ", version='" + version + '\''
+ ", weight=" + weight + ", getFallback=" + fallbackVersion + '}';
}
}

@ -0,0 +1,50 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.labelrouting;
/**
* @author HH
*/
public class UnifiedRouteDataStructure {
private LabelRouteRule labelRouteRule;
private String targetService;
public LabelRouteRule getLabelRouteRule() {
return labelRouteRule;
}
public void setLabelRouteRule(LabelRouteRule labelRouteRule) {
this.labelRouteRule = labelRouteRule;
}
public String getTargetService() {
return targetService;
}
public void setTargetService(String targetService) {
this.targetService = targetService;
}
@Override
public String toString() {
return "UntiedRouteDataStructure{" + "labelRouteData=" + labelRouteRule
+ ", targetService='" + targetService + '\'' + '}';
}
}

@ -0,0 +1,100 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.labelrouting.rule;
import java.util.Objects;
/**
* @author HH
*/
public class HeaderRule implements RouteRule {
private String type;
private String condition;
private String key;
private String value;
@Override
public String getCondition() {
return condition;
}
@Override
public void setCondition(String condition) {
this.condition = condition;
}
@Override
public String getKey() {
return key;
}
@Override
public void setKey(String key) {
this.key = key;
}
@Override
public String getValue() {
return value;
}
@Override
public void setValue(String value) {
this.value = value;
}
@Override
public String getType() {
return this.type;
}
@Override
public void setType(String type) {
this.type = type;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
HeaderRule that = (HeaderRule) o;
return Objects.equals(getType(), that.getType())
&& Objects.equals(getCondition(), that.getCondition())
&& Objects.equals(getKey(), that.getKey())
&& Objects.equals(getValue(), that.getValue());
}
@Override
public int hashCode() {
return Objects.hash(getType(), getCondition(), getKey(), getValue());
}
@Override
public String toString() {
return "HeaderRule{" + "type='" + type + '\'' + ", condition='" + condition + '\''
+ ", key='" + key + '\'' + ", value='" + value + '\'' + '}';
}
}

@ -0,0 +1,44 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.labelrouting.rule;
/**
* @author HH
*/
public interface RouteRule {
/**
* get type of rule.
* @return String
*/
String getType();
void setType(String type);
String getCondition();
void setCondition(String condition);
String getKey();
void setKey(String key);
String getValue();
void setValue(String value);
}

@ -0,0 +1,179 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.governance.labelrouting.rule;
import java.util.Objects;
/**
* @author HH
*/
public class UrlRule {
public static class Path implements RouteRule {
private String type;
private String condition;
private String value;
@Override
public String getCondition() {
return condition;
}
@Override
public void setCondition(String condition) {
this.condition = condition;
}
@Override
public String getKey() {
return null;
}
@Override
public void setKey(String key) {
//
}
@Override
public String getValue() {
return value;
}
@Override
public void setValue(String value) {
this.value = value;
}
@Override
public String getType() {
return this.type;
}
@Override
public void setType(String type) {
this.type = type;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Path path = (Path) o;
return Objects.equals(getType(), path.getType())
&& Objects.equals(getCondition(), path.getCondition())
&& Objects.equals(getValue(), path.getValue());
}
@Override
public int hashCode() {
return Objects.hash(getType(), getCondition(), getValue());
}
@Override
public String toString() {
return "Path{" + "type='" + type + '\'' + ", condition='" + condition + '\''
+ ", value='" + value + '\'' + '}';
}
}
public static class Parameter implements RouteRule {
private String type;
private String condition;
private String key;
private String value;
@Override
public String getCondition() {
return condition;
}
@Override
public void setCondition(String condition) {
this.condition = condition;
}
@Override
public String getKey() {
return key;
}
@Override
public void setKey(String key) {
this.key = key;
}
@Override
public String getValue() {
return value;
}
@Override
public void setValue(String value) {
this.value = value;
}
@Override
public String getType() {
return this.type;
}
@Override
public void setType(String type) {
this.type = type;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Parameter parameter = (Parameter) o;
return Objects.equals(getType(), parameter.getType())
&& Objects.equals(getCondition(), parameter.getCondition())
&& Objects.equals(getKey(), parameter.getKey())
&& Objects.equals(getValue(), parameter.getValue());
}
@Override
public int hashCode() {
return Objects.hash(getType(), getCondition(), getKey(), getValue());
}
@Override
public String toString() {
return "Parameter{" + "type='" + type + '\'' + ", condition='" + condition
+ '\'' + ", key='" + key + '\'' + ", value='" + value + '\'' + '}';
}
}
}

@ -0,0 +1,108 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.matcher;
import com.alibaba.cloud.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class IpMatcher implements Matcher {
private static final Logger log = LoggerFactory.getLogger(IpMatcher.class);
private int prefixLen;
private String ip;
public IpMatcher() {
}
public IpMatcher(int prefixLen, String ip) {
this.prefixLen = prefixLen;
this.ip = ip;
}
public boolean match(Object object) {
if (!(object instanceof String)) {
return false;
}
String ip = (String) object;
String ruleIp = ip2BinaryString(this.ip);
if (StringUtils.isEmpty(ruleIp)) {
return false;
}
String ipBinary = ip2BinaryString(ip);
if (StringUtils.isEmpty(ipBinary)) {
return false;
}
if (prefixLen <= 0) {
return ruleIp.equals(ipBinary);
}
if (ruleIp.length() >= prefixLen && ipBinary.length() >= prefixLen) {
return ruleIp.substring(0, prefixLen)
.equals(ipBinary.substring(0, prefixLen));
}
return false;
}
/**
* @param ip dotted ip string, for example: 127.0.0.1
* @return
*/
private String ip2BinaryString(String ip) {
try {
String[] ips = ip.split("\\.");
long[] ipLong = new long[4];
for (int i = 0; i < 4; ++i) {
ipLong[i] = Long.parseLong(ips[i]);
if (ipLong[i] < 0 || ipLong[i] > 255) {
return "";
}
}
return String
.format("%32s", Long.toBinaryString((ipLong[0] << 24)
+ (ipLong[1] << 16) + (ipLong[2] << 8) + ipLong[3]))
.replace(" ", "0");
}
catch (Exception e) {
log.error("failed to parse ip {} to binary string", ip);
}
return "";
}
public int getPrefixLen() {
return prefixLen;
}
public String getIp() {
return ip;
}
public void setPrefixLen(int prefixLen) {
this.prefixLen = prefixLen;
}
public void setIp(String ip) {
this.ip = ip;
}
}

@ -0,0 +1,27 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.matcher;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public interface Matcher {
boolean match(Object obj);
}

@ -0,0 +1,47 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.matcher;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class PortMatcher implements Matcher {
private Integer matcher;
public PortMatcher() {
}
public PortMatcher(Integer matcher) {
this.matcher = matcher;
}
@Override
public boolean match(Object object) {
if (!(object instanceof Integer)) {
return false;
}
return matcher != null && matcher.equals(object);
}
public void setMatcher(Integer matcher) {
this.matcher = matcher;
}
}

@ -0,0 +1,119 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.matcher;
import java.util.Locale;
import java.util.regex.Pattern;
import com.alibaba.cloud.commons.lang.StringUtils;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class StringMatcher implements Matcher {
private String matcher;
private StringMatcherType type;
private boolean isIgnoreCase;
private String regex;
public StringMatcher() {
}
public StringMatcher(String regex) {
this.regex = regex;
this.type = StringMatcherType.REGEX;
}
public StringMatcher(String matcher, StringMatcherType type, boolean isIgnoreCase) {
this.matcher = matcher;
this.type = type;
this.isIgnoreCase = isIgnoreCase;
}
public boolean match(Object obj) {
if (!(obj instanceof String)) {
return false;
}
String str = (String) obj;
if (StringUtils.isEmpty(str)) {
return false;
}
if (isIgnoreCase) {
str = str.toLowerCase(Locale.ROOT);
matcher = matcher.toLowerCase(Locale.ROOT);
}
switch (type) {
case EXACT:
return str.equals(matcher);
case PREFIX:
return str.startsWith(matcher);
case SUFFIX:
return str.endsWith(matcher);
case CONTAIN:
return str.contains(matcher);
case REGEX:
try {
return Pattern.matches(regex, str);
}
catch (Exception e) {
return false;
}
default:
throw new UnsupportedOperationException(
"unsupported string compare operation");
}
}
public String getMatcher() {
return matcher;
}
public void setMatcher(String matcher) {
this.matcher = matcher;
}
public StringMatcherType getType() {
return type;
}
public void setType(StringMatcherType type) {
this.type = type;
}
public boolean isIgnoreCase() {
return isIgnoreCase;
}
public void setIgnoreCase(boolean ignoreCase) {
isIgnoreCase = ignoreCase;
}
public String getRegex() {
return regex;
}
public void setRegex(String regex) {
this.regex = regex;
}
}

@ -0,0 +1,64 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.matcher;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public enum StringMatcherType {
/**
* exact match.
*/
EXACT("exact"),
/**
* prefix match.
*/
PREFIX("prefix"),
/**
* suffix match.
*/
SUFFIX("suffix"),
/**
* present match.
*/
PRESENT("present"),
/**
* regex match.
*/
REGEX("regex"),
/**
* contain match.
*/
CONTAIN("contain");
/**
* type of matcher.
*/
public final String type;
StringMatcherType(String type) {
this.type = type;
}
@Override
public String toString() {
return this.type;
}
}

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-starters</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
<name>Spring Cloud Alibaba Istio Control Plane</name>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
</dependency>
<dependency>
<groupId>io.envoyproxy.controlplane</groupId>
<artifactId>api</artifactId>
<version>1.0.36</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.16</version>
<scope>test</scope>
</dependency>
<!-- Only for unit test-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
</project>

@ -0,0 +1,81 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio;
import java.net.InetAddress;
import java.net.UnknownHostException;
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
import com.google.protobuf.Struct;
import com.google.protobuf.Value;
import io.envoyproxy.envoy.config.core.v3.Node;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public final class NodeBuilder {
private static final Logger log = LoggerFactory.getLogger(NodeBuilder.class);
private static Node NODE;
private NodeBuilder() {
}
public static Node getNode() {
try {
if (NODE != null) {
return NODE;
}
String podName = System.getenv(IstioConstants.POD_NAME);
if (podName == null) {
podName = IstioConstants.DEFAULT_POD_NAME;
}
String podNamespace = System.getenv(IstioConstants.NAMESPACE_NAME);
if (podNamespace == null) {
podNamespace = IstioConstants.DEFAULT_NAMESPACE;
}
String ip = "127.0.0.1";
try {
InetAddress local = InetAddress.getLocalHost();
ip = local.getHostAddress();
}
catch (UnknownHostException e) {
log.error("can not get local ip", e);
}
Struct.Builder metaBuilder = Struct.newBuilder();
// metadata is necessary for RequestAuthentication CRD
metaBuilder.putFields("NAMESPACE",
Value.newBuilder().setStringValue(podNamespace).build());
NODE = Node.newBuilder()
.setId(String.format(
"sidecar~%s~%s.%s~%s" + IstioConstants.SVC_CLUSTER_LOCAL, ip,
podName, podNamespace, podNamespace))
.setMetadata(metaBuilder.build()).build();
return NODE;
}
catch (Exception e) {
log.error("unable to create node for xds request", e);
}
return null;
}
}

@ -0,0 +1,100 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio;
import java.util.List;
import java.util.Set;
import com.alibaba.cloud.governance.istio.protocol.impl.CdsProtocol;
import com.alibaba.cloud.governance.istio.protocol.impl.EdsProtocol;
import com.alibaba.cloud.governance.istio.protocol.impl.LdsProtocol;
import com.alibaba.cloud.governance.istio.protocol.impl.RdsProtocol;
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
import io.envoyproxy.envoy.config.listener.v3.Listener;
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a> PilotExchanger is the class which
* communicate with istio pilot.
*/
public class PilotExchanger {
private static final Logger log = LoggerFactory.getLogger(PilotExchanger.class);
private final LdsProtocol ldsProtocol;
private final CdsProtocol cdsProtocol;
private final EdsProtocol edsProtocol;
private final RdsProtocol rdsProtocol;
private void observeListeners(List<Listener> listeners) {
if (listeners == null) {
return;
}
Set<String> resourceName = ldsProtocol.getResourceNames();
if (resourceName != null && !resourceName.isEmpty()) {
rdsProtocol.observeResource(resourceName, this::observeRoutes);
}
}
private void observeClusters(List<Cluster> clusters) {
Set<String> resourceName = cdsProtocol.getResourceNames();
if (resourceName != null && !resourceName.isEmpty()) {
// eds
edsProtocol.observeResource(resourceName, this::observeEndpoints);
}
else {
// lds
ldsProtocol.observeResource(null, this::observeListeners);
}
}
private void observeEndpoints(List<ClusterLoadAssignment> endpoints) {
ldsProtocol.observeResource(null, this::observeListeners);
}
private void observeRoutes(List<RouteConfiguration> routes) {
if (log.isDebugEnabled()) {
log.debug("A Xds configuration update is finished");
}
}
public PilotExchanger(LdsProtocol ldsProtocol, CdsProtocol cdsProtocol,
EdsProtocol edsProtocol, RdsProtocol rdsProtocol) {
this.ldsProtocol = ldsProtocol;
this.cdsProtocol = cdsProtocol;
this.edsProtocol = edsProtocol;
this.rdsProtocol = rdsProtocol;
// observe cluster first, and update the other xds sequentially
this.ldsProtocol.setNeedPolling(false);
this.edsProtocol.setNeedPolling(false);
this.rdsProtocol.setNeedPolling(false);
// only polling cds, other protocol will be obtained sequentially
this.cdsProtocol.setNeedPolling(true);
cdsProtocol.observeResource(null, this::observeClusters);
}
}

@ -0,0 +1,141 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio;
import java.util.List;
import com.alibaba.cloud.commons.governance.event.GovernanceEvent;
import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter;
import com.alibaba.cloud.governance.istio.filter.impl.AuthXdsResolveFilter;
import com.alibaba.cloud.governance.istio.filter.impl.LabelRoutingXdsResolveFilter;
import com.alibaba.cloud.governance.istio.protocol.impl.CdsProtocol;
import com.alibaba.cloud.governance.istio.protocol.impl.EdsProtocol;
import com.alibaba.cloud.governance.istio.protocol.impl.LdsProtocol;
import com.alibaba.cloud.governance.istio.protocol.impl.RdsProtocol;
import io.envoyproxy.envoy.config.listener.v3.Listener;
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.istio.config.enabled", matchIfMissing = true)
@EnableConfigurationProperties(XdsConfigProperties.class)
// We need to auto config the class after all the governance data listener, to prevent
// event publisher hang permanently.
@AutoConfigureOrder(XdsAutoConfiguration.RESOURCE_TRANSFORM_AUTO_CONFIG_ORDER)
public class XdsAutoConfiguration {
/**
* xds auto configuration log.
*/
private static final Logger log = LoggerFactory.getLogger(XdsAutoConfiguration.class);
/**
* Order of xds auto config.
*/
public static final int RESOURCE_TRANSFORM_AUTO_CONFIG_ORDER = 100;
@Autowired
private XdsConfigProperties xdsConfigProperties;
@Bean
public DummyGovernanceDataListener dummyGovernanceDataListener() {
return new DummyGovernanceDataListener();
}
@Bean
public XdsChannel xdsChannel() {
return new XdsChannel(xdsConfigProperties);
}
@Bean
public XdsScheduledThreadPool xdsScheduledThreadPool() {
return new XdsScheduledThreadPool(xdsConfigProperties);
}
@Bean
public XdsResolveFilter<List<Listener>> authXdsResolveFilter() {
return new AuthXdsResolveFilter();
}
@Bean
public XdsResolveFilter<List<RouteConfiguration>> labelRoutingXdsResolveFilter() {
return new LabelRoutingXdsResolveFilter();
}
@Bean
public PilotExchanger pilotExchanger(LdsProtocol ldsProtocol, CdsProtocol cdsProtocol,
EdsProtocol edsProtocol, RdsProtocol rdsProtocol) {
return new PilotExchanger(ldsProtocol, cdsProtocol, edsProtocol, rdsProtocol);
}
@Bean
public LdsProtocol ldsProtocol(XdsChannel xdsChannel,
XdsScheduledThreadPool xdsScheduledThreadPool,
List<XdsResolveFilter<List<Listener>>> filters) {
return new LdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties,
filters);
}
@Bean
public CdsProtocol cdsProtocol(XdsChannel xdsChannel,
XdsScheduledThreadPool xdsScheduledThreadPool) {
return new CdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
}
@Bean
public EdsProtocol edsProtocol(XdsChannel xdsChannel,
XdsScheduledThreadPool xdsScheduledThreadPool) {
return new EdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
}
@Bean
public RdsProtocol rdsProtocol(XdsChannel xdsChannel,
XdsScheduledThreadPool xdsScheduledThreadPool,
List<XdsResolveFilter<List<RouteConfiguration>>> filters) {
return new RdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties,
filters);
}
/**
* To prevent the event publish hang permanently.
*/
private final class DummyGovernanceDataListener
implements ApplicationListener<GovernanceEvent> {
@Override
public void onApplicationEvent(GovernanceEvent event) {
if (log.isDebugEnabled()) {
log.debug("Received governance event " + event.toString());
}
}
}
}

@ -0,0 +1,135 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.alibaba.cloud.commons.io.FileUtils;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
import io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NegotiationType;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.grpc.stub.MetadataUtils;
import io.grpc.stub.StreamObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.alibaba.cloud.governance.istio.constant.IstioConstants.ISTIOD_SECURE_PORT;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class XdsChannel implements AutoCloseable {
private static final Logger log = LoggerFactory.getLogger(XdsChannel.class);
private ManagedChannel channel;
private String istiodToken;
private final XdsConfigProperties xdsConfigProperties;
public XdsChannel(XdsConfigProperties xdsConfigProperties) {
this.xdsConfigProperties = xdsConfigProperties;
try {
if (xdsConfigProperties.getPort() == ISTIOD_SECURE_PORT) {
// fetch token first
if (StringUtils.isNotEmpty(xdsConfigProperties.getIstiodToken())) {
istiodToken = xdsConfigProperties.getIstiodToken();
}
else {
this.refreshIstiodToken();
}
SslContext sslcontext = GrpcSslContexts.forClient()
// if server's cert doesn't chain to a standard root
.trustManager(InsecureTrustManagerFactory.INSTANCE)
// TODO: fill the publicKey and privateKey
.build();
this.channel = NettyChannelBuilder
.forTarget(xdsConfigProperties.getHost() + ":"
+ xdsConfigProperties.getPort())
.negotiationType(NegotiationType.TLS).sslContext(sslcontext)
.build();
}
else {
this.channel = NettyChannelBuilder
.forTarget(xdsConfigProperties.getHost() + ":"
+ xdsConfigProperties.getPort())
.negotiationType(NegotiationType.PLAINTEXT).build();
}
}
catch (Exception e) {
log.error("create XdsChannel failed", e);
}
}
public void refreshIstiodToken() {
if (xdsConfigProperties.getPort() != ISTIOD_SECURE_PORT) {
return;
}
File saFile = new File(IstioConstants.THIRD_PART_JWT_PATH);
if (saFile.canRead()) {
try {
this.istiodToken = FileUtils.readFileToString(saFile,
StandardCharsets.UTF_8);
return;
}
catch (IOException e) {
log.error("Unable to read token file", e);
}
}
if (this.istiodToken == null) {
throw new UnsupportedOperationException(
"Unable to found kubernetes service account token file. "
+ "Please check if work in Kubernetes and mount service account token file correctly.");
}
}
@Override
public void close() {
if (channel != null) {
channel.shutdown();
}
}
public StreamObserver<DiscoveryRequest> createDiscoveryRequest(
StreamObserver<DiscoveryResponse> observer) {
if (channel == null) {
return null;
}
AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceStub stub = AggregatedDiscoveryServiceGrpc
.newStub(channel);
Metadata header = new Metadata();
Metadata.Key<String> key = Metadata.Key.of("authorization",
Metadata.ASCII_STRING_MARSHALLER);
header.put(key, "Bearer " + this.istiodToken);
stub = MetadataUtils.attachHeaders(stub, header);
return stub.streamAggregatedResources(observer);
}
}

@ -0,0 +1,120 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio;
import javax.annotation.PostConstruct;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
@ConfigurationProperties(XdsConfigProperties.PREFIX)
public class XdsConfigProperties {
/**
* Prefix in yaml.
*/
public static final String PREFIX = "spring.cloud.istio.config";
private String host;
private int port;
private int pollingPoolSize;
private int pollingTime;
/**
* jwt token for istiod 15012 port.
*/
private String istiodToken;
private Boolean logXds;
@PostConstruct
public void init() {
if (this.port <= 0 || this.port > 65535) {
this.port = IstioConstants.ISTIOD_SECURE_PORT;
}
if (StringUtils.isEmpty(host)) {
this.host = IstioConstants.DEFAULT_ISTIOD_ADDR;
}
if (pollingPoolSize <= 0) {
pollingPoolSize = IstioConstants.DEFAULT_POLLING_SIZE;
}
if (pollingTime <= 0) {
pollingTime = IstioConstants.DEFAULT_POLLING_TIME;
}
if (logXds == null) {
logXds = true;
}
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getPollingPoolSize() {
return pollingPoolSize;
}
public void setPollingPoolSize(int pollingPoolSize) {
this.pollingPoolSize = pollingPoolSize;
}
public int getPollingTime() {
return pollingTime;
}
public void setPollingTime(int pollingTime) {
this.pollingTime = pollingTime;
}
public String getIstiodToken() {
return istiodToken;
}
public void setIstiodToken(String istiodToken) {
this.istiodToken = istiodToken;
}
public boolean isLogXds() {
return Boolean.TRUE.equals(logXds);
}
public void setLogXds(boolean logXds) {
this.logXds = logXds;
}
}

@ -0,0 +1,35 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio;
import java.util.concurrent.ScheduledThreadPoolExecutor;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class XdsScheduledThreadPool extends ScheduledThreadPoolExecutor {
public XdsScheduledThreadPool(XdsConfigProperties xdsConfigProperties) {
this(xdsConfigProperties.getPollingPoolSize());
}
public XdsScheduledThreadPool(int corePoolSize) {
super(corePoolSize);
}
}

@ -0,0 +1,99 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.constant;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public final class IstioConstants {
/**
* Suffix of node.
*/
public static final String SVC_CLUSTER_LOCAL = ".svc.cluster.local";
/**
* Default pod name.
*/
public static final String DEFAULT_POD_NAME = "sidecar";
/**
* Default namespace name.
*/
public static final String DEFAULT_NAMESPACE = "default";
/**
* Key of pod name.
*/
public static final String POD_NAME = "POD_NAME";
/**
* Key of namespace name.
*/
public static final String NAMESPACE_NAME = "NAMESPACE_NAME";
/**
* third-part jwt token location.
*/
public static final String THIRD_PART_JWT_PATH = "/var/run/secrets/tokens/istio-token";
/**
* url of cds request.
*/
public static final String CDS_URL = "type.googleapis.com/envoy.config.cluster.v3.Cluster";
/**
* url of eds request.
*/
public static final String EDS_URL = "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment";
/**
* url of lds request.
*/
public static final String LDS_URL = "type.googleapis.com/envoy.config.listener.v3.Listener";
/**
* url of rds request.
*/
public static final String RDS_URL = "type.googleapis.com/envoy.config.route.v3.RouteConfiguration";
/**
* secure port of istiod.
*/
public static final int ISTIOD_SECURE_PORT = 15012;
/**
* default polling size of xds request.
*/
public static final int DEFAULT_POLLING_SIZE = 10;
/**
* default polling time of xds request.
*/
public static final int DEFAULT_POLLING_TIME = 30;
/**
* default ip address of istiod.
*/
public static final String DEFAULT_ISTIOD_ADDR = "127.0.0.1";
private IstioConstants() {
}
}

@ -0,0 +1,72 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public abstract class AbstractXdsResolveFilter<T>
implements XdsResolveFilter<T>, ApplicationContextAware {
protected static final Logger log = LoggerFactory
.getLogger(AbstractXdsResolveFilter.class);
protected ApplicationContext applicationContext;
protected static final String ALLOW_ANY = "allow_any";
protected static final String PATH = "path";
protected static final String VIRTUAL_INBOUND = "virtualInbound";
protected static final String CONNECTION_MANAGER = "envoy.filters.network.http_connection_manager";
protected static final String RBAC_FILTER = "envoy.filters.http.rbac";
protected static final String JWT_FILTER = "envoy.filters.http.jwt_authn";
protected static final String ISTIO_AUTHN = "istio_authn";
protected static final String REQUEST_AUTH_PRINCIPAL = "request.auth.principal";
protected static final String REQUEST_AUTH_AUDIENCE = "request.auth.audiences";
protected static final String REQUEST_AUTH_PRESENTER = "request.auth.presenter";
protected static final String REQUEST_AUTH_CLAIMS = "request.auth.claims";
protected static final String HEADER_NAME_AUTHORITY = ":authority";
protected static final String HEADER_NAME_METHOD = ":method";
protected static final int MIN_PORT = 0;
protected static final int MAX_PORT = 65535;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}

@ -0,0 +1,29 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.filter;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public interface XdsResolveFilter<T> {
boolean resolve(T t);
String getTypeUrl();
}

@ -0,0 +1,377 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.filter.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import com.alibaba.cloud.commons.governance.auth.condition.AuthCondition;
import com.alibaba.cloud.commons.governance.auth.rule.AuthRule;
import com.alibaba.cloud.commons.governance.auth.rule.AuthRules;
import com.alibaba.cloud.commons.governance.auth.rule.JwtRule;
import com.alibaba.cloud.commons.governance.event.AuthDataChangedEvent;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.commons.matcher.PortMatcher;
import com.alibaba.cloud.commons.matcher.StringMatcher;
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
import com.alibaba.cloud.governance.istio.filter.AbstractXdsResolveFilter;
import com.alibaba.cloud.governance.istio.util.ConvUtil;
import com.google.protobuf.Any;
import com.google.protobuf.InvalidProtocolBufferException;
import io.envoyproxy.envoy.config.listener.v3.FilterChain;
import io.envoyproxy.envoy.config.listener.v3.Listener;
import io.envoyproxy.envoy.config.rbac.v3.Permission;
import io.envoyproxy.envoy.config.rbac.v3.Policy;
import io.envoyproxy.envoy.config.rbac.v3.Principal;
import io.envoyproxy.envoy.config.rbac.v3.RBAC;
import io.envoyproxy.envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication;
import io.envoyproxy.envoy.extensions.filters.http.jwt_authn.v3.JwtHeader;
import io.envoyproxy.envoy.extensions.filters.http.jwt_authn.v3.JwtProvider;
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager;
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter;
import io.envoyproxy.envoy.type.matcher.v3.MetadataMatcher;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class AuthXdsResolveFilter extends AbstractXdsResolveFilter<List<Listener>> {
@Override
public boolean resolve(List<Listener> listeners) {
if (listeners == null || listeners.isEmpty()) {
return false;
}
Map<String, AuthRule> allowAuthRules = new HashMap<>();
Map<String, AuthRule> denyAuthRules = new HashMap<>();
Map<String, JwtRule> jwtRules = new HashMap<>();
List<HttpFilter> httpFilters = resolveHttpFilter(listeners);
List<RBAC> rbacList = resolveRbac(httpFilters);
for (RBAC rbac : rbacList) {
for (Map.Entry<String, Policy> entry : rbac.getPoliciesMap().entrySet()) {
AuthRule authRule = new AuthRule(AuthRule.RuleOperation.AND);
AuthRule principalOr = new AuthRule(AuthRule.RuleOperation.OR);
// principals
List<Principal> principals = entry.getValue().getPrincipalsList();
for (Principal principal : principals) {
AuthRule principalAnd = resolvePrincipal(principal);
if (principalAnd != null && !principalAnd.isEmpty()) {
principalOr.addChildren(principalAnd);
}
}
// permission
AuthRule permissionOr = new AuthRule(AuthRule.RuleOperation.OR);
List<Permission> permissions = entry.getValue().getPermissionsList();
for (Permission permission : permissions) {
AuthRule permissionAnd = resolvePermission(permission);
if (permissionAnd != null && !permissionAnd.isEmpty()) {
permissionOr.addChildren(permissionAnd);
}
}
if (!principalOr.isEmpty()) {
authRule.addChildren(principalOr);
}
if (!permissionOr.isEmpty()) {
authRule.addChildren(permissionOr);
}
if (authRule.isEmpty()) {
continue;
}
switch (rbac.getAction()) {
case UNRECOGNIZED:
case ALLOW:
allowAuthRules.put(entry.getKey(), authRule);
break;
case DENY:
denyAuthRules.put(entry.getKey(), authRule);
break;
default:
log.warn("Unknown rbac action, {}", rbac.getAction());
}
}
}
List<JwtAuthentication> jwtAuthentications = resolveJWT(httpFilters);
for (JwtAuthentication jwtRule : jwtAuthentications) {
Map<String, JwtProvider> jwtProviders = jwtRule.getProvidersMap();
for (Map.Entry<String, JwtProvider> entry : jwtProviders.entrySet()) {
JwtProvider provider = entry.getValue();
Map<String, String> fromHeaders = new HashMap<>();
for (JwtHeader header : provider.getFromHeadersList()) {
fromHeaders.put(header.getName(), header.getValuePrefix());
}
jwtRules.put(entry.getKey(),
new JwtRule(entry.getKey(), fromHeaders, provider.getIssuer(),
new ArrayList<>(provider.getAudiencesList()),
provider.getLocalJwks().getInlineString(),
new ArrayList<>(provider.getFromParamsList()),
provider.getForwardPayloadHeader(),
provider.getForward()));
}
}
log.info("auth rules resolve finish, RBAC rules size: {}, Jwt rules size: {}",
allowAuthRules.size() + denyAuthRules.size(), jwtRules.size());
applicationContext.publishEvent(new AuthDataChangedEvent(this,
new AuthRules(allowAuthRules, denyAuthRules, jwtRules)));
return true;
}
private List<RBAC> resolveRbac(List<HttpFilter> httpFilters) {
return httpFilters.stream()
.filter(httpFilter -> httpFilter.getName().equals(RBAC_FILTER))
.map(httpFilter -> {
try {
return httpFilter.getTypedConfig().unpack(
io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBAC.class);
}
catch (InvalidProtocolBufferException e) {
return null;
}
}).filter(Objects::nonNull)
.map(io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBAC::getRules)
.collect(Collectors.toList());
}
private List<HttpFilter> resolveHttpFilter(List<Listener> listeners) {
return listeners.stream()
.filter(listener -> listener.getName().equals(VIRTUAL_INBOUND))
.map(Listener::getFilterChainsList)
.flatMap(filterChains -> filterChains.stream()
.map(FilterChain::getFiltersList))
.flatMap(filters -> filters.stream()
.filter(filter -> filter.getName().equals(CONNECTION_MANAGER)))
.map(filter -> unpackHttpConnectionManager(filter.getTypedConfig()))
.filter(Objects::nonNull)
.flatMap(httpConnectionManager -> httpConnectionManager
.getHttpFiltersList().stream())
.filter(Objects::nonNull).collect(Collectors.toList());
}
private List<JwtAuthentication> resolveJWT(List<HttpFilter> httpFilters) {
return httpFilters.stream()
.filter(httpFilter -> httpFilter.getName().equals(JWT_FILTER))
.map(httpFilter -> {
try {
return httpFilter.getTypedConfig()
.unpack(JwtAuthentication.class);
}
catch (InvalidProtocolBufferException e) {
return null;
}
}).filter(Objects::nonNull).collect(Collectors.toList());
}
private HttpConnectionManager unpackHttpConnectionManager(Any any) {
try {
if (!any.is(HttpConnectionManager.class)) {
return null;
}
return any.unpack(HttpConnectionManager.class);
}
catch (InvalidProtocolBufferException e) {
return null;
}
}
private AuthRule resolvePrincipal(Principal principal) {
Principal.Set andIds = principal.getAndIds();
AuthRule andChildren = new AuthRule(AuthRule.RuleOperation.AND);
for (Principal andId : andIds.getIdsList()) {
if (andId.getAny()) {
return null;
}
boolean isNot = false;
if (andId.hasNotId()) {
isNot = true;
andId = andId.getNotId();
}
AuthRule orChildren = new AuthRule(AuthRule.RuleOperation.OR, isNot);
Principal.Set orIds = andId.getOrIds();
for (Principal orId : orIds.getIdsList()) {
if (orId.hasAuthenticated()
&& orId.getAuthenticated().hasPrincipalName()) {
StringMatcher identity = ConvUtil.convStringMatcher(
orId.getAuthenticated().getPrincipalName());
if (identity != null) {
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.IDENTITY, identity)));
}
}
if (orId.hasDirectRemoteIp()) {
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.SOURCE_IP,
ConvUtil.convertIpMatcher(orId.getDirectRemoteIp()))));
}
if (orId.hasRemoteIp()) {
orChildren.addChildren(new AuthRule(
new AuthCondition(AuthCondition.ValidationType.REMOTE_IP,
ConvUtil.convertIpMatcher(orId.getRemoteIp()))));
}
if (orId.hasMetadata()
&& ISTIO_AUTHN.equals(orId.getMetadata().getFilter())) {
List<MetadataMatcher.PathSegment> segments = orId.getMetadata()
.getPathList();
switch (segments.get(0).getKey()) {
case REQUEST_AUTH_PRINCIPAL:
if (orId.hasMetadata() && orId.getMetadata().hasValue()
&& orId.getMetadata().getValue().hasStringMatch()) {
StringMatcher requestPrinciple = ConvUtil.convStringMatcher(
orId.getMetadata().getValue().getStringMatch());
if (requestPrinciple != null) {
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.REQUEST_PRINCIPALS,
requestPrinciple)));
}
}
break;
case REQUEST_AUTH_AUDIENCE:
if (orId.hasMetadata() && orId.getMetadata().hasValue()
&& orId.getMetadata().getValue().hasStringMatch()) {
StringMatcher authAudience = ConvUtil.convStringMatcher(
orId.getMetadata().getValue().getStringMatch());
if (authAudience != null) {
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.AUTH_AUDIENCES,
authAudience)));
}
}
break;
case REQUEST_AUTH_PRESENTER:
if (orId.hasMetadata() && orId.getMetadata().hasValue()
&& orId.getMetadata().getValue().hasStringMatch()) {
StringMatcher authPresenter = ConvUtil.convStringMatcher(
orId.getMetadata().getValue().getStringMatch());
if (authPresenter != null) {
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.AUTH_PRESENTERS,
authPresenter)));
}
}
break;
case REQUEST_AUTH_CLAIMS:
if (orId.hasMetadata() && orId.getMetadata().hasValue()
&& orId.getMetadata().getValue().hasListMatch()) {
if (segments.size() >= 2) {
String key = segments.get(1).getKey();
StringMatcher stringMatcher = null;
try {
stringMatcher = ConvUtil.convStringMatcher(
orId.getMetadata().getValue().getListMatch()
.getOneOf().getStringMatch());
}
catch (Exception e) {
log.error(
"unable to get/convert request auth claims");
}
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.AUTH_CLAIMS, key,
stringMatcher)));
}
}
break;
default:
}
}
if (orId.hasHeader()) {
String headerName = orId.getHeader().getName();
if (StringUtils.isEmpty(headerName)) {
continue;
}
StringMatcher stringMatcher = ConvUtil
.convertHeaderMatcher(orId.getHeader());
orChildren.addChildren(new AuthRule(
new AuthCondition(AuthCondition.ValidationType.HEADER,
headerName, stringMatcher)));
}
}
if (!orChildren.isEmpty()) {
andChildren.addChildren(orChildren);
}
}
return andChildren;
}
private AuthRule resolvePermission(Permission permission) {
Permission.Set andRules = permission.getAndRules();
AuthRule andChildren = new AuthRule(AuthRule.RuleOperation.AND);
for (Permission andRule : andRules.getRulesList()) {
if (andRule.getAny()) {
return null;
}
boolean isNot = false;
if (andRule.hasNotRule()) {
isNot = true;
andRule = andRule.getNotRule();
}
Permission.Set orRules = andRule.getOrRules();
AuthRule orChildren = new AuthRule(AuthRule.RuleOperation.OR, isNot);
for (Permission orRule : orRules.getRulesList()) {
int port = orRule.getDestinationPort();
if (port > MIN_PORT && port <= MAX_PORT) {
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.PORTS, new PortMatcher(port))));
}
if (orRule.hasHeader()) {
switch (orRule.getHeader().getName()) {
case HEADER_NAME_AUTHORITY:
StringMatcher host = ConvUtil
.convStringMatcher(orRule.getHeader());
if (host != null) {
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.HOSTS, host)));
}
break;
case HEADER_NAME_METHOD:
StringMatcher method = ConvUtil
.convStringMatcher(orRule.getHeader());
if (method != null) {
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.METHODS, method)));
}
break;
}
}
if (orRule.hasUrlPath() && orRule.getUrlPath().hasPath()) {
StringMatcher path = ConvUtil
.convStringMatcher(orRule.getUrlPath().getPath());
if (path != null) {
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.PATHS, path)));
}
}
if (orRule.hasDestinationIp()) {
orChildren.addChildren(new AuthRule(new AuthCondition(
AuthCondition.ValidationType.DEST_IP,
ConvUtil.convertIpMatcher(orRule.getDestinationIp()))));
}
}
if (!orChildren.isEmpty()) {
andChildren.addChildren(orChildren);
}
}
return andChildren;
}
@Override
public String getTypeUrl() {
return IstioConstants.LDS_URL;
}
}

@ -0,0 +1,176 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.filter.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.cloud.commons.governance.event.LabelRoutingDataChangedEvent;
import com.alibaba.cloud.commons.governance.labelrouting.LabelRouteRule;
import com.alibaba.cloud.commons.governance.labelrouting.MatchService;
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule;
import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule;
import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.commons.matcher.StringMatcherType;
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
import com.alibaba.cloud.governance.istio.filter.AbstractXdsResolveFilter;
import com.alibaba.cloud.governance.istio.util.ConvUtil;
import io.envoyproxy.envoy.config.route.v3.HeaderMatcher;
import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher;
import io.envoyproxy.envoy.config.route.v3.Route;
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
import io.envoyproxy.envoy.config.route.v3.RouteMatch;
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
import io.envoyproxy.envoy.config.route.v3.WeightedCluster;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class LabelRoutingXdsResolveFilter
extends AbstractXdsResolveFilter<List<RouteConfiguration>> {
@Override
public boolean resolve(List<RouteConfiguration> routeConfigurations) {
if (routeConfigurations == null) {
return false;
}
Map<String, UnifiedRouteDataStructure> untiedRouteDataStructures = new HashMap<>();
for (RouteConfiguration routeConfiguration : routeConfigurations) {
List<VirtualHost> virtualHosts = routeConfiguration.getVirtualHostsList();
for (VirtualHost virtualHost : virtualHosts) {
UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure();
String targetService = "";
String[] serviceAndPort = virtualHost.getName().split(":");
if (serviceAndPort.length > 0) {
targetService = serviceAndPort[0].split("\\.")[0];
}
if (ALLOW_ANY.equals(targetService)) {
continue;
}
unifiedRouteDataStructure.setTargetService(targetService);
List<Route> routes = virtualHost.getRoutesList();
LabelRouteRule labelRouteRule = getLabelRouteData(routes);
unifiedRouteDataStructure.setLabelRouteRule(labelRouteRule);
untiedRouteDataStructures.put(
unifiedRouteDataStructure.getTargetService(),
unifiedRouteDataStructure);
}
}
applicationContext.publishEvent(new LabelRoutingDataChangedEvent(this,
untiedRouteDataStructures.values()));
return true;
}
private LabelRouteRule getLabelRouteData(List<Route> routes) {
List<MatchService> matchServices = new ArrayList<>();
LabelRouteRule labelRouteRule = new LabelRouteRule();
for (Route route : routes) {
String cluster = route.getRoute().getCluster();
if (StringUtils.isNotEmpty(cluster)) {
MatchService matchService = getMatchService(route, cluster, 100);
matchServices.add(matchService);
}
WeightedCluster weightedCluster = route.getRoute().getWeightedClusters();
for (WeightedCluster.ClusterWeight clusterWeight : weightedCluster
.getClustersList()) {
MatchService matchService = getMatchService(route,
clusterWeight.getName(), clusterWeight.getWeight().getValue());
matchServices.add(matchService);
}
}
labelRouteRule.setMatchRouteList(matchServices);
if (!matchServices.isEmpty()) {
labelRouteRule.setDefaultRouteVersion(
matchServices.get(matchServices.size() - 1).getVersion());
}
return labelRouteRule;
}
private MatchService getMatchService(Route route, String cluster, int weight) {
String version = "";
try {
String[] info = cluster.split("\\|");
version = info[2];
}
catch (Exception e) {
log.error("invalid cluster info for route {}", route.getName());
}
MatchService matchService = new MatchService();
matchService.setVersion(version);
matchService.setRuleList(match2RouteRules(route.getMatch()));
matchService.setWeight(weight);
return matchService;
}
private List<RouteRule> match2RouteRules(RouteMatch routeMatch) {
List<RouteRule> routeRules = new ArrayList<>();
for (HeaderMatcher headerMatcher : routeMatch.getHeadersList()) {
HeaderRule headerRule = ConvUtil.headerMatcher2HeaderRule(headerMatcher);
if (headerRule != null) {
routeRules.add(headerRule);
}
}
for (QueryParameterMatcher parameterMatcher : routeMatch
.getQueryParametersList()) {
UrlRule.Parameter parameter = ConvUtil
.parameterMatcher2ParameterRule(parameterMatcher);
if (parameter != null) {
routeRules.add(parameter);
}
}
UrlRule.Path path = new UrlRule.Path();
path.setType(PATH);
switch (routeMatch.getPathSpecifierCase()) {
case PREFIX:
path.setCondition(StringMatcherType.PREFIX.toString());
path.setValue(routeMatch.getPrefix());
break;
case PATH:
path.setCondition(StringMatcherType.EXACT.toString());
path.setValue(routeMatch.getPath());
break;
case SAFE_REGEX:
path.setCondition(StringMatcherType.REGEX.toString());
path.setValue(routeMatch.getSafeRegex().getRegex());
break;
default:
// unknown type
path = null;
}
if (path != null) {
routeRules.add(path);
}
return routeRules;
}
@Override
public String getTypeUrl() {
return IstioConstants.RDS_URL;
}
}

@ -0,0 +1,301 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.protocol;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import com.alibaba.cloud.governance.istio.NodeBuilder;
import com.alibaba.cloud.governance.istio.XdsChannel;
import com.alibaba.cloud.governance.istio.XdsConfigProperties;
import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool;
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter;
import io.envoyproxy.envoy.config.core.v3.Node;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
import io.grpc.stub.StreamObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public abstract class AbstractXdsProtocol<T>
implements XdsProtocol<T>, ApplicationContextAware {
protected static final Logger log = LoggerFactory
.getLogger(AbstractXdsProtocol.class);
protected XdsChannel xdsChannel;
protected final Node node = NodeBuilder.getNode();
protected XdsConfigProperties xdsConfigProperties;
protected List<XdsResolveFilter<List<T>>> filters = new ArrayList<>();
private Set<String> resourceNames = new HashSet<>();
private final XdsScheduledThreadPool xdsScheduledThreadPool;
/**
* does the protocol need polling.
*/
private boolean needPolling;
/**
* send event to submodules.
*/
protected ApplicationContext applicationContext;
private final Map<Long, StreamObserver<DiscoveryRequest>> requestObserverMap = new ConcurrentHashMap<>();
private final Map<Long, CompletableFuture<List<T>>> futureMap = new ConcurrentHashMap<>();
private final Map<Long, Set<String>> requestResource = new ConcurrentHashMap<>();
protected final static AtomicLong requestId = new AtomicLong(0);
public AbstractXdsProtocol(XdsChannel xdsChannel,
XdsScheduledThreadPool xdsScheduledThreadPool,
XdsConfigProperties xdsConfigProperties) {
this.xdsChannel = xdsChannel;
this.xdsScheduledThreadPool = xdsScheduledThreadPool;
this.xdsConfigProperties = xdsConfigProperties;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
public void setNeedPolling(boolean needPolling) {
this.needPolling = needPolling;
}
@Override
public synchronized long observeResource(Set<String> resourceNames,
Consumer<List<T>> consumer) {
long id = getDefaultRequestId();
if (resourceNames == null) {
resourceNames = new HashSet<>();
}
requestResource.put(id, resourceNames);
try {
consumer.accept(doGetResource(id, resourceNames, consumer));
}
catch (Exception e) {
log.error("error on get observe resource from xds", e);
}
if (needPolling) {
xdsScheduledThreadPool.scheduleAtFixedRate(() -> {
try {
consumer.accept(doGetResource(id, requestResource.get(id), consumer));
}
catch (Exception e) {
log.error("error on get observe resource from xds", e);
}
}, xdsConfigProperties.getPollingTime(), xdsConfigProperties.getPollingTime(),
TimeUnit.SECONDS);
needPolling = false;
}
return id;
}
@Override
public List<T> getResource(Set<String> resourceNames) {
long id = requestId.getAndDecrement();
List<T> source = doGetResource(id, resourceNames, null);
requestObserverMap.remove(id);
return source;
}
public Set<String> getResourceNames() {
return resourceNames;
}
private List<T> doGetResource(long id, Set<String> resourceNames,
Consumer<List<T>> consumer) {
if (resourceNames == null) {
resourceNames = new HashSet<>();
}
CompletableFuture<List<T>> future = new CompletableFuture<>();
futureMap.put(id, future);
StreamObserver<DiscoveryRequest> requestObserver = requestObserverMap.get(id);
if (requestObserver == null) {
// reuse observer
requestObserver = xdsChannel
.createDiscoveryRequest(new XdsObserver(id, consumer));
// requestObserver may be null when testing
if (requestObserver != null) {
requestObserverMap.put(id, requestObserver);
}
}
sendXdsRequest(requestObserver, resourceNames);
try {
return future.get();
}
catch (ExecutionException | InterruptedException e) {
return null;
}
finally {
futureMap.remove(id);
}
}
protected abstract List<T> decodeXdsResponse(DiscoveryResponse response);
protected Set<String> resolveResourceNames(List<T> resources) {
return new HashSet<>();
}
protected void fireXdsFilters(List<T> resources) {
try {
this.resourceNames = resolveResourceNames(resources);
}
catch (Exception e) {
log.error("Error on resolving resource names from {}", resources);
}
for (XdsResolveFilter<List<T>> filter : filters) {
try {
if (!filter.resolve(resources)) {
return;
}
}
catch (Exception e) {
log.error("Error on executing Xds filter {}", filter.getClass().getName(),
e);
}
}
}
private void sendXdsRequest(StreamObserver<DiscoveryRequest> observer,
Set<String> resourceNames) {
DiscoveryRequest request = DiscoveryRequest.newBuilder().setNode(node)
.setTypeUrl(getTypeUrl()).addAllResourceNames(resourceNames).build();
observer.onNext(request);
}
private void sendAckRequest(long id, DiscoveryResponse response) {
StreamObserver<DiscoveryRequest> observer = requestObserverMap.get(id);
if (observer == null) {
return;
}
DiscoveryRequest request = DiscoveryRequest.newBuilder()
.setVersionInfo(response.getVersionInfo()).setNode(node)
.addAllResourceNames(requestResource.get(id) == null ? new ArrayList<>()
: requestResource.get(id))
.setTypeUrl(response.getTypeUrl()).setResponseNonce(response.getNonce())
.build();
observer.onNext(request);
}
private int getDefaultRequestId() {
switch (getTypeUrl()) {
case IstioConstants.CDS_URL:
return -1;
case IstioConstants.EDS_URL:
return -2;
case IstioConstants.LDS_URL:
return -3;
case IstioConstants.RDS_URL:
return -4;
}
throw new UnsupportedOperationException("Unknown type url");
}
private class XdsObserver implements StreamObserver<DiscoveryResponse> {
private Consumer<List<T>> consumer;
private long id;
XdsObserver(long id, Consumer<List<T>> consumer) {
this.id = id;
this.consumer = consumer;
}
@Override
public void onNext(DiscoveryResponse discoveryResponse) {
if (xdsChannel == null) {
return;
}
if (xdsConfigProperties.isLogXds()) {
log.info("receive notification from xds server, type: " + getTypeUrl()
+ " requestId: " + id);
}
List<T> responses = decodeXdsResponse(discoveryResponse);
CompletableFuture<List<T>> future = futureMap.get(id);
if (future == null) {
// means it is push operation from xds, consume it directly
consumer.accept(responses);
sendAckRequest(id, discoveryResponse);
return;
}
future.complete(responses);
sendAckRequest(id, discoveryResponse);
}
@Override
public void onError(Throwable throwable) {
if (xdsChannel == null) {
return;
}
if (xdsConfigProperties.isLogXds()) {
log.error("connect to xds server failed, reconnecting", throwable);
}
CompletableFuture<List<T>> future = futureMap.get(id);
if (future != null) {
future.complete(null);
futureMap.remove(id);
}
requestResource.remove(id);
// refresh token again
xdsChannel.refreshIstiodToken();
// reconnected immediately
StreamObserver<DiscoveryRequest> observer = xdsChannel
.createDiscoveryRequest(new XdsObserver(id, consumer));
if (observer != null) {
requestObserverMap.put(id, observer);
}
}
@Override
public void onCompleted() {
log.info("xds connect completed");
}
}
}

@ -0,0 +1,35 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.protocol;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public interface XdsProtocol<T> {
List<T> getResource(Set<String> resourceNames);
String getTypeUrl();
long observeResource(Set<String> resourceNames, Consumer<List<T>> consumer);
}

@ -0,0 +1,79 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.protocol.impl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.alibaba.cloud.governance.istio.XdsChannel;
import com.alibaba.cloud.governance.istio.XdsConfigProperties;
import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool;
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol;
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a> CdsProtocol contains infomation about
* service.
*/
public class CdsProtocol extends AbstractXdsProtocol<Cluster> {
public CdsProtocol(XdsChannel xdsChannel,
XdsScheduledThreadPool xdsScheduledThreadPool,
XdsConfigProperties xdsConfigProperties) {
super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
}
@Override
protected List<Cluster> decodeXdsResponse(DiscoveryResponse response) {
List<Cluster> clusters = new ArrayList<>();
for (com.google.protobuf.Any res : response.getResourcesList()) {
try {
Cluster cluster = res.unpack(Cluster.class);
clusters.add(cluster);
}
catch (Exception e) {
log.error("unpack cluster failed", e);
}
}
fireXdsFilters(clusters);
return clusters;
}
@Override
protected Set<String> resolveResourceNames(List<Cluster> resources) {
Set<String> endpoints = new HashSet<>();
if (resources == null) {
return endpoints;
}
for (Cluster cluster : resources) {
cluster.getEdsClusterConfig().getServiceName();
endpoints.add(cluster.getEdsClusterConfig().getServiceName());
}
return endpoints;
}
@Override
public String getTypeUrl() {
return IstioConstants.CDS_URL;
}
}

@ -0,0 +1,63 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.protocol.impl;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.cloud.governance.istio.XdsChannel;
import com.alibaba.cloud.governance.istio.XdsConfigProperties;
import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool;
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol;
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a> TODO: Fetch all endpoints in EdsProtocol.
*/
public class EdsProtocol extends AbstractXdsProtocol<ClusterLoadAssignment> {
public EdsProtocol(XdsChannel xdsChannel,
XdsScheduledThreadPool xdsScheduledThreadPool,
XdsConfigProperties xdsConfigProperties) {
super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
}
@Override
protected List<ClusterLoadAssignment> decodeXdsResponse(DiscoveryResponse response) {
List<ClusterLoadAssignment> endpoints = new ArrayList<>();
for (com.google.protobuf.Any res : response.getResourcesList()) {
try {
ClusterLoadAssignment endpoint = res.unpack(ClusterLoadAssignment.class);
endpoints.add(endpoint);
}
catch (Exception e) {
log.error("unpack cluster failed", e);
}
}
fireXdsFilters(endpoints);
return endpoints;
}
@Override
public String getTypeUrl() {
return IstioConstants.EDS_URL;
}
}

@ -0,0 +1,105 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.protocol.impl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.governance.istio.XdsChannel;
import com.alibaba.cloud.governance.istio.XdsConfigProperties;
import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool;
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter;
import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol;
import com.google.protobuf.InvalidProtocolBufferException;
import io.envoyproxy.envoy.config.listener.v3.Filter;
import io.envoyproxy.envoy.config.listener.v3.Listener;
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager;
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a> LdsProtocol contains the authentication
* configuration and other configuration about security.
*/
public class LdsProtocol extends AbstractXdsProtocol<Listener> {
public LdsProtocol(XdsChannel xdsChannel,
XdsScheduledThreadPool xdsScheduledThreadPool,
XdsConfigProperties xdsConfigProperties,
List<XdsResolveFilter<List<Listener>>> ldsFilters) {
super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
// init filters
for (XdsResolveFilter<List<Listener>> filter : ldsFilters) {
if (IstioConstants.LDS_URL.equals(filter.getTypeUrl())) {
filters.add(filter);
}
}
}
@Override
public String getTypeUrl() {
return IstioConstants.LDS_URL;
}
@Override
public List<Listener> decodeXdsResponse(DiscoveryResponse response) {
List<Listener> listeners = new ArrayList<>();
for (com.google.protobuf.Any res : response.getResourcesList()) {
try {
Listener listener = res.unpack(Listener.class);
if (listener != null) {
listeners.add(listener);
}
}
catch (Exception e) {
log.error("unpack listeners failed", e);
}
}
fireXdsFilters(listeners);
return listeners;
}
@Override
protected Set<String> resolveResourceNames(List<Listener> resources) {
Set<String> routeNames = new HashSet<>();
resources.forEach(listener -> routeNames.addAll(listener.getFilterChainsList()
.stream().flatMap((e) -> e.getFiltersList().stream())
.map(Filter::getTypedConfig).map(any -> {
try {
if (!any.is(HttpConnectionManager.class)) {
return null;
}
return any.unpack(HttpConnectionManager.class);
}
catch (InvalidProtocolBufferException e) {
return null;
}
}).filter(Objects::nonNull).map(HttpConnectionManager::getRds)
.map(Rds::getRouteConfigName).filter(StringUtils::isNotEmpty)
.collect(Collectors.toList())));
return routeNames;
}
}

@ -0,0 +1,70 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.protocol.impl;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.cloud.governance.istio.XdsChannel;
import com.alibaba.cloud.governance.istio.XdsConfigProperties;
import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool;
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter;
import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol;
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a> RdsProtocol contains route info.
*/
public class RdsProtocol extends AbstractXdsProtocol<RouteConfiguration> {
public RdsProtocol(XdsChannel xdsChannel,
XdsScheduledThreadPool xdsScheduledThreadPool,
XdsConfigProperties xdsConfigProperties,
List<XdsResolveFilter<List<RouteConfiguration>>> rdsFilters) {
super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
for (XdsResolveFilter<List<RouteConfiguration>> filter : rdsFilters) {
if (IstioConstants.RDS_URL.equals(filter.getTypeUrl())) {
filters.add(filter);
}
}
}
@Override
public List<RouteConfiguration> decodeXdsResponse(DiscoveryResponse response) {
List<RouteConfiguration> routes = new ArrayList<>();
for (com.google.protobuf.Any res : response.getResourcesList()) {
try {
RouteConfiguration route = res.unpack(RouteConfiguration.class);
routes.add(route);
}
catch (Exception e) {
log.error("unpack cluster failed", e);
}
}
fireXdsFilters(routes);
return routes;
}
@Override
public String getTypeUrl() {
return IstioConstants.RDS_URL;
}
}

@ -0,0 +1,156 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio.util;
import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule;
import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.commons.matcher.IpMatcher;
import com.alibaba.cloud.commons.matcher.StringMatcher;
import com.alibaba.cloud.commons.matcher.StringMatcherType;
import io.envoyproxy.envoy.config.core.v3.CidrRange;
import io.envoyproxy.envoy.config.route.v3.HeaderMatcher;
import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher;
import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public final class ConvUtil {
private static final String HEADER = "header";
private static final String PARAMETER = "parameter";
private ConvUtil() {
}
public static StringMatcher convStringMatcher(
io.envoyproxy.envoy.type.matcher.v3.StringMatcher stringMatcher) {
if (stringMatcher == null) {
return null;
}
boolean isIgnoreCase = stringMatcher.getIgnoreCase();
String exact = stringMatcher.getExact();
String prefix = stringMatcher.getPrefix();
String suffix = stringMatcher.getSuffix();
String contains = stringMatcher.getContains();
String regex = stringMatcher.getSafeRegex().getRegex();
if (StringUtils.isNotBlank(exact)) {
return new StringMatcher(exact, StringMatcherType.EXACT, isIgnoreCase);
}
if (StringUtils.isNotBlank(prefix)) {
return new StringMatcher(prefix, StringMatcherType.PREFIX, isIgnoreCase);
}
if (StringUtils.isNotBlank(suffix)) {
return new StringMatcher(suffix, StringMatcherType.SUFFIX, isIgnoreCase);
}
if (StringUtils.isNotBlank(contains)) {
return new StringMatcher(contains, StringMatcherType.CONTAIN, isIgnoreCase);
}
if (StringUtils.isNotBlank(regex)) {
return new StringMatcher(regex);
}
return null;
}
public static StringMatcher convStringMatcher(
io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) {
return convStringMatcher(headerMatch2StringMatch(headerMatcher));
}
public static IpMatcher convertIpMatcher(CidrRange cidrRange) {
return new IpMatcher(cidrRange.getPrefixLen().getValue(),
cidrRange.getAddressPrefix());
}
public static StringMatcher convertHeaderMatcher(
io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) {
return convStringMatcher(headerMatch2StringMatch(headerMatcher));
}
public static io.envoyproxy.envoy.type.matcher.v3.StringMatcher headerMatch2StringMatch(
io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) {
if (headerMatcher == null) {
return null;
}
if (headerMatcher.getPresentMatch()) {
io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher
.newBuilder();
return builder.setSafeRegex(RegexMatcher.newBuilder().build())
.setIgnoreCase(true).build();
}
if (!headerMatcher.hasStringMatch()) {
io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher
.newBuilder();
String exactMatch = headerMatcher.getExactMatch();
String containsMatch = headerMatcher.getContainsMatch();
String prefixMatch = headerMatcher.getPrefixMatch();
String suffixMatch = headerMatcher.getSuffixMatch();
RegexMatcher safeRegex = headerMatcher.getSafeRegexMatch();
if (!StringUtils.isEmpty(exactMatch)) {
builder.setExact(exactMatch);
}
else if (!StringUtils.isEmpty(containsMatch)) {
builder.setContains(containsMatch);
}
else if (!StringUtils.isEmpty(prefixMatch)) {
builder.setPrefix(prefixMatch);
}
else if (!StringUtils.isEmpty(suffixMatch)) {
builder.setSuffix(suffixMatch);
}
else if (safeRegex.isInitialized()) {
builder.setSafeRegex(safeRegex);
}
return builder.setIgnoreCase(true).build();
}
return headerMatcher.getStringMatch();
}
public static UrlRule.Parameter parameterMatcher2ParameterRule(
QueryParameterMatcher queryParameterMatcher) {
UrlRule.Parameter parameter = new UrlRule.Parameter();
StringMatcher stringMatcher = ConvUtil
.convStringMatcher(queryParameterMatcher.getStringMatch());
if (stringMatcher != null) {
parameter.setCondition(stringMatcher.getType().toString());
parameter.setKey(queryParameterMatcher.getName());
parameter.setValue(stringMatcher.getMatcher());
parameter.setType(PARAMETER);
return parameter;
}
return null;
}
public static HeaderRule headerMatcher2HeaderRule(HeaderMatcher headerMatcher) {
StringMatcher stringMatcher = ConvUtil
.convStringMatcher(ConvUtil.headerMatch2StringMatch(headerMatcher));
if (stringMatcher != null) {
HeaderRule headerRule = new HeaderRule();
headerRule.setCondition(stringMatcher.getType().toString());
headerRule.setKey(headerMatcher.getName());
headerRule.setValue(stringMatcher.getMatcher());
headerRule.setType(HEADER);
return headerRule;
}
return null;
}
}

@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.governance.istio.XdsAutoConfiguration

@ -0,0 +1,143 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.istio;
import java.io.File;
import java.io.FileInputStream;
import java.util.List;
import com.alibaba.cloud.commons.io.FileUtils;
import com.alibaba.cloud.governance.auth.AuthenticationAutoConfiguration;
import com.alibaba.cloud.governance.auth.repository.AuthRepository;
import com.alibaba.cloud.governance.istio.protocol.impl.LdsProtocol;
import com.alibaba.cloud.governance.istio.protocol.impl.RdsProtocol;
import com.alibaba.cloud.router.LabelRoutingAutoConfiguration;
import com.alibaba.cloud.router.repository.FilterService;
import com.alibaba.cloud.router.repository.RouteDataRepository;
import com.alibaba.fastjson.JSONObject;
import io.envoyproxy.envoy.config.listener.v3.Listener;
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = XdsRulesTests.TestConfig.class,
properties = { "spring.cloud.istio.config.port=15010",
"spring.cloud.istio.config.enabled=true",
"spring.cloud.istio.config.log-xds=false",
"spring.cloud.nacos.discovery.watch.enabled=false" },
webEnvironment = NONE)
@EnableFeignClients
public class XdsRulesTests {
private static final Logger log = LoggerFactory.getLogger(XdsRulesTests.class);
private static final String TARGET_SERVICE = "service-provider";
@Autowired
private AuthRepository authRepository;
@Autowired
private RouteDataRepository routeDataRepository;
@Autowired
private LdsProtocol ldsProtocol;
@Autowired
private RdsProtocol rdsProtocol;
private DiscoveryResponse decodeResponse(String path) throws Exception {
File file = new File(path);
FileInputStream stream = FileUtils.openInputStream(file);
byte[] bytes = new byte[(int) file.length()];
int readBytes = stream.read(bytes);
if (readBytes == -1) {
throw new Exception("Unreadable response file");
}
return DiscoveryResponse.parseFrom(bytes);
}
@Test
public void testAuthTransform() throws Exception {
DiscoveryResponse discoveryResponse = decodeResponse(
"src/test/resources/LdsResponse.in");
List<Listener> listeners = ldsProtocol.decodeXdsResponse(discoveryResponse);
if (listeners == null) {
throw new Exception("Can not parse listeners from xds response");
}
log.info("Auth rules are {}", JSONObject.toJSONString(authRepository));
Assert.assertEquals(authRepository.getAllowAuthRules().size(), 1);
Assert.assertEquals(authRepository.getDenyAuthRules().size(), 1);
Assert.assertEquals(authRepository.getJwtRules().size(), 1);
}
@Test
public void testLabelRoutingTransform() throws Exception {
DiscoveryResponse discoveryResponse = decodeResponse(
"src/test/resources/RdsResponse.in");
List<RouteConfiguration> routeConfigurations = rdsProtocol
.decodeXdsResponse(discoveryResponse);
if (routeConfigurations == null) {
throw new Exception("Can not parse route configurations from xds response");
}
if (routeDataRepository.getRouteRule(TARGET_SERVICE) == null) {
throw new Exception("Can not get target service from route configurations");
}
log.info("Label routing rules are {}", JSONObject
.toJSONString(routeDataRepository.getRouteRule(TARGET_SERVICE)));
}
/**
* dummy class for label routing filter service.
*/
static class Dummy {
}
@Configuration
@EnableAutoConfiguration
@ImportAutoConfiguration({ XdsAutoConfiguration.class,
AuthenticationAutoConfiguration.class, LabelRoutingAutoConfiguration.class })
public static class TestConfig {
@Bean(name = TARGET_SERVICE + FilterService.FEIGN_CLIENT_BEAN_SPECIFICATION)
public Dummy dummy() {
return new Dummy();
}
}
}

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-starters</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>spring-cloud-starter-alibaba-controlplane-opensergo</artifactId>
<name>Spring Cloud Alibaba OpenSergo Control Plane</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-commons</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opensergo</groupId>
<artifactId>opensergo-java-sdk</artifactId>
<version>${opensergo.version}</version>
</dependency>
<!-- Only for unit test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.16</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
</project>

@ -0,0 +1,64 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.opensergo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author panxiaojun233
* @author <a href="m13201628570@163.com"></a>
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.opensergo.config.enabled",
matchIfMissing = true)
@EnableConfigurationProperties(OpenSergoConfigProperties.class)
@AutoConfigureOrder(OpenSergoAutoConfig.OPENSERGO_RESOURCE_AUTO_CONFIG_ORDER)
public class OpenSergoAutoConfig {
/**
* Order of OpenSergo auto config.
*/
public static final int OPENSERGO_RESOURCE_AUTO_CONFIG_ORDER = 101;
@Autowired
private OpenSergoConfigProperties openSergoConfigProperties;
@Bean
public OpenSergoTrafficRouterParser openSergoTrafficRouterParser() {
return new OpenSergoTrafficRouterParser();
}
@Bean
public OpenSergoTrafficExchanger openSergoTrafficExchanger(
OpenSergoTrafficRouterParser openSergoTrafficRouterParser) {
return new OpenSergoTrafficExchanger(openSergoConfigProperties,
openSergoTrafficRouterParser);
}
@Bean
public TargetServiceChangedListener targetServiceChangedListener(
OpenSergoTrafficExchanger openSergoTrafficExchanger) {
return new TargetServiceChangedListener(openSergoConfigProperties,
openSergoTrafficExchanger);
}
}

@ -0,0 +1,59 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.opensergo;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author panxiaojun233
* @author <a href="m13201628570@163.com"></a>
*/
@ConfigurationProperties(OpenSergoConfigProperties.PREFIX)
public class OpenSergoConfigProperties {
/**
* Prefix in yaml.
*/
public static final String PREFIX = "spring.cloud.opensergo";
/**
* Configurations about OpenSergo Server Endpoint.
*/
private String endpoint;
/**
* Namespace Configuration about OpenSergo Config.
*/
private String namespace = "default";
public String getNamespace() {
return namespace;
}
void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
}

@ -0,0 +1,109 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.opensergo;
import java.util.Collection;
import java.util.List;
import com.alibaba.cloud.commons.governance.event.LabelRoutingDataChangedEvent;
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.governance.opensergo.util.ConvUtils;
import com.google.protobuf.InvalidProtocolBufferException;
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
import io.opensergo.ConfigKind;
import io.opensergo.OpenSergoClient;
import io.opensergo.subscribe.OpenSergoConfigSubscriber;
import io.opensergo.subscribe.SubscribeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* OpenSergoTrafficExchanger is the class which communicate with OpenSergo control plane.
*
* @author panxiaojun233
* @author <a href="m13201628570@163.com"></a>
*/
public class OpenSergoTrafficExchanger implements ApplicationContextAware {
protected static final Logger log = LoggerFactory
.getLogger(OpenSergoTrafficExchanger.class);
private OpenSergoClient client;
private ApplicationContext applicationContext;
private OpenSergoTrafficRouterParser openSergoTrafficRouterParser;
public OpenSergoTrafficExchanger(OpenSergoConfigProperties openSergoConfigProperties,
OpenSergoTrafficRouterParser openSergoTrafficRouterParser) {
Integer port = ConvUtils
.getOpenSergoPort(openSergoConfigProperties.getEndpoint());
String host = ConvUtils.getOpenSergoHost(openSergoConfigProperties.getEndpoint());
this.openSergoTrafficRouterParser = openSergoTrafficRouterParser;
try {
if (port != null && StringUtils.isNotEmpty(host)) {
client = new OpenSergoClient(host, port);
client.start();
}
else {
log.error("OpenSergo endpoint" + openSergoConfigProperties.getEndpoint()
+ " is illegal");
}
}
catch (Exception e) {
log.error("start OpenSergo client enhance error", e);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
public void subscribeTrafficRouterConfig(String namespace, String appName) {
client.subscribeConfig(
new SubscribeKey(namespace, appName, ConfigKind.TRAFFIC_ROUTER_STRATEGY),
new OpenSergoConfigSubscriber() {
@Override
public boolean onConfigUpdate(SubscribeKey subscribeKey,
Object dataList) {
log.debug("OpenSergo client subscribeKey:{} receive message :{}",
subscribeKey, dataList);
try {
Collection<UnifiedRouteDataStructure> rules = openSergoTrafficRouterParser
.resolveLabelRouting(
(List<RouteConfiguration>) dataList);
applicationContext.publishEvent(
new LabelRoutingDataChangedEvent(this, rules));
}
catch (InvalidProtocolBufferException e) {
log.error("resolve label routing enhance error", e);
return false;
}
return true;
}
});
}
}

@ -0,0 +1,253 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.opensergo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.cloud.commons.governance.labelrouting.LabelRouteRule;
import com.alibaba.cloud.commons.governance.labelrouting.MatchService;
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule;
import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule;
import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.commons.matcher.StringMatcher;
import com.alibaba.cloud.commons.matcher.StringMatcherType;
import com.alibaba.cloud.governance.opensergo.util.ConvUtils;
import com.google.protobuf.InvalidProtocolBufferException;
import io.envoyproxy.envoy.config.route.v3.ClusterSpecifierPlugin;
import io.envoyproxy.envoy.config.route.v3.HeaderMatcher;
import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher;
import io.envoyproxy.envoy.config.route.v3.Route;
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
import io.envoyproxy.envoy.config.route.v3.RouteMatch;
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
import io.envoyproxy.envoy.config.route.v3.WeightedCluster;
import io.opensergo.proto.router.v1.ClusterFallbackConfig_ClusterConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author panxiaojun233
* @author <a href="m13201628570@163.com"></a>
*/
public class OpenSergoTrafficRouterParser {
protected static final Logger log = LoggerFactory
.getLogger(OpenSergoTrafficRouterParser.class);
private static final String HEADER = "header";
private static final String PARAMETER = "parameter";
private static final String PATH = "path";
public OpenSergoTrafficRouterParser() {
}
/**
* transform rds RouterConfig list to spring cloud alibaba router data list.
* @param routeConfigurations the routerConfig list from OpenSergo control plane.
* @return spring cloud alibaba router rules.
* @throws InvalidProtocolBufferException transform exception.
*/
public Collection<UnifiedRouteDataStructure> resolveLabelRouting(
List<RouteConfiguration> routeConfigurations)
throws InvalidProtocolBufferException {
if (routeConfigurations == null) {
return new ArrayList<>();
}
Map<String, UnifiedRouteDataStructure> unifiedRouteDataStructures = new HashMap<>();
for (RouteConfiguration routeConfiguration : routeConfigurations) {
List<VirtualHost> virtualHosts = routeConfiguration.getVirtualHostsList();
for (VirtualHost virtualHost : virtualHosts) {
UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure();
String targetService = "";
String[] serviceAndPort = virtualHost.getName().split(":");
if (serviceAndPort.length > 0) {
targetService = serviceAndPort[0].split("\\.")[0];
}
unifiedRouteDataStructure.setTargetService(targetService);
List<Route> routes = virtualHost.getRoutesList();
LabelRouteRule labelRouteRule = getLabelRouteData(routes);
unifiedRouteDataStructure.setLabelRouteRule(labelRouteRule);
unifiedRouteDataStructures.put(
unifiedRouteDataStructure.getTargetService(),
unifiedRouteDataStructure);
}
}
return unifiedRouteDataStructures.values();
}
private LabelRouteRule getLabelRouteData(List<Route> routes)
throws InvalidProtocolBufferException {
List<MatchService> matchServices = new ArrayList<>();
LabelRouteRule labelRouteRule = new LabelRouteRule();
for (Route route : routes) {
ClusterSpecifierPlugin clusterSpecifierPlugin = route.getRoute()
.getInlineClusterSpecifierPlugin();
String cluster = "";
String fallbackCluster = "";
if (clusterSpecifierPlugin != null) {
ClusterFallbackConfig_ClusterConfig fallbackConfig = ConvUtils
.convFallbackClusterConfig(clusterSpecifierPlugin);
fallbackCluster = fallbackConfig.getFallbackCluster();
cluster = fallbackConfig.getRoutingCluster();
}
if (StringUtils.isEmpty(cluster)) {
cluster = route.getRoute().getCluster();
}
if (StringUtils.isNotEmpty(cluster)) {
MatchService matchService = null;
if (StringUtils.isNotEmpty(fallbackCluster)) {
matchService = getMatchService(route, cluster, 100,
getVersion(route, fallbackCluster));
}
else {
matchService = getMatchService(route, cluster, 100);
}
matchServices.add(matchService);
}
WeightedCluster weightedCluster = route.getRoute().getWeightedClusters();
for (WeightedCluster.ClusterWeight clusterWeight : weightedCluster
.getClustersList()) {
MatchService matchService = getMatchService(route,
clusterWeight.getName(), clusterWeight.getWeight().getValue());
matchServices.add(matchService);
}
}
labelRouteRule.setMatchRouteList(matchServices);
if (!matchServices.isEmpty()) {
labelRouteRule.setDefaultRouteVersion(
matchServices.get(matchServices.size() - 1).getVersion());
}
return labelRouteRule;
}
private MatchService getMatchService(Route route, String cluster, int weight) {
return getMatchService(route, cluster, weight, null);
}
private MatchService getMatchService(Route route, String cluster, int weight,
String fallback) {
String version = getVersion(route, cluster);
MatchService matchService = new MatchService();
matchService.setVersion(version);
matchService.setRuleList(match2RouteRules(route.getMatch()));
matchService.setWeight(weight);
if (StringUtils.isNotEmpty(fallback)) {
matchService.setFallback(fallback);
}
return matchService;
}
private String getVersion(Route route, String cluster) {
String version = "";
try {
String[] info = cluster.split("\\|");
version = info[2];
}
catch (Exception e) {
log.error("invalid cluster info for route {}", route.getName());
}
return version;
}
private List<RouteRule> match2RouteRules(RouteMatch routeMatch) {
List<RouteRule> routeRules = new ArrayList<>();
for (HeaderMatcher headerMatcher : routeMatch.getHeadersList()) {
HeaderRule headerRule = headerMatcher2HeaderRule(headerMatcher);
if (headerRule != null) {
routeRules.add(headerRule);
}
}
for (QueryParameterMatcher parameterMatcher : routeMatch
.getQueryParametersList()) {
UrlRule.Parameter parameter = parameterMatcher2ParameterRule(
parameterMatcher);
if (parameter != null) {
routeRules.add(parameter);
}
}
UrlRule.Path path = new UrlRule.Path();
path.setType(PATH);
switch (routeMatch.getPathSpecifierCase()) {
case PREFIX:
path.setCondition(StringMatcherType.PREFIX.toString());
path.setValue(routeMatch.getPrefix());
break;
case PATH:
path.setCondition(StringMatcherType.EXACT.toString());
path.setValue(routeMatch.getPath());
break;
case SAFE_REGEX:
path.setCondition(StringMatcherType.REGEX.toString());
path.setValue(routeMatch.getSafeRegex().getRegex());
break;
default:
// unknown type
path = null;
}
if (path != null) {
routeRules.add(path);
}
return routeRules;
}
private UrlRule.Parameter parameterMatcher2ParameterRule(
QueryParameterMatcher queryParameterMatcher) {
UrlRule.Parameter parameter = new UrlRule.Parameter();
StringMatcher stringMatcher = ConvUtils
.convStringMatcher(queryParameterMatcher.getStringMatch());
if (stringMatcher != null) {
parameter.setCondition(stringMatcher.getType().toString());
parameter.setKey(queryParameterMatcher.getName());
parameter.setValue(stringMatcher.getMatcher());
parameter.setType(PARAMETER);
return parameter;
}
return null;
}
private HeaderRule headerMatcher2HeaderRule(HeaderMatcher headerMatcher) {
StringMatcher stringMatcher = ConvUtils
.convStringMatcher(ConvUtils.headerMatch2StringMatch(headerMatcher));
if (stringMatcher != null) {
HeaderRule headerRule = new HeaderRule();
headerRule.setCondition(stringMatcher.getType().toString());
headerRule.setKey(headerMatcher.getName());
headerRule.setValue(stringMatcher.getMatcher());
headerRule.setType(HEADER);
return headerRule;
}
return null;
}
}

@ -0,0 +1,53 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.opensergo;
import com.alibaba.cloud.commons.governance.event.TargetServiceChangedEvent;
import org.springframework.context.ApplicationListener;
/**
* Subscribe OpenSergo configuration when provider service changed.
*
* @author panxiaojun233
* @author <a href="m13201628570@163.com"></a>
*/
public class TargetServiceChangedListener
implements ApplicationListener<TargetServiceChangedEvent> {
private OpenSergoTrafficExchanger openSergoTrafficExchanger;
private OpenSergoConfigProperties openSergoConfigProperties;
public TargetServiceChangedListener(
OpenSergoConfigProperties openSergoConfigProperties,
OpenSergoTrafficExchanger openSergoTrafficExchanger) {
this.openSergoConfigProperties = openSergoConfigProperties;
this.openSergoTrafficExchanger = openSergoTrafficExchanger;
}
@Override
public void onApplicationEvent(TargetServiceChangedEvent targetServiceChangedEvent) {
Object source = targetServiceChangedEvent.getSource();
if (source instanceof String) {
String targetService = (String) targetServiceChangedEvent.getSource();
openSergoTrafficExchanger.subscribeTrafficRouterConfig(
openSergoConfigProperties.getNamespace(), targetService);
}
}
}

@ -0,0 +1,132 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.opensergo.util;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.commons.matcher.StringMatcher;
import com.alibaba.cloud.commons.matcher.StringMatcherType;
import com.google.protobuf.Internal;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import io.envoyproxy.envoy.config.route.v3.ClusterSpecifierPlugin;
import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher;
import io.opensergo.proto.router.v1.ClusterFallbackConfig_ClusterConfig;
/**
* @author panxiaojun233
* @author <a href="m13201628570@163.com"></a>
*/
public final class ConvUtils {
private ConvUtils() {
}
public static String getOpenSergoHost(String endpoint) {
if (StringUtils.isNotEmpty(endpoint)) {
return endpoint.split(":")[0];
}
return null;
}
public static Integer getOpenSergoPort(String endpoint) {
if (StringUtils.isNotEmpty(endpoint)) {
String portStr = endpoint.split(":")[1];
return Integer.valueOf(portStr);
}
return null;
}
public static ClusterFallbackConfig_ClusterConfig convFallbackClusterConfig(
ClusterSpecifierPlugin clusterSpecifierPlugin)
throws InvalidProtocolBufferException {
Message defaultInstance = Internal
.getDefaultInstance(ClusterFallbackConfig_ClusterConfig.class);
return (ClusterFallbackConfig_ClusterConfig) defaultInstance.getParserForType()
.parseFrom(clusterSpecifierPlugin.getExtension().getTypedConfig()
.getValue());
}
public static StringMatcher convStringMatcher(
io.envoyproxy.envoy.type.matcher.v3.StringMatcher stringMatcher) {
if (stringMatcher == null) {
return null;
}
boolean isIgnoreCase = stringMatcher.getIgnoreCase();
String exact = stringMatcher.getExact();
String prefix = stringMatcher.getPrefix();
String suffix = stringMatcher.getSuffix();
String contains = stringMatcher.getContains();
String regex = stringMatcher.getSafeRegex().getRegex();
if (StringUtils.isNotBlank(exact)) {
return new StringMatcher(exact, StringMatcherType.EXACT, isIgnoreCase);
}
if (StringUtils.isNotBlank(prefix)) {
return new StringMatcher(prefix, StringMatcherType.PREFIX, isIgnoreCase);
}
if (StringUtils.isNotBlank(suffix)) {
return new StringMatcher(suffix, StringMatcherType.SUFFIX, isIgnoreCase);
}
if (StringUtils.isNotBlank(contains)) {
return new StringMatcher(contains, StringMatcherType.CONTAIN, isIgnoreCase);
}
if (StringUtils.isNotBlank(regex)) {
return new StringMatcher(regex);
}
return null;
}
public static io.envoyproxy.envoy.type.matcher.v3.StringMatcher headerMatch2StringMatch(
io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) {
if (headerMatcher == null) {
return null;
}
if (headerMatcher.getPresentMatch()) {
io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher
.newBuilder();
return builder.setSafeRegex(RegexMatcher.newBuilder().build())
.setIgnoreCase(true).build();
}
if (!headerMatcher.hasStringMatch()) {
io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher
.newBuilder();
String exactMatch = headerMatcher.getExactMatch();
String containsMatch = headerMatcher.getContainsMatch();
String prefixMatch = headerMatcher.getPrefixMatch();
String suffixMatch = headerMatcher.getSuffixMatch();
RegexMatcher safeRegex = headerMatcher.getSafeRegexMatch();
if (!StringUtils.isEmpty(exactMatch)) {
builder.setExact(exactMatch);
}
else if (!StringUtils.isEmpty(containsMatch)) {
builder.setContains(containsMatch);
}
else if (!StringUtils.isEmpty(prefixMatch)) {
builder.setPrefix(prefixMatch);
}
else if (!StringUtils.isEmpty(suffixMatch)) {
builder.setSuffix(suffixMatch);
}
else if (safeRegex.isInitialized()) {
builder.setSafeRegex(safeRegex);
}
return builder.setIgnoreCase(true).build();
}
return headerMatcher.getStringMatch();
}
}

@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.governance.opensergo.OpenSergoAutoConfig

@ -0,0 +1,72 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.opensergo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
import com.alibaba.fastjson.JSONObject;
import io.envoyproxy.envoy.config.route.v3.HeaderMatcher;
import io.envoyproxy.envoy.config.route.v3.Route;
import io.envoyproxy.envoy.config.route.v3.RouteAction;
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
import io.envoyproxy.envoy.config.route.v3.RouteMatch;
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
import io.envoyproxy.envoy.type.matcher.v3.StringMatcher;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author panxiaojun233
* @author <a href="m13201628570@163.com"></a>
*/
public class OpenSergoRuleTests {
private static final Logger log = LoggerFactory.getLogger(OpenSergoRuleTests.class);
private OpenSergoTrafficRouterParser openSergoTrafficRouterParser = new OpenSergoTrafficRouterParser();
@Test
public void testOpenSergoTrafficRouterTransform() throws Exception {
HeaderMatcher headerMatcher = HeaderMatcher.newBuilder().setName("x-tag")
.setStringMatch(StringMatcher.newBuilder().setExact("v2").buildPartial())
.build();
RouteMatch routeMatch = RouteMatch.newBuilder().addHeaders(headerMatcher).build();
Route route = Route.newBuilder().setMatch(routeMatch)
.setRoute(RouteAction.newBuilder()
.setCluster(
"outbound||v2|service-provider.default.svc.cluster.local")
.build())
.build();
VirtualHost virtualHost = VirtualHost.newBuilder().setName("service-provider")
.addDomains("service-provider.default.svc.cluster.local").addRoutes(route)
.build();
RouteConfiguration routeConfiguration = RouteConfiguration.newBuilder()
.setName("service-provider").addVirtualHosts(virtualHost).build();
List<RouteConfiguration> routeConfigurations = new ArrayList<>();
routeConfigurations.add(routeConfiguration);
Collection<UnifiedRouteDataStructure> rules = openSergoTrafficRouterParser
.resolveLabelRouting(routeConfigurations);
log.info("TrafficRouter rules are {}", JSONObject.toJSONString(rules));
Assert.assertEquals(rules.size(), 1);
}
}

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-starters</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
<name>Spring Cloud Starter Alibaba Governance Authentication</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>${jose4j.version}</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.16</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,39 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.auth;
import com.alibaba.cloud.governance.auth.repository.AuthRepository;
import com.alibaba.cloud.governance.auth.validator.AuthValidator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(AuthenticationAutoConfiguration.class)
public class AuthValidatorAutoConfiguration {
@Bean
public AuthValidator authValidator(AuthRepository authRepository) {
return new AuthValidator(authRepository);
}
}

@ -0,0 +1,54 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.auth;
import com.alibaba.cloud.governance.auth.listener.AuthListener;
import com.alibaba.cloud.governance.auth.repository.AuthRepository;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
@Configuration(proxyBeanMethods = false)
// We need to auto config the class before spring cloud alibaba istio module, to prevent
// event publisher hang permanently.
@AutoConfigureOrder(AuthenticationAutoConfiguration.AUTH_AUTO_CONFIG_ORDER)
public class AuthenticationAutoConfiguration {
/**
* Order of auth auto config.
*/
public static final int AUTH_AUTO_CONFIG_ORDER = 9;
@Bean
@ConditionalOnMissingBean
public AuthRepository authRepository() {
return new AuthRepository();
}
@Bean
public AuthListener authListener(AuthRepository authRepository) {
return new AuthListener(authRepository);
}
}

@ -0,0 +1,53 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.auth;
import com.alibaba.cloud.governance.auth.validator.AuthValidator;
import com.alibaba.cloud.governance.auth.webmvc.AuthWebInterceptor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(AuthenticationAutoConfiguration.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled",
matchIfMissing = true)
public class XdsWebAutoConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled",
matchIfMissing = true)
public AuthWebInterceptor authWebInterceptor(AuthValidator authValidator) {
return new AuthWebInterceptor(authValidator);
}
@Bean
@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled",
matchIfMissing = true)
public XdsWebMvcConfigurer xdsWebMvcConfigurer() {
return new XdsWebMvcConfigurer();
}
}

@ -0,0 +1,46 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.auth;
import com.alibaba.cloud.governance.auth.validator.AuthValidator;
import com.alibaba.cloud.governance.auth.webflux.AuthWebFluxFilter;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(AuthValidatorAutoConfiguration.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled",
matchIfMissing = true)
public class XdsWebFluxAutoConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled",
matchIfMissing = true)
public AuthWebFluxFilter authWebFluxFilter(AuthValidator authValidator) {
return new AuthWebFluxFilter(authValidator);
}
}

@ -0,0 +1,44 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.auth;
import java.util.Optional;
import com.alibaba.cloud.governance.auth.webmvc.AuthWebInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public class XdsWebMvcConfigurer implements WebMvcConfigurer {
@Autowired
private Optional<AuthWebInterceptor> authWebInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
if (!authWebInterceptor.isPresent()) {
return;
}
registry.addInterceptor(authWebInterceptor.get());
}
}

@ -0,0 +1,52 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.auth.listener;
import com.alibaba.cloud.commons.governance.auth.rule.AuthRules;
import com.alibaba.cloud.commons.governance.event.AuthDataChangedEvent;
import com.alibaba.cloud.governance.auth.repository.AuthRepository;
import org.springframework.context.ApplicationListener;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a> To receive the auth data when it is
* changed by user.
*/
public class AuthListener implements ApplicationListener<AuthDataChangedEvent> {
private final AuthRepository authRepository;
public AuthListener(AuthRepository authRepository) {
this.authRepository = authRepository;
}
@Override
public void onApplicationEvent(AuthDataChangedEvent event) {
AuthRules authRules = event.getAuthRules();
if (authRules.getAllowAuthRules() != null) {
authRepository.setAllowAuthRule(authRules.getAllowAuthRules());
}
if (authRules.getDenyAuthRules() != null) {
authRepository.setDenyAuthRules(authRules.getDenyAuthRules());
}
if (authRules.getJwtRules() != null) {
authRepository.setJwtRule(authRules.getJwtRules());
}
}
}

@ -0,0 +1,73 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.auth.repository;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.cloud.commons.governance.auth.rule.AuthRule;
import com.alibaba.cloud.commons.governance.auth.rule.JwtRule;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a> To store auth rules in Spring Cloud
* Alibaba.
*/
public class AuthRepository {
private Map<String, AuthRule> allowAuthRules = new HashMap<>();
private Map<String, AuthRule> denyAuthRules = new HashMap<>();
private Map<String, JwtRule> jwtRules = new HashMap<>();
public AuthRepository() {
}
public AuthRepository(Map<String, AuthRule> allowAuthRules,
Map<String, AuthRule> denyAuthRules, Map<String, JwtRule> jwtRules) {
this.allowAuthRules = allowAuthRules;
this.denyAuthRules = denyAuthRules;
this.jwtRules = jwtRules;
}
public Map<String, AuthRule> getAllowAuthRules() {
return allowAuthRules;
}
public Map<String, AuthRule> getDenyAuthRules() {
return denyAuthRules;
}
public Map<String, JwtRule> getJwtRules() {
return jwtRules;
}
public void setAllowAuthRule(Map<String, AuthRule> allowAuthRules) {
this.allowAuthRules = allowAuthRules;
}
public void setDenyAuthRules(Map<String, AuthRule> denyAuthRules) {
this.denyAuthRules = denyAuthRules;
}
public void setJwtRule(Map<String, JwtRule> jwtRules) {
this.jwtRules = jwtRules;
}
}

@ -0,0 +1,73 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.governance.auth.util;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import com.alibaba.cloud.commons.lang.StringUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
/**
* @author musi
* @author <a href="liuziming@buaa.edu.cn"></a>
*/
public final class IpUtil {
private static final String UNKNOWN = "unknown";
private IpUtil() {
}
public static String getRemoteIpAddress(ServerHttpRequest request) {
String ip = request.getHeaders().getFirst("X-Forwarded-For");
if (StringUtils.isNotEmpty(ip) && !UNKNOWN.equalsIgnoreCase(ip)) {
if (ip.contains(",")) {
ip = ip.split(",")[0];
}
}
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
if (request.getRemoteAddress() != null
&& request.getRemoteAddress().getAddress() != null) {
ip = request.getRemoteAddress().getAddress().getHostAddress();
}
}
return StringUtils.isEmpty(ip) ? null : ip;
}
public static String getRemoteIpAddress(ServletRequest request) {
if (!(request instanceof HttpServletRequest)) {
return null;
}
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String ip = httpServletRequest.getHeader("X-Forwarded-For");
if (StringUtils.isNotEmpty(ip) && !UNKNOWN.equalsIgnoreCase(ip)) {
if (ip.contains(",")) {
ip = ip.split(",")[0];
}
}
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
if (httpServletRequest.getRemoteAddr() != null) {
ip = httpServletRequest.getRemoteAddr();
}
}
return StringUtils.isEmpty(ip) ? null : ip;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save