Browse Source

Add sample integrating client and resource server

Closes gh-25
Joe Grandja 5 years ago
parent
commit
bfb646fff4
19 changed files with 704 additions and 0 deletions
  1. 11 0
      samples/boot/oauth2-integration/README.adoc
  2. 9 0
      samples/boot/oauth2-integration/authorizationserver/spring-security-samples-boot-oauth2-integrated-authorizationserver.gradle
  3. 32 0
      samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/OAuth2AuthorizationServerApplication.java
  4. 77 0
      samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java
  5. 10 0
      samples/boot/oauth2-integration/authorizationserver/src/main/resources/application.yml
  6. 14 0
      samples/boot/oauth2-integration/client/spring-security-samples-boot-oauth2-integrated-client.gradle
  7. 32 0
      samples/boot/oauth2-integration/client/src/main/java/sample/OAuth2ClientApplication.java
  8. 71 0
      samples/boot/oauth2-integration/client/src/main/java/sample/config/SecurityConfig.java
  9. 61 0
      samples/boot/oauth2-integration/client/src/main/java/sample/config/WebClientConfig.java
  10. 75 0
      samples/boot/oauth2-integration/client/src/main/java/sample/web/AuthorizationController.java
  11. 49 0
      samples/boot/oauth2-integration/client/src/main/java/sample/web/DefaultController.java
  12. 38 0
      samples/boot/oauth2-integration/client/src/main/resources/application.yml
  13. 62 0
      samples/boot/oauth2-integration/client/src/main/resources/templates/index.html
  14. 34 0
      samples/boot/oauth2-integration/client/src/main/resources/templates/login.html
  15. 7 0
      samples/boot/oauth2-integration/resourceserver/spring-security-samples-boot-oauth2-integrated-resourceserver.gradle
  16. 32 0
      samples/boot/oauth2-integration/resourceserver/src/main/java/sample/OAuth2ResourceServerApplication.java
  17. 41 0
      samples/boot/oauth2-integration/resourceserver/src/main/java/sample/config/ResourceServerConfig.java
  18. 32 0
      samples/boot/oauth2-integration/resourceserver/src/main/java/sample/web/MessagesController.java
  19. 17 0
      samples/boot/oauth2-integration/resourceserver/src/main/resources/application.yml

+ 11 - 0
samples/boot/oauth2-integration/README.adoc

@@ -0,0 +1,11 @@
+= OAuth 2.0 Integration Sample
+
+This sample integrates `spring-security-oauth2-client` and `spring-security-oauth2-resource-server` with *Spring Authorization Server*.
+
+== Run the Sample
+
+* Run Authorization Server -> `./gradlew -b samples/boot/oauth2-integration/authorizationserver/spring-security-samples-boot-oauth2-integrated-authorizationserver.gradle bootRun`
+** *IMPORTANT:* Make sure to modify your `/etc/hosts` file to avoid problems with session cookie overwrites between `client` and `authorizationserver`. Simply add the entry `127.0.0.1	auth-server`
+* Run Resource Server -> `./gradlew -b samples/boot/oauth2-integration/resourceserver/spring-security-samples-boot-oauth2-integrated-resourceserver.gradle bootRun`
+* Run Client -> `./gradlew -b samples/boot/oauth2-integration/client/spring-security-samples-boot-oauth2-integrated-client.gradle bootRun`
+* Go to `http://localhost:8080` and login using *user1/password*

+ 9 - 0
samples/boot/oauth2-integration/authorizationserver/spring-security-samples-boot-oauth2-integrated-authorizationserver.gradle

@@ -0,0 +1,9 @@
+apply plugin: 'io.spring.convention.spring-sample-boot'
+
+dependencies {
+	compile 'org.springframework.boot:spring-boot-starter-web'
+	compile 'org.springframework.boot:spring-boot-starter-security'
+	compile project(':spring-security-oauth2-authorization-server')
+	compile project(':spring-security-config2')
+	compile project(':spring-security-crypto2')
+}

+ 32 - 0
samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/OAuth2AuthorizationServerApplication.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 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 sample;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author Joe Grandja
+ * @since 0.0.1
+ */
+@SpringBootApplication
+public class OAuth2AuthorizationServerApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(OAuth2AuthorizationServerApplication.class, args);
+	}
+
+}

