Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create sample spring-cloud-gateway + AAD resource server #147

Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 14 additions & 20 deletions aad/spring-security/docs/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

- [1. About](#1-about)
- [2. Ask for help](#2-ask-for-help)
- [3. Homeworks](#3-homeworks)
Expand All @@ -7,6 +6,8 @@
+ [4.1.1. OAuth 2](#411-oauth-2)
+ [4.1.2. SAML2](#412-saml2)
* [4.2. Reactive](#42-reactive)
+ [4.2.1. Web flux](#421-web-flux)
- [4.2.1.1. OAuth2](#4211-oauth2)



Expand All @@ -16,42 +17,35 @@


# 1. About
This repo demonstrates how to use [Azure Active Directory] in [Spring Boot] application by [spring-security].
This repo demonstrates how to use [Azure Active Directory](https://azure.microsoft.com/services/active-directory/) in [Spring Boot](https://spring.io/projects/spring-boot) application by [spring-security](https://github.com/spring-projects/spring-security).

# 2. Ask for help
If you have any question about these samples, please ask by [creating a new github issue].
If you have any question about these samples, please ask by [creating a new github issue](https://github.com/Azure-Samples/azure-spring-boot-samples/issues/new).

# 3. Homeworks
Each sample has homework, you can get the answer in [homework answers].
Each sample has homework, you can get the answer in [homework answers](homework-answers.md).

# 4. Sample applications

## 4.1. Servlet

### 4.1.1. OAuth 2

1. [login]
2. [client-access-resource-server]
3. [resource-server-check-permissions-by-claims-in-access-token]
4. [client-access-multiple-resource-server]
5. [resource-server-support-on-behalf-of-flow]
1. [login](./servlet/oauth2/login.md)
2. [client-access-resource-server](./servlet/oauth2/client-access-resource-server.md)
3. [resource-server-check-permissions-by-claims-in-access-token](./servlet/oauth2/resource-server-check-permissions-by-claims-in-access-token.md)
4. [client-access-multiple-resource-server](./servlet/oauth2/client-access-multiple-resource-server.md)
5. [resource-server-support-on-behalf-of-flow](./servlet/oauth2/resource-server-support-on-behalf-of-flow.md)

### 4.1.2. SAML2
To be done.

## 4.2. Reactive
To be done.

### 4.2.1. Web flux

#### 4.2.1.1. OAuth2

[Azure Active Directory]: https://azure.microsoft.com/services/active-directory/
[Spring Boot]: https://spring.io/projects/spring-boot
[spring-security]: https://github.com/spring-projects/spring-security
[creating a new github issue]: https://github.com/Azure-Samples/azure-spring-boot-samples/issues/new
[homework answers]: homework-answers.md
[login]: servlet/oauth2/login.md
[client-access-resource-server]: servlet/oauth2/client-access-resource-server.md
[resource-server-check-permissions-by-claims-in-access-token]: servlet/oauth2/resource-server-check-permissions-by-claims-in-access-token.md
[client-access-multiple-resource-server]: servlet/oauth2/client-access-multiple-resource-server.md
[resource-server-support-on-behalf-of-flow]: servlet/oauth2/resource-server-support-on-behalf-of-flow.md
1. [spring-cloud-gateway](./reactive/webflux/oauth2/spring-cloud-gateway.md)


Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
- [1. About](#1-about)
- [2. Get sample applications](#2-get-sample-applications)
- [3. Create resources in Azure](#3-create-resources-in-azure)
* [3.1. Create a tenant](#31-create-a-tenant)
* [3.2. Add a new user](#32-add-a-new-user)
* [3.3. Register client-1](#33-register-client-1)
* [3.4. Add a client secret for client-1](#34-add-a-client-secret-for-client-1)
* [3.5. Add a redirect URI for client-1](#35-add-a-redirect-uri-for-client-1)
* [3.6. Register resource-server-1](#36-register-resource-server-1)
* [3.7. Expose apis for resource-server-1](#37-expose-apis-for-resource-server-1)
* [3.8. Set accessTokenAcceptedVersion to 2 for resource-server-1](#38-set-accesstokenacceptedversion-to-2-for-resource-server-1)
* [3.9. Register resource-server-2](#39-register-resource-server-2)
* [3.10. Expose apis for resource-server-2](#310-expose-apis-for-resource-server-2)
* [3.11. Set accessTokenAcceptedVersion to 2 for resource-server-2](#311-set-accesstokenacceptedversion-to-2-for-resource-server-2)
- [4. Run sample applications](#4-run-sample-applications)
- [5. Homework](#5-homework)


# 1. About

This section will demonstrate this scenario:

![image](https://user-images.githubusercontent.com/13167207/148503627-700f287c-ea93-4957-b811-ad8f7f8c5ed3.png)

1. Client get [access token](https://docs.microsoft.com/azure/active-directory/develop/access-tokens) from [Azure Active Directory](https://azure.microsoft.com/services/active-directory/)
2. Client use the access token to access Gateway.
3. Gateway validate the access token. If the access token is valid, use the access token to access the ResourceServer. There are 2 ResourceServers, which ResourceServer to access depends on the request URL, it's configured in Gateway's application.yml. Gateway is implemented by [spring-cloud-gateway](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/)
4. Gateway get the response from ResourceServer, then return to Client.

# 2. Get sample applications
Get samples applications from in GitHub: [spring-cloud-gateway](../../../../reactive/webflux/oauth2/spring-cloud-gateway).

# 3. Create resources in Azure

## 3.1. Create a tenant
Read [document about creating an Azure AD tenant](https://docs.microsoft.com/azure/active-directory/develop/quickstart-create-new-tenant#create-a-new-azure-ad-tenant), create a new tenant. Get the tenant-id: **${tenant-id}**.

## 3.2. Add a new user
Read [document about adding users](https://docs.microsoft.com/azure/active-directory/fundamentals/add-users-azure-active-directory), add a new user: **user-1@${tenant-name}.com**. Get the user's password.

## 3.3. Register client-1
Read [document about registering an application](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app), register an application named **client-1**. Get the client-id: **${client-1-client-id}**.

## 3.4. Add a client secret for client-1
Read [document about adding a client secret](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app#add-a-client-secret), add a client secret. Get the client-secret value: **${client-1-client-secret}**.

## 3.5. Add a redirect URI for client-1
Read [document about adding a redirect URI](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app#add-a-redirect-uri), add 2 redirect URIs: **http://localhost:8080/login/oauth2/code/client-1-resource-server-1**, **http://localhost:8080/login/oauth2/code/client-1-resource-server-2**.

## 3.6. Register resource-server-1
Read [document about registering an application](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app), register an application named **resource-server-1**. Get the client-id: **${resource-server-1-client-id}**.

## 3.7. Expose apis for resource-server-1
Read [document about exposing an api](https://docs.microsoft.com/azure/active-directory/develop/quickstart-configure-app-expose-web-apis), expose 2 scopes for resource-server-1: **resource-server-1.scope-1** and **resource-server-1.scope-2**, choose **Admins and users** for **Who can consent** option.

## 3.8. Set accessTokenAcceptedVersion to 2 for resource-server-1
Read [document about Application manifest](https://docs.microsoft.com/azure/active-directory/develop/reference-app-manifest#accesstokenacceptedversion-attribute), set `accessTokenAcceptedVersion` to `2`.

## 3.9. Register resource-server-2
Read [document about registering an application](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app), register an application named **resource-server-2**. Get the client-id: **${resource-server-2-client-id}**.

## 3.10. Expose apis for resource-server-2
Read [document about exposing an api](https://docs.microsoft.com/azure/active-directory/develop/quickstart-configure-app-expose-web-apis), expose 2 scopes for resource-server-2: **resource-server-2.scope-1** and **resource-server-2.scope-2**, choose **Admins and users** for **Who can consent** option.

## 3.11. Set accessTokenAcceptedVersion to 2 for resource-server-2
Read [document about Application manifest](https://docs.microsoft.com/azure/active-directory/develop/reference-app-manifest#accesstokenacceptedversion-attribute), set `accessTokenAcceptedVersion` to `2`.

# 4. Run sample applications
1. Open sample application: [client](../../../../reactive/webflux/oauth2/spring-cloud-gateway/client), fill the placeholders in **application.yml**, then run the application.
2. Open sample application: [gateway](../../../../reactive/webflux/oauth2/spring-cloud-gateway/gateway), fill the placeholders in **application.yml**, then run the application.
3. Open sample application: [resource-server-1](../../../../reactive/webflux/oauth2/spring-cloud-gateway/resource-server-1), fill the placeholders in **application.yml**, then run the application.
4. Open sample application: [resource-server-2](../../../../reactive/webflux/oauth2/spring-cloud-gateway/resource-server-2), fill the placeholders in **application.yml**, then run the application.
5. Open browser(for example: [Edge](https://www.microsoft.com/edge?r=1)), close all [InPrivate window](https://support.microsoft.com/microsoft-edge/browse-inprivate-in-microsoft-edge-cd2c9a48-0bc4-b98e-5e46-ac40c84e27e2), and open a new InPrivate window.
6. Access **http://localhost:8080/**, it will return **Hello, this is client-1.**.
7. Access **http://localhost:8080/resource-server-1**, it will redirect to Microsoft consent page.
9. Input username and password (update password if it requests you to), it will return permission request page: let user permit **client-1** to access **resource-server-1**.
11. Click **Accept**, then it will return **Hello, resource-server-1.**. This means Client can access ResourceServer1 through Gateway successfully.
12. Access **http://localhost:8080/resource-server-2**, consent as before, it will return **Hello, this is resource-server-2.**.

# 5. Homework
1. Read [rfc6749](https://datatracker.ietf.org/doc/html/rfc6749).
13 changes: 12 additions & 1 deletion aad/spring-security/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
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>

<groupId>com.azure.spring</groupId>

<parent>
<groupId>com.azure.spring</groupId>
<artifactId>azure-spring-boot-samples</artifactId>
<version>1.0.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>spring-boot-application-with-azure-active-directory</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
Expand All @@ -16,6 +23,10 @@
</properties>

<modules>
<module>reactive/webflux/oauth2/spring-cloud-gateway/client</module>
<module>reactive/webflux/oauth2/spring-cloud-gateway/gateway</module>
<module>reactive/webflux/oauth2/spring-cloud-gateway/resource-server-1</module>
<module>reactive/webflux/oauth2/spring-cloud-gateway/resource-server-2</module>
<module>servlet/oauth2/login</module>
<module>servlet/oauth2/client-access-resource-server/client</module>
<module>servlet/oauth2/client-access-resource-server/resource-server</module>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.azure.spring</groupId>
<artifactId>azure-spring-boot-samples</artifactId>
<version>1.0.0</version>
<relativePath>../../../../../../../pom.xml</relativePath>
</parent>

<artifactId>spring-security-sample-reactive-webflux-oauth2-spring-cloud-gateway-client-application</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>Spring Security Sample: Reactive: Webflux: OAuth2: Spring Cloud Gateway: Client Application</name>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId> <!-- Require this because this project used WebClient。 -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ClientApplication {

public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class ApplicationConfiguration {

@Bean
public static WebClient webClient(ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
ServerOAuth2AuthorizedClientExchangeFilterFunction function =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository,
authorizedClientRepository);
return WebClient.builder()
.filter(function)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@EnableWebFluxSecurity
public class SecurityConfiguration {

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.oauth2Client();
return http.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {

@GetMapping("/")
public String home() {
return "Hello, this is client-1.";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client.controller;

import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient;

@RestController
public class ResourceServer1Controller {

private final WebClient webClient;

public ResourceServer1Controller(WebClient webClient) {
this.webClient = webClient;
}

@GetMapping("/resource-server-1")
public Mono<String> hello(@RegisteredOAuth2AuthorizedClient("client-1-resource-server-1") OAuth2AuthorizedClient client1ResourceServer1) {
return webClient
.get()
.uri("http://localhost:8083/resource-server-1")
.attributes(oauth2AuthorizedClient(client1ResourceServer1))
.retrieve()
.bodyToMono(String.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.azure.spring.sample.activedirectory.reactive.webflux.oauth2.gateway.client.controller;

import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient;

@RestController
public class ResourceServer2Controller {

private final WebClient webClient;

public ResourceServer2Controller(WebClient webClient) {
this.webClient = webClient;
}

@GetMapping("/resource-server-2")
public Mono<String> hello(@RegisteredOAuth2AuthorizedClient("client-1-resource-server-2") OAuth2AuthorizedClient client1ResourceServer1) {
return webClient
.get()
.uri("http://localhost:8083/resource-server-2")
.attributes(oauth2AuthorizedClient(client1ResourceServer1))
.retrieve()
.bodyToMono(String.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Please fill these placeholders before run this application:
# 1. ${tenant-id}
# 2. ${client-1-client-id}
# 3. ${client-1-client-secret}
# 4. ${resource-server-1-client-id}
# 5. ${resource-server-2-client-id}

logging:
level:
root: DEBUG
server:
port: 8080
spring:
security:
oauth2:
client:
provider: # Refs: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-common-oauth2-provider
azure-active-directory:
issuer-uri: https://login.microsoftonline.com/${tenant-id}/v2.0 # Refs: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#webflux-oauth2-login-openid-provider-configuration
user-name-attribute: name
registration:
client-1-resource-server-1:
provider: azure-active-directory
client-name: client-1-resource-server-1
client-id: ${client-1-client-id}
client-secret: ${client-1-client-secret}
scope: api://${resource-server-1-client-id}/resource-server-1.scope-1
client-1-resource-server-2:
provider: azure-active-directory
client-name: client-1-resource-server-2
client-id: ${client-1-client-id}
client-secret: ${client-1-client-secret}
scope: api://${resource-server-2-client-id}/resource-server-2.scope-1, AccessReview.Read.Al,
profiles:
active: develop
Loading