This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-samples.git


The following commit(s) were added to refs/heads/master by this push:
     new f9a3d5f4c Add RESTful Dubbo Integration with Spring Security and 
OAuth2 Sample (#1193)
f9a3d5f4c is described below

commit f9a3d5f4c10172512732e22a2cd973e3813ad76d
Author: ZeruiYang <zeruiyo...@gmail.com>
AuthorDate: Fri Dec 6 11:43:35 2024 +0800

    Add RESTful Dubbo Integration with Spring Security and OAuth2 Sample (#1193)
    
    * feat: add authorization server
    
    * feat: create a resource server
    
    * feat: create case-configuration.yml
    
    * feat: create case-versions.conf
    
    * feat: add RestExtension spi
    
    * fix: use restExtension to intercept request(unfinished)
    
    * fix: use restExtension to intercept request
    
    * feat: update case-configuration.yml
    
    * fix: use restExtension to intercept request
    
    * fix: change the name of the test file
    
    * fix: change the systemProps in the case-configuration.yml
    
    * fix: change the systemProps in the case-configuration.yml
    
    * feat: add test cases
    
    * fix: update config in AuthorizationSeverConfig.java
    
    ---------
    
    Co-authored-by: Sean Yang <oxs...@gmail.com>
---
 .../case-configuration.yml                         |  51 +++++++++
 .../case-versions.conf                             |  25 +++++
 .../pom.xml                                        |  61 +++++++++++
 .../spring-security-authorization-server/pom.xml   |  98 +++++++++++++++++
 .../dubbo/rest/demo/AuthorizationApplication.java  |  30 ++++++
 .../rest/demo/config/AuthorizationSeverConfig.java |  73 +++++++++++++
 .../src/main/resources/application.yml             |  27 +++++
 .../test/java/OAuth2AuthorizationServerTest.java   |  75 +++++++++++++
 .../spring-security-resource-server/pom.xml        | 106 +++++++++++++++++++
 .../dubbo/rest/demo/ResourceApplication.java       |  32 ++++++
 .../apache/dubbo/rest/demo/filter/OAuthFilter.java |  95 +++++++++++++++++
 .../dubbo/rest/demo/service/HelloService.java      |  29 ++++++
 .../rest/demo/service/impl/HelloServiceImpl.java   |  30 ++++++
 ...ubbo.rpc.protocol.tri.rest.filter.RestExtension |   1 +
 .../src/main/resources/application.yml             |  31 ++++++
 .../src/test/java/ResourceServerIT.java            | 116 +++++++++++++++++++++
 2-advanced/dubbo-samples-triple-rest/pom.xml       |   1 +
 17 files changed, 881 insertions(+)

diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/case-configuration.yml
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/case-configuration.yml
new file mode 100644
index 000000000..a6ffa9f6a
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/case-configuration.yml
@@ -0,0 +1,51 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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
+#
+#     http://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.
+
+services:
+  authorization:
+    type: app
+    basedir: spring-security-authorization-server
+    mainClass: org.apache.dubbo.rest.demo.AuthorizationApplication
+    systemProps:
+      - authorization.address=authorization
+    checkPorts:
+      - 9000
+    checkLog: "authorization service started"
+
+  resource:
+    type: app
+    basedir: spring-security-resource-server
+    mainClass: org.apache.dubbo.rest.demo.ResourceApplication
+    systemProps:
+      - authorization.address=authorization
+    checkPorts:
+      - 50051
+    checkLog: "resource service started"
+
+  test:
+    type: test
+    basedir: spring-security-resource-server
+    tests:
+      - "**/*IT.class"
+    systemProps:
+      - authorization.address=authorization
+      - resource.address=resource
+    waitPortsBeforeRun:
+      - authorization:9000
+      - resource:50051
+    depends_on:
+      - authorization
+      - resource
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/case-versions.conf
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/case-versions.conf
new file mode 100644
index 000000000..82aeb4560
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/case-versions.conf
@@ -0,0 +1,25 @@
+#
+#
+#   Licensed to the Apache Software Foundation (ASF) under one or more
+#   contributor license agreements.  See the NOTICE file distributed with
+#   this work for additional information regarding copyright ownership.
+#   The ASF licenses this file to You 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
+#
+#       http://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.
+#
+
+
+# Supported component versions of the test case
+
+# Spring app
+dubbo.version=3.3.*
+spring.version=6.*
+java.version= [>= 17]
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/pom.xml
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/pom.xml
new file mode 100644
index 000000000..f4d69047f
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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
+
+      http://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.
+  -->
+<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 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.dubbo</groupId>
+    <artifactId>dubbo-samples-triple-rest</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+  </parent>
+  <packaging>pom</packaging>
+  <modules>
+    <module>spring-security-resource-server</module>
+    <module>spring-security-authorization-server</module>
+  </modules>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.springframework.security</groupId>
+        <artifactId>spring-security-core</artifactId>
+        <version>6.2.2</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework.security</groupId>
+        <artifactId>spring-security-oauth2-authorization-server</artifactId>
+        <version>1.2.3</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework.security</groupId>
+        <artifactId>spring-security-oauth2-resource-server</artifactId>
+        <version>6.2.2</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework.security</groupId>
+        <artifactId>spring-security-oauth2-jose</artifactId>
+        <version>6.2.2</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <groupId>org.apache.dubbo.rest.demo</groupId>
+  <artifactId>dubbo-samples-triple-rest-spring-security</artifactId>
+
+</project>
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/pom.xml
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/pom.xml
new file mode 100644
index 000000000..3bfd26479
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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
+
+      http://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.
+  -->
+<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 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.dubbo.rest.demo</groupId>
+    <artifactId>dubbo-samples-triple-rest-spring-security</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+  </parent>
+
+  <groupId>org.apache.dubbo.rest.demo</groupId>
+  <artifactId>spring-security-authorization-server</artifactId>
+  <version>1.0-SNAPSHOT</version>
+
+  <properties>
+    <java.version>17</java.version>
+
+  </properties>
+
+  <dependencies>
+    <!-- Spring Boot Starter Web -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+    <!-- Spring Boot Starter Security -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-security</artifactId>
+    </dependency>
+
+    <!-- Spring Security OAuth2 Authorization Server -->
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-oauth2-authorization-server</artifactId>
+    </dependency>
+
+    <!-- Spring Security OAuth2 JOSE (for JWT support) -->
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-oauth2-jose</artifactId>
+      <exclusions>
+        <exclusion>
+          <artifactId>nimbus-jose-jwt</artifactId>
+          <groupId>com.nimbusds</groupId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
+    <!-- Spring Boot Starter Logging -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-logging</artifactId>
+    </dependency>
+
+    <!-- Spring Boot Starter Test (optional, for testing purposes) -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- Spring Security Test (optional, for testing security) -->
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/main/java/org/apache/dubbo/rest/demo/AuthorizationApplication.java
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/main/java/org/apache/dubbo/rest/demo/AuthorizationApplication.java
new file mode 100644
index 000000000..0d8278670
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/main/java/org/apache/dubbo/rest/demo/AuthorizationApplication.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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 org.apache.dubbo.rest.demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class AuthorizationApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(AuthorizationApplication.class, args);
+        System.out.println("authorization service started");
+    }
+}
+
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/main/java/org/apache/dubbo/rest/demo/config/AuthorizationSeverConfig.java
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/main/java/org/apache/dubbo/rest/demo/config/AuthorizationSeverConfig.java
new file mode 100644
index 000000000..393e0a903
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/main/java/org/apache/dubbo/rest/demo/config/AuthorizationSeverConfig.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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 org.apache.dubbo.rest.demo.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import 
org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import 
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import 
org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
+import 
org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
+import 
org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
+import 
org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
+import 
org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
+import org.springframework.security.web.SecurityFilterChain;
+
+import java.util.UUID;
+
+@Configuration
+@EnableWebSecurity
+public class AuthorizationSeverConfig {
+
+    private static final String HOST = 
System.getProperty("authorization.address", "localhost");
+
+    String issuer = "http://"; + HOST + ":9000";
+
+    @Bean
+    public SecurityFilterChain 
authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
+        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
+
+        return http.build();
+    }
+
+    @Bean
+    public RegisteredClientRepository registeredClientRepository() {
+        RegisteredClient registeredClient = 
RegisteredClient.withId(UUID.randomUUID().toString())
+                .clientId("49fd8518-12eb-422b-9264-2bae0ab89f66") //configure 
the client id
+                .clientSecret("{noop}H3DTtm2fR3GRAdr4ls1mcg") // configure the 
client secret
+                
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
+                
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
+                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
+                .redirectUri("http://localhost:9000/oauth2/token";) // 
configure the redirect uri
+                .scope("openid")
+                .scope("read")
+                .scope("write")
+                .build();
+
+        return new InMemoryRegisteredClientRepository(registeredClient);
+    }
+
+    @Bean
+    public AuthorizationServerSettings authorizationServerSettings() {
+        return AuthorizationServerSettings.builder()
+                .issuer(issuer) // set the address of the authorization server
+                .build();
+    }
+
+}
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/main/resources/application.yml
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/main/resources/application.yml
new file mode 100644
index 000000000..a4c8163a8
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/main/resources/application.yml
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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
+#
+#     http://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.
+
+server:
+  port: 9000
+
+spring:
+  application:
+    name: authorization-server  # Application name for Spring Boot
+
+logging:
+  level:
+    org.springframework.security: DEBUG
+    org.springframework.boot.autoconfigure.security: DEBUG
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/test/java/OAuth2AuthorizationServerTest.java
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/test/java/OAuth2AuthorizationServerTest.java
new file mode 100644
index 000000000..5cf033508
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-authorization-server/src/test/java/OAuth2AuthorizationServerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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.
+ */
+
+import org.apache.dubbo.rest.demo.AuthorizationApplication;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+
+import org.springframework.web.client.RestClient;
+import org.springframework.web.client.RestClientResponseException;
+
+import java.util.Base64;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+@SpringBootTest(classes = AuthorizationApplication.class, webEnvironment = 
SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class OAuth2AuthorizationServerTest {
+
+    @LocalServerPort
+    private int port;
+
+    private final String clientId = "49fd8518-12eb-422b-9264-2bae0ab89f66";
+    private final String clientSecret = "H3DTtm2fR3GRAdr4ls1mcg";
+
+    @Test
+    public void testClientCredentialsGrantFlow() {
+        assertNotEquals(0, port, "Port should not be 0");
+        // build Basic Auth header
+        String credentials = clientId + ":" + clientSecret;
+        String encodedCredentials = 
Base64.getEncoder().encodeToString(credentials.getBytes());
+        System.out.println("Encoded Credentials: " + encodedCredentials);
+
+        // build RestClient request
+        RestClient restClient = RestClient.builder().build();
+        String url = "http://localhost:"; + port + "/oauth2/token";
+
+        try {
+            // make a post request
+            String response = restClient.post()
+                    .uri(url)
+                    .header(HttpHeaders.AUTHORIZATION, "Basic " + 
encodedCredentials)
+                    .header(HttpHeaders.CONTENT_TYPE, 
MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+                    .body("grant_type=client_credentials&scope=read")
+                    .retrieve()
+                    .body(String.class);
+
+            System.out.println("Access Token Response: " + response);
+
+        } catch (RestClientResponseException e) {
+            // use getStatusCode().value() to get status code
+            assertEquals(HttpStatus.UNAUTHORIZED.value(), e.getStatusCode()
+                    .value(), "The request failed and was not authorized");
+            System.err.println("Error Response: " + 
e.getResponseBodyAsString());
+        }
+    }
+}
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/pom.xml
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/pom.xml
new file mode 100644
index 000000000..83f268c38
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/pom.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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
+
+      http://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.
+  -->
+<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 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.dubbo.rest.demo</groupId>
+    <artifactId>dubbo-samples-triple-rest-spring-security</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>spring-security-resource-server</artifactId>
+
+  <properties>
+    <java.version>17</java.version>
+    <spring.boot.version>3.2.3</spring.boot.version>
+    <dubbo.version>3.3.1</dubbo.version>
+  </properties>
+
+  <dependencies>
+    <!-- Dubbo Spring Boot Starter -->
+    <dependency>
+      <groupId>org.apache.dubbo</groupId>
+      <artifactId>dubbo-spring-boot-starter</artifactId>
+      <version>${dubbo.version}</version>
+    </dependency>
+
+    <!-- Spring Boot Starter Web -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+      <version>${spring.boot.version}</version>
+    </dependency>
+
+    <!-- Spring Boot Starter Logging -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-logging</artifactId>
+      <version>${spring.boot.version}</version>
+    </dependency>
+
+<!--    &lt;!&ndash; Spring Security Starter &ndash;&gt;-->
+<!--    <dependency>-->
+<!--      <groupId>org.springframework.boot</groupId>-->
+<!--      <artifactId>spring-boot-starter-security</artifactId>-->
+<!--      <version>${spring.boot.version}</version>-->
+<!--    </dependency>-->
+
+    <!-- Spring Security OAuth2 Resource Server -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
+      <version>${spring.boot.version}</version>
+    </dependency>
+
+    <!-- Spring Security OAuth2 JOSE (for JWT support) -->
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-oauth2-jose</artifactId>
+      <version>6.2.2</version>
+    </dependency>
+
+<!--    &lt;!&ndash; Dubbo Zookeeper Curator &ndash;&gt;-->
+<!--    <dependency>-->
+<!--      <groupId>org.apache.dubbo</groupId>-->
+<!--      <artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>-->
+<!--      <version>${dubbo.version}</version>-->
+<!--      <type>pom</type>-->
+<!--    </dependency>-->
+
+    <!-- Spring Boot Starter Test (optional, for testing purposes) -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <version>${spring.boot.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+        <version>${spring.boot.version}</version>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/ResourceApplication.java
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/ResourceApplication.java
new file mode 100644
index 000000000..305b2fde3
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/ResourceApplication.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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 org.apache.dubbo.rest.demo;
+
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+@EnableDubbo
+public class ResourceApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(ResourceApplication.class, args);
+        System.out.println("resource service started");
+    }
+}
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/filter/OAuthFilter.java
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/filter/OAuthFilter.java
new file mode 100644
index 000000000..f14ea351c
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/filter/OAuthFilter.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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 org.apache.dubbo.rest.demo.filter;
+
+import jakarta.servlet.Filter;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension;
+
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
+import 
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
+import 
org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
+
+import java.io.IOException;
+
+@Activate
+public class OAuthFilter implements Filter, RestExtension {
+
+    private static final String HOST = 
System.getProperty("authorization.address", "localhost");
+
+    String issuer = "http://"; + HOST + ":9000";
+
+    private JwtDecoder jwtDecoder;
+    private JwtAuthenticationConverter jwtAuthenticationConverter;
+
+    @Override
+    public void init(FilterConfig filterConfig) {
+        // Initialize the JwtDecoder and obtain the public key from the 
configured authorization server URL for decoding the JWT
+        jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
+        // Initialize JwtAuthenticationConverter to convert JWT
+        jwtAuthenticationConverter = new JwtAuthenticationConverter();
+        JwtGrantedAuthoritiesConverter authoritiesConverter = new 
JwtGrantedAuthoritiesConverter();
+        
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
+    }
+
+    @Override
+    public String[] getPatterns() {
+        return new String[] {"/**"}; // Intercept all requests
+    }
+
+    @Override
+    public void doFilter(
+            ServletRequest servletRequest,
+            ServletResponse servletResponse,
+            FilterChain filterChain) throws IOException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        String authorization = request.getHeader("Authorization");
+        if (authorization != null && authorization.startsWith("Bearer ")) {
+            String jwtToken = authorization.substring("Bearer ".length());
+            // Decode the JWT token
+            try {
+                Jwt jwt = jwtDecoder.decode(jwtToken);
+                jwtAuthenticationConverter.convert(jwt);
+                filterChain.doFilter(request, response);
+            } catch (Exception e) {
+                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, 
"Invalid JWT token");
+            }
+
+        } else {
+            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing 
JWT token");
+        }
+
+    }
+
+    @Override
+    public int getPriority() {
+        return -200;
+    }
+}
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/service/HelloService.java
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/service/HelloService.java
new file mode 100644
index 000000000..a2230a1f7
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/service/HelloService.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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 org.apache.dubbo.rest.demo.service;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@RequestMapping("/hello")
+public interface HelloService {
+
+    @GetMapping("/sayHello/{name}")
+    String sayHello(@PathVariable String name);
+}
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/service/impl/HelloServiceImpl.java
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/service/impl/HelloServiceImpl.java
new file mode 100644
index 000000000..49e1c1474
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/java/org/apache/dubbo/rest/demo/service/impl/HelloServiceImpl.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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 org.apache.dubbo.rest.demo.service.impl;
+
+import org.apache.dubbo.config.annotation.DubboService;
+import org.apache.dubbo.rest.demo.service.HelloService;
+
+@DubboService
+public class HelloServiceImpl implements HelloService {
+
+    @Override
+    public String sayHello(String name) {
+        return "Hello, " + name;
+    }
+}
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension
new file mode 100644
index 000000000..69e53cb32
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension
@@ -0,0 +1 @@
+OAuthFilter=org.apache.dubbo.rest.demo.filter.OAuthFilter
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/resources/application.yml
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/resources/application.yml
new file mode 100644
index 000000000..88148fd18
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/main/resources/application.yml
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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
+#
+#     http://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.
+server:
+  port: 9001
+
+spring:
+  application:
+    name: resource-server  # Application name for Spring Boot
+
+dubbo:
+  application:
+    name: ${spring.application.name}
+    qos-enable: false
+  protocol:
+    name: tri                 # Use the Triple protocol
+    port: 50051               # Service port (adjust as needed)
+    triple:
+      verbose: true           # Enable verbose mode
diff --git 
a/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/test/java/ResourceServerIT.java
 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/test/java/ResourceServerIT.java
