Browse Source

Add authcodegrant-webflux sample

Issue: gh-5620
Rob Winch 7 years ago
parent
commit
7c5c274854

+ 64 - 0
samples/boot/authcodegrant-webflux/README.adoc

@@ -0,0 +1,64 @@
+= OAuth 2.0 Authorization Code Grant Sample
+
+== GitHub Repositories
+
+This guide provides instructions on setting up the sample application, which leverages the OAuth 2.0 Authorization Code Grant, and displays a list of public GitHub repositories that are accessible to the authenticated user.
+
+This includes repositories owned by the authenticated user, repositories where the authenticated user is a collaborator, and repositories that the authenticated user has access to through an organization membership.
+
+The following sections provide detailed steps for setting up the sample and covers the following topics:
+
+* <<github-register-application,Register OAuth application>>
+* <<github-application-config,Configure application.yml>>
+* <<github-boot-application,Boot up the application>>
+
+[[github-register-application]]
+=== Register OAuth application
+
+To use GitHub's OAuth 2.0 authorization system, you must https://github.com/settings/applications/new[Register a new OAuth application].
+
+When registering the OAuth application, ensure the *Authorization callback URL* is set to `http://localhost:8080/github-repos`.
+
+The Authorization callback URL (redirect URI) is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with GitHub and have granted access to the OAuth application on the _Authorize application_ page.
+
+[[github-application-config]]
+=== Configure application.yml
+
+Now that you have a new OAuth application with GitHub, you need to configure the sample to use the OAuth application for the _authorization code grant flow_.
+To do so:
+
+. Go to `application.yml` and set the following configuration:
++
+[source,yaml]
+----
+spring:
+  security:
+    oauth2:
+      client:
+        registration:	<1>
+          github:       <2>
+            client-id: github-client-id
+            client-secret: github-client-secret
+            scope: public_repo
+            redirect-uri-template: "{baseUrl}/github-repos"
+            client-name: GitHub Repositories
+----
++
+.OAuth Client properties
+====
+<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties.
+<2> Following the base property prefix is the ID for the `ClientRegistration`, which is github.
+====
+
+. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier.
+
+[[github-boot-application]]
+=== Boot up the application
+
+Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`.
+You are then redirected to the default _auto-generated_ form login page.
+Log in using *'user'* (username) and *'password'* (password) and then you'll be redirected to GitHub for authentication.
+
+After authenticating with your GitHub credentials, the next page presented to you is "Authorize application".
+This page will ask you to *Authorize* the application you created in the previous step.
+Click _Authorize application_ to allow the OAuth application to access and display your public repository information.

+ 13 - 0
samples/boot/authcodegrant-webflux/spring-security-samples-boot-authcodegrant-webflux.gradle

@@ -0,0 +1,13 @@
+apply plugin: 'io.spring.convention.spring-sample-boot'
+
+dependencies {
+	compile project(':spring-security-config')
+	compile project(':spring-security-oauth2-client')
+	compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
+	compile 'org.springframework.boot:spring-boot-starter-webflux'
+	compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity4'
+	compile 'io.projectreactor.netty:reactor-netty'
+
+	testCompile project(':spring-security-test')
+	testCompile 'org.springframework.boot:spring-boot-starter-test'
+}

+ 57 - 0
samples/boot/authcodegrant-webflux/src/integration-test/java/sample/OAuth2AuthorizationCodeGrantApplicationTests.java

@@ -0,0 +1,57 @@
+/*
+ * Copyright 2002-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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 sample;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpHeaders;
+import org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter;
+import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+/**
+ * Integration tests for the OAuth 2.0 client filters {@link OAuth2AuthorizationRequestRedirectFilter}
+ * and {@link OAuth2AuthorizationCodeGrantFilter}. These filters work together to realize
+ * the OAuth 2.0 Authorization Code Grant flow.
+ *
+ * @author Joe Grandja
+ * @since 5.1
+ */
+@SpringBootTest
+@ActiveProfiles("test")
+@AutoConfigureWebTestClient
+@RunWith(SpringRunner.class)
+public class OAuth2AuthorizationCodeGrantApplicationTests {
+	@Autowired
+	private WebTestClient rest;
+
+	@Test
+	@WithMockUser
+	public void requestWhenClientNotAuthorizedThenRedirectForAuthorization() throws Exception {
+		this.rest.get()
+			.uri("http://localhost/repos")
+			.exchange()
+			.expectStatus().is3xxRedirection()
+			.expectHeader().valueMatches(HttpHeaders.LOCATION, "https://github.com/login/oauth/authorize\\?response_type=code&client_id=client-id&scope=public_repo&state=.{15,}&redirect_uri=http%3A%2F%2Flocalhost%2Fauthorize%2Foauth2%2Fcode%2Fgithub");
+	}
+
+}

+ 20 - 0
samples/boot/authcodegrant-webflux/src/integration-test/resources/application-test.yml

@@ -0,0 +1,20 @@
+logging:
+  level:
+    root: INFO
+    org.springframework.web: INFO
+    org.springframework.security: INFO
+#    org.springframework.boot.autoconfigure: DEBUG
+
+spring:
+  thymeleaf:
+    cache: false
+  security:
+    oauth2:
+      client:
+        registration:
+          github:
+            client-id: client-id
+            client-secret: client-secret
+            scope: public_repo
+            redirect-uri-template: "{baseUrl}/authorize/oauth2/code/github"
+            client-name: GitHub Repositories

+ 30 - 0
samples/boot/authcodegrant-webflux/src/main/java/sample/OAuth2AuthorizationCodeGrantApplication.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright 2002-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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 sample;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author Rob Winch
+ */
+@SpringBootApplication
+public class OAuth2AuthorizationCodeGrantApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(OAuth2AuthorizationCodeGrantApplication.class, args);
+	}
+}

+ 62 - 0
samples/boot/authcodegrant-webflux/src/main/java/sample/config/SecurityConfig.java

@@ -0,0 +1,62 @@
+/*
+ * Copyright 2002-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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 sample.config;
+
+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.core.userdetails.MapReactiveUserDetailsService;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
+import org.springframework.security.oauth2.client.web.server.AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository;
+import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+
+/**
+ * @author Rob Winch
+ */
+@EnableWebFluxSecurity
+public class SecurityConfig {
+
+	@Bean
+	SecurityWebFilterChain configure(ServerHttpSecurity http) throws Exception {
+		http
+			.authorizeExchange()
+				.anyExchange().authenticated()
+				.and()
+			.formLogin()
+				.and()
+			.oauth2()
+				.client();
+		return http.build();
+	}
+
+	@Bean
+	ServerOAuth2AuthorizedClientRepository authorizedClientRepository(ReactiveOAuth2AuthorizedClientService authorizedClientService) {
+		return new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);
+	}
+
+	@Bean
+	MapReactiveUserDetailsService userDetailsService() {
+		UserDetails userDetails = User.withDefaultPasswordEncoder()
+			.username("user")
+			.password("password")
+			.roles("USER")
+			.build();
+		return new MapReactiveUserDetailsService(userDetails);
+	}
+}

+ 37 - 0
samples/boot/authcodegrant-webflux/src/main/java/sample/config/WebClientConfig.java

@@ -0,0 +1,37 @@
+/*
+ * Copyright 2002-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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 sample.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
+import org.springframework.web.reactive.function.client.WebClient;
+
+/**
+ * @author Rob Winch
+ * @since 5.1
+ */
+@Configuration
+public class WebClientConfig {
+
+	@Bean
+	WebClient webClient() {
+		return WebClient.builder()
+				.filter(new ServerOAuth2AuthorizedClientExchangeFilterFunction())
+				.build();
+	}
+}

+ 62 - 0
samples/boot/authcodegrant-webflux/src/main/java/sample/web/GitHubReposController.java

@@ -0,0 +1,62 @@
+/*
+ * Copyright 2002-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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 sample.web;
+
+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 reactor.core.publisher.Mono;
+
+import java.security.Principal;
+import java.util.List;
+
+import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient;
+
+/**
+ * @author Joe Grandja
+ * @author Rob Winch
+ */
+@Controller
+public class GitHubReposController {
+	private final WebClient webClient;
+
+	public GitHubReposController(WebClient webClient) {
+		this.webClient = webClient;
+	}
+
+	@GetMapping("/")
+	public String index() {
+		return "redirect:/repos";
+	}
+
+	@GetMapping("/repos")
+	public String gitHubRepos(Model model, @RegisteredOAuth2AuthorizedClient("github") OAuth2AuthorizedClient authorizedClient, Principal principal) {
+		String endpointUri = "https://api.github.com/user/repos";
+		Mono<List> repos = this.webClient
+			.get()
+			.uri(endpointUri)
+			.attributes(oauth2AuthorizedClient(authorizedClient))
+			.retrieve()
+			.bodyToMono(List.class);
+		model.addAttribute("repos", repos);
+		model.addAttribute("username", principal.getName());
+
+		return "github-repos";
+	}
+}

+ 23 - 0
samples/boot/authcodegrant-webflux/src/main/resources/application.yml

@@ -0,0 +1,23 @@
+server:
+  port: 8080
+
+logging:
+  level:
+    root: INFO
+    org.springframework.web: INFO
+    org.springframework.security: INFO
+#    org.springframework.boot.autoconfigure: DEBUG
+
+spring:
+  thymeleaf:
+    cache: false
+  security:
+    oauth2:
+      client:
+        registration:
+          github:
+            client-id: your-app-client-id
+            client-secret: your-app-client-secret
+            scope: public_repo
+            redirect-uri-template: "{baseUrl}/authorize/oauth2/code/github"
+            client-name: GitHub Repositories

+ 26 - 0
samples/boot/authcodegrant-webflux/src/main/resources/templates/github-repos.html

@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
+<head>
+	<title>Spring Security - OAuth 2.0 Authorization Code Grant</title>
+	<meta charset="utf-8" />
+</head>
+<body>
+<div style="float: right" th:fragment="logout">
+	<div style="float:left">
+		<span style="font-weight:bold">User: </span><span th:text="${username}"></span>
+	</div>
+	<div style="float:none">&nbsp;</div>
+	<div style="float:right">
+		<a href="/logout">Log Out</a>
+	</div>
+</div>
+<h1>GitHub Repositories</h1>
+<div>
+	<ul>
+		<li th:each="repo : ${repos}">
+			<span style="font-weight:bold" th:text="${repo.name}"></span>: <span th:text="${repo.url}"></span>
+		</li>
+	</ul>
+</div>
+</body>
+</html>