+ 77 - 0
samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 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 sample.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.keys.KeyManager;
+import org.springframework.security.crypto.keys.StaticKeyGeneratingKeyManager;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+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.provisioning.InMemoryUserDetailsManager;
+
+import java.util.UUID;
+
+/**
+ * @author Joe Grandja
+ * @since 0.0.1
+ */
+@EnableWebSecurity
+@Import(OAuth2AuthorizationServerConfiguration.class)
+public class AuthorizationServerConfig {
+
+	// @formatter:off
+	@Bean
+	public RegisteredClientRepository registeredClientRepository() {
+		RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
+				.clientId("messaging-client")
+				.clientSecret("secret")
+				.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
+				.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
+				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
+				.redirectUri("http://localhost:8080/authorized")
+				.scope("message.read")
+				.scope("message.write")
+				.build();
+		return new InMemoryRegisteredClientRepository(registeredClient);
+	}
+	// @formatter:on
+
+	@Bean
+	public KeyManager keyManager() {
+		return new StaticKeyGeneratingKeyManager();
+	}
+
+	// @formatter:off
+	@Bean
+	public UserDetailsService users() {
+		UserDetails user = User.withDefaultPasswordEncoder()
+				.username("user1")
+				.password("password")
+				.roles("USER")
+				.build();
+		return new InMemoryUserDetailsManager(user);
+	}
+	// @formatter:on
+}

+ 10 - 0
samples/boot/oauth2-integration/authorizationserver/src/main/resources/application.yml

@@ -0,0 +1,10 @@
+server:
+  port: 9000
+
+logging:
+  level:
+    root: INFO
+    org.springframework.web: INFO
+    org.springframework.security: INFO
+    org.springframework.security.oauth2: INFO
+#    org.springframework.boot.autoconfigure: DEBUG

+ 14 - 0
samples/boot/oauth2-integration/client/spring-security-samples-boot-oauth2-integrated-client.gradle

@@ -0,0 +1,14 @@
+apply plugin: 'io.spring.convention.spring-sample-boot'
+
+dependencies {
+	compile 'org.springframework.boot:spring-boot-starter-web'
+	compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
+	compile 'org.springframework.boot:spring-boot-starter-security'
+	compile 'org.springframework.boot:spring-boot-starter-oauth2-client'
+	compile 'org.springframework:spring-webflux'
+	compile 'io.projectreactor.netty:reactor-netty'
+	compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
+	compile 'org.webjars:webjars-locator-core'
+	compile 'org.webjars:bootstrap:3.4.1'
+	compile 'org.webjars:jquery:3.4.1'
+}

+ 32 - 0
samples/boot/oauth2-integration/client/src/main/java/sample/OAuth2ClientApplication.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 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 sample;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author Joe Grandja
+ * @since 0.0.1
+ */
+@SpringBootApplication
+public class OAuth2ClientApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(OAuth2ClientApplication.class, args);
+	}
+
+}

+ 71 - 0
samples/boot/oauth2-integration/client/src/main/java/sample/config/SecurityConfig.java

@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020 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 sample.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+
+/**
+ * @author Joe Grandja
+ * @since 0.0.1
+ */
+@EnableWebSecurity
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+	// @formatter:off
+	@Override
+	public void configure(WebSecurity web) {
+		web
+			.ignoring()
+				.antMatchers("/webjars/**");
+	}
+	// @formatter:on
+
+	// @formatter:off
+	@Override
+	protected void configure(HttpSecurity http) throws Exception {
+		http
+			.authorizeRequests()
+				.anyRequest().authenticated()
+				.and()
+			.formLogin()
+				.loginPage("/login")
+				.failureUrl("/login-error")
+				.permitAll()
+				.and()
+			.oauth2Client();
+	}
+	// @formatter:on
+
+	// @formatter:off
+	@Bean
+	public UserDetailsService users() {
+		UserDetails user = User.withDefaultPasswordEncoder()
+				.username("user1")
+				.password("password")
+				.roles("USER")
+				.build();
+		return  new InMemoryUserDetailsManager(user);
+	}
+	// @formatter:on
+}

+ 61 - 0
samples/boot/oauth2-integration/client/src/main/java/sample/config/WebClientConfig.java

@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 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 sample.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
+import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
+import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
+import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
+import org.springframework.web.reactive.function.client.WebClient;
+
+/**
+ * @author Joe Grandja
+ * @since 0.0.1
+ */
+@Configuration
+public class WebClientConfig {
+
+	@Bean
+	WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
+		ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
+				new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
+		return WebClient.builder()
+				.apply(oauth2Client.oauth2Configuration())
+				.build();
+	}
+
+	@Bean
+	OAuth2AuthorizedClientManager authorizedClientManager(
+			ClientRegistrationRepository clientRegistrationRepository,
+			OAuth2AuthorizedClientRepository authorizedClientRepository) {
+
+		OAuth2AuthorizedClientProvider authorizedClientProvider =
+				OAuth2AuthorizedClientProviderBuilder.builder()
+						.authorizationCode()
+						.clientCredentials()
+						.build();
+		DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
+				clientRegistrationRepository, authorizedClientRepository);
+		authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
+
+		return authorizedClientManager;
+	}
+}

