parent
85d5d4083f
commit
7c5c274854
|
@ -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.
|
|
@ -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'
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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"> </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>
|
Loading…
Reference in New Issue