Add sample integrating client and resource server
Closes gh-25
This commit is contained in:
parent
eeca3df66b
commit
bfb646fff4
11
samples/boot/oauth2-integration/README.adoc
Normal file
11
samples/boot/oauth2-integration/README.adoc
Normal file
@ -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*
|
@ -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')
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
@ -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'
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
@ -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> <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>
|
@ -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>
|
@ -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'
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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"};
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
Loading…
Reference in New Issue
Block a user