+ 75 - 0
samples/boot/oauth2-integration/client/src/main/java/sample/web/AuthorizationController.java

@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 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 sample.web;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId;
+import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient;
+
+/**
+ * @author Joe Grandja
+ * @since 0.0.1
+ */
+@Controller
+public class AuthorizationController {
+	private final WebClient webClient;
+	private final String messagesBaseUri;
+
+	public AuthorizationController(WebClient webClient,
+			@Value("${messages.base-uri}") String messagesBaseUri) {
+		this.webClient = webClient;
+		this.messagesBaseUri = messagesBaseUri;
+	}
+
+	@GetMapping(value = "/authorize", params = "grant_type=authorization_code")
+	public String authorizationCodeGrant(Model model,
+			@RegisteredOAuth2AuthorizedClient("messaging-client-authorization-code")
+					OAuth2AuthorizedClient authorizedClient) {
+
+		String[] messages = this.webClient
+				.get()
+				.uri(this.messagesBaseUri)
+				.attributes(oauth2AuthorizedClient(authorizedClient))
+				.retrieve()
+				.bodyToMono(String[].class)
+				.block();
+		model.addAttribute("messages", messages);
+
+		return "index";
+	}
+
+	@GetMapping(value = "/authorize", params = "grant_type=client_credentials")
+	public String clientCredentialsGrant(Model model) {
+
+		String[] messages = this.webClient
+				.get()
+				.uri(this.messagesBaseUri)
+				.attributes(clientRegistrationId("messaging-client-client-credentials"))
+				.retrieve()
+				.bodyToMono(String[].class)
+				.block();
+		model.addAttribute("messages", messages);
+
+		return "index";
+	}
+}

+ 49 - 0
samples/boot/oauth2-integration/client/src/main/java/sample/web/DefaultController.java

@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 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 sample.web;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+
+/**
+ * @author Joe Grandja
+ * @since 0.0.1
+ */
+@Controller
+public class DefaultController {
+
+	@GetMapping("/")
+	public String root() {
+		return "redirect:/index";
+	}
+
+	@GetMapping("/index")
+	public String index() {
+		return "index";
+	}
+
+	@GetMapping("/login")
+	public String login() {
+		return "login";
+	}
+
+	@GetMapping("/login-error")
+	public String loginError(Model model) {
+		model.addAttribute("loginError", true);
+		return login();
+	}
+}

+ 38 - 0
samples/boot/oauth2-integration/client/src/main/resources/application.yml

@@ -0,0 +1,38 @@
+server:
+  port: 8080
+
+logging:
+  level:
+    root: INFO
+    org.springframework.web: INFO
+    org.springframework.security: INFO
+    org.springframework.security.oauth2: INFO
+#    org.springframework.boot.autoconfigure: DEBUG
+
+spring:
+  thymeleaf:
+    cache: false
+  security:
+    oauth2:
+      client:
+        registration:
+          messaging-client-authorization-code:
+            provider: spring
+            client-id: messaging-client
+            client-secret: secret
+            authorization-grant-type: authorization_code
+            redirect-uri: "{baseUrl}/authorized"
+            scope: message.read,message.write
+          messaging-client-client-credentials:
+            provider: spring
+            client-id: messaging-client
+            client-secret: secret
+            authorization-grant-type: client_credentials
+            scope: message.read,message.write
+        provider:
+          spring:
+            authorization-uri: http://auth-server:9000/oauth2/authorize
+            token-uri: http://auth-server:9000/oauth2/token
+
+messages:
+  base-uri: http://localhost:8090/messages

+ 62 - 0
samples/boot/oauth2-integration/client/src/main/resources/templates/index.html

@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
+    <head>
+        <title>Spring Security OAuth 2.0 Sample</title>
+        <meta charset="utf-8" />
+        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+        <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
+    </head>
+    <body>
+        <div th:fragment="header">
+            <nav class="navbar navbar-default">
+                <div class="container">
+                    <div class="container-fluid">
+                        <div class="navbar-collapse collapse" id="navbar">
+                            <ul class="nav navbar-nav navbar-right">
+                                <li class="dropdown">
+                                    <a id="user-menu" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+                                        <span sec:authentication="name">User</span>
+                                    </a>
+                                    <ul class="dropdown-menu">
+                                        <li><a id="sign-out" href="javascript:document.logoutForm.submit()">Sign Out</a></li>
+                                    </ul>
+                                    <form name="logoutForm" th:action="@{/logout}" method="post" th:hidden="true">
+                                        <input hidden type="submit" value="Sign Out"/>
+                                    </form>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </nav>
+        </div>
+        <div class="container">
+            <div class="panel panel-default">
+                <div class="panel-heading">
+                    <h3 class="panel-title">Authorize the client using <span style="font-family:monospace">grant_type</span>:</h3>
+                </div>
+                <ul class="list-group">
+                    <li class="list-group-item">
+                        <a href="/authorize?grant_type=authorization_code" th:href="@{/authorize?grant_type=authorization_code}"><span style="font-size:medium">Authorization Code</span>&nbsp;&nbsp;<small class="text-muted">(Login to Spring Authorization Server using: user1/password)</small></a>
+                    </li>
+                    <li class="list-group-item">
+                        <a href="/authorize?grant_type=client_credentials" th:href="@{/authorize?grant_type=client_credentials}"><span style="font-size:medium">Client Credentials</span></a>
+                    </li>
+                </ul>
+                <div th:if="${messages}" class="panel-footer">
+                    <h4>Messages:</h4>
+                    <table class="table table-condensed">
+                        <tbody>
+                            <tr class="row" th:each="message : ${messages}">
+                                <td th:text="${message}">message</td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+        <script src="/webjars/jquery/jquery.min.js" th:src="@{/webjars/jquery/jquery.min.js}"></script>
+        <script src="/webjars/bootstrap/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
+    </body>
+</html>