new file mode 100644
index 000000000..aabfdeda4
--- /dev/null
+++ 
b/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-spring-security/spring-security-resource-server/src/test/java/ResourceServerIT.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *     http://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.
+ */
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+
+import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.junit.runner.RunWith;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.client.RestClientResponseException;
+
+import java.util.Base64;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@EnableDubbo
+@RunWith(SpringRunner.class)
+public class ResourceServerIT {
+
+    //    @LocalServerPort
+    //    private int port;
+
+//    @DubboReference(url = "tri://localhost:50051")
+//    private HelloService helloService;
+
+    private final String clientId = "49fd8518-12eb-422b-9264-2bae0ab89f66";
+    private final String clientSecret = "H3DTtm2fR3GRAdr4ls1mcg";
+
+    private static final String OAUTH2HOST = 
System.getProperty("authorization.address", "localhost");
+    private static final String HOST = System.getProperty("resource.address", 
"localhost");
+
+    @Test
+        public void testGetUserEndpoint() {
+            String credentials = clientId + ":" + clientSecret;
+            String encodedCredentials = 
Base64.getEncoder().encodeToString(credentials.getBytes());
+
+            // build RestClient request
+            RestClient restClient = RestClient.builder().build();
+            String url = "http://"+ OAUTH2HOST + ":9000/oauth2/token";
+
+            try {
+                // make a post request
+                String response = restClient.post()
+                        .uri(url)
+                        .header(HttpHeaders.AUTHORIZATION, "Basic " + 
encodedCredentials)
+                        .header(HttpHeaders.CONTENT_TYPE, 
MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+                        .body("grant_type=client_credentials&scope=read")
+                        .retrieve()
+                        .body(String.class);
+
+                ObjectMapper objectMapper = new ObjectMapper();
+                JsonNode jsonNode = objectMapper.readTree(response);
+                String accessToken = jsonNode.get("access_token").asText();
+
+                // Use the access token to authenticate the request to the 
/user endpoint
+                assert accessToken != null;
+                String userUrl = "http://"; + HOST + 
":50051/hello/sayHello/World";
+                try {
+                    String userResponse = restClient.get()
+                            .uri(userUrl)
+                            .header(HttpHeaders.AUTHORIZATION, "Bearer " + 
accessToken)
+                            .retrieve()
+                            .body(String.class);
+
+                    assertEquals("\"Hello, World\"", userResponse, "error");
+                } catch (RestClientResponseException e) {
+                    System.err.println("Error Response: " + 
e.getResponseBodyAsString());
+                    Assertions.fail("Request failed with response: " + 
e.getResponseBodyAsString());
+                }
+
+            } catch (JsonProcessingException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+    @Test
+    public void testGetUserEndpointWithInvalidToken() {
+        String invalidAccessToken = "invalid_token";
+        RestClient restClient = RestClient.builder().build();
+        String userUrl = "http://"; + HOST + ":50051/hello/sayHello/World";
+
+        try {
+            restClient.get()
+                    .uri(userUrl)
+                    .header(HttpHeaders.AUTHORIZATION, "Bearer " + 
invalidAccessToken)
+                    .retrieve()
+                    .body(String.class);
+
+            Assertions.fail("Request should have failed with an invalid 
token");
+        } catch (RestClientResponseException e) {
+            System.err.println("Error Response: " + 
e.getResponseBodyAsString());
+            assertEquals(401, e.getStatusCode().value(), "Expected 401 
Unauthorized status");
+        }
+    }
+}
diff --git a/2-advanced/dubbo-samples-triple-rest/pom.xml 
b/2-advanced/dubbo-samples-triple-rest/pom.xml
index 4826d5417..d0910334d 100644
--- a/2-advanced/dubbo-samples-triple-rest/pom.xml
+++ b/2-advanced/dubbo-samples-triple-rest/pom.xml
@@ -46,6 +46,7 @@
     <module>dubbo-samples-triple-rest-basic</module>
     <module>dubbo-samples-triple-rest-jaxrs</module>
     <module>dubbo-samples-triple-rest-springmvc</module>
+    <module>dubbo-samples-triple-rest-spring-security</module>
   </modules>
 
   <dependencyManagement>


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@dubbo.apache.org
For additional commands, e-mail: notifications-h...@dubbo.apache.org


Reply via email to