+ 34 - 0
samples/boot/oauth2-integration/client/src/main/resources/templates/login.html

@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
+    <head>
+        <title>Spring Security OAuth 2.0 Sample</title>
+        <meta charset="utf-8" />
+        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+        <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
+	</head>
+    <body>
+        <div class="container">
+            <h1>Login</h1>
+            <p th:if="${loginError}" style="font-weight:bold;color:red;">Wrong username or password</p>
+            <form th:action="@{/login}" method="post">
+                <div class="form-row">
+                    <div class="form-group">
+                        <label for="username">Username</label>
+                        <input type="text" id="username" name="username" value="user1" autofocus="autofocus" class="form-control">
+                        <small class="form-text text-muted">user1 / password</small>
+                    </div>
+                </div>
+                <div class="form-row">
+                    <div class="form-group">
+                        <label for="password">Password</label>
+                        <input type="password" id="password" name="password" value="password" class="form-control">
+                    </div>
+                </div>
+                <button type="submit" class="btn btn-primary">Log in</button>
+            </form>
+        </div>
+        <script src="/webjars/jquery/jquery.min.js" th:src="@{/webjars/jquery/jquery.min.js}"></script>
+        <script src="/webjars/bootstrap/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
+    </body>
+</html>

+ 7 - 0
samples/boot/oauth2-integration/resourceserver/spring-security-samples-boot-oauth2-integrated-resourceserver.gradle

@@ -0,0 +1,7 @@
+apply plugin: 'io.spring.convention.spring-sample-boot'
+
+dependencies {
+	compile 'org.springframework.boot:spring-boot-starter-web'
+	compile 'org.springframework.boot:spring-boot-starter-security'
+	compile 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
+}

+ 32 - 0
samples/boot/oauth2-integration/resourceserver/src/main/java/sample/OAuth2ResourceServerApplication.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 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 sample;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author Joe Grandja
+ * @since 0.0.1
+ */
+@SpringBootApplication
+public class OAuth2ResourceServerApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(OAuth2ResourceServerApplication.class, args);
+	}
+
+}

+ 41 - 0
samples/boot/oauth2-integration/resourceserver/src/main/java/sample/config/ResourceServerConfig.java

@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 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 sample.config;
+
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+/**
+ * @author Joe Grandja
+ * @since 0.0.1
+ */
+@EnableWebSecurity
+public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
+
+	// @formatter:off
+	@Override
+	protected void configure(HttpSecurity http) throws Exception {
+		http
+			.mvcMatcher("/messages/**")
+				.authorizeRequests()
+					.mvcMatchers("/messages/**").access("hasAuthority('SCOPE_message.read')")
+					.and()
+				.oauth2ResourceServer()
+					.jwt();
+	}
+	// @formatter:on
+}

+ 32 - 0
samples/boot/oauth2-integration/resourceserver/src/main/java/sample/web/MessagesController.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 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 sample.web;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author Joe Grandja
+ * @since 0.0.1
+ */
+@RestController
+public class MessagesController {
+
+	@GetMapping("/messages")
+	public String[] getMessages() {
+		return new String[] {"Message 1", "Message 2", "Message 3"};
+	}
+}

+ 17 - 0
samples/boot/oauth2-integration/resourceserver/src/main/resources/application.yml

@@ -0,0 +1,17 @@
+server:
+  port: 8090
+
+logging:
+  level:
+    root: INFO
+    org.springframework.web: INFO
+    org.springframework.security: INFO
+    org.springframework.security.oauth2: INFO
+#    org.springframework.boot.autoconfigure: DEBUG
+
+spring:
+  security:
+    oauth2:
+      resourceserver:
+        jwt:
+          jwk-set-uri: http://auth-server:9000/oauth2/jwks