Skip to content

Commit cc58086

Browse files
committed
Update demo-client sample
1 parent 3084c08 commit cc58086

File tree

8 files changed

+193
-268
lines changed

8 files changed

+193
-268
lines changed

samples/demo-client/samples-demo-client.gradle

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@ dependencies {
2222
implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
2323
implementation "org.springframework.boot:spring-boot-starter-security"
2424
implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
25-
implementation "org.springframework:spring-webflux"
26-
implementation "io.projectreactor.netty:reactor-netty"
2725
implementation "org.apache.httpcomponents.client5:httpclient5"
28-
implementation "org.webjars:webjars-locator-core"
26+
implementation "org.webjars:webjars-locator-lite"
2927
implementation "org.webjars:bootstrap:5.2.3"
3028
implementation "org.webjars:popper.js:2.9.3"
3129
implementation "org.webjars:jquery:3.6.4"

samples/demo-client/src/main/java/sample/authorization/DeviceCodeOAuth2AuthorizedClientProvider.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,6 +36,8 @@
3636
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
3737
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
3838
import org.springframework.util.Assert;
39+
import org.springframework.web.context.request.RequestContextHolder;
40+
import org.springframework.web.context.request.ServletRequestAttributes;
3941

4042
/**
4143
* @author Steve Riesenberg
@@ -107,7 +109,16 @@ private boolean hasTokenExpired(OAuth2Token token) {
107109
public static Function<OAuth2AuthorizeRequest, Map<String, Object>> deviceCodeContextAttributesMapper() {
108110
return (authorizeRequest) -> {
109111
HttpServletRequest request = authorizeRequest.getAttribute(HttpServletRequest.class.getName());
110-
Assert.notNull(request, "request cannot be null");
112+
if (request == null) {
113+
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
114+
.getRequestAttributes();
115+
if (requestAttributes != null) {
116+
request = requestAttributes.getRequest();
117+
}
118+
}
119+
if (request == null) {
120+
return Collections.emptyMap();
121+
}
111122

112123
// Obtain device code from request
113124
String deviceCode = request.getParameter(OAuth2ParameterNames.DEVICE_CODE);

samples/demo-client/src/main/java/sample/authorization/OAuth2DeviceAccessTokenResponseClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public final class OAuth2DeviceAccessTokenResponseClient implements OAuth2Access
4040
private RestClient restClient;
4141

4242
public OAuth2DeviceAccessTokenResponseClient() {
43+
// @formatter:off
4344
this.restClient = RestClient.builder()
4445
.messageConverters((messageConverters) -> {
4546
messageConverters.clear();
@@ -48,6 +49,7 @@ public OAuth2DeviceAccessTokenResponseClient() {
4849
})
4950
.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
5051
.build();
52+
// @formatter:on
5153
}
5254

5355
public void setRestClient(RestClient restClient) {

samples/demo-client/src/main/java/sample/config/RestClientConfig.java

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,33 @@
2828
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
2929
import org.apache.hc.core5.http.config.Registry;
3030
import org.apache.hc.core5.http.config.RegistryBuilder;
31+
import sample.authorization.DeviceCodeOAuth2AuthorizedClientProvider;
3132

33+
import org.springframework.beans.factory.annotation.Qualifier;
3234
import org.springframework.boot.ssl.SslBundle;
3335
import org.springframework.boot.ssl.SslBundles;
3436
import org.springframework.context.annotation.Bean;
3537
import org.springframework.context.annotation.Configuration;
3638
import org.springframework.http.client.ClientHttpRequestFactory;
3739
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
40+
import org.springframework.http.converter.FormHttpMessageConverter;
41+
import org.springframework.security.oauth2.client.OAuth2AuthorizationFailureHandler;
42+
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
43+
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
44+
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
45+
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
46+
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;
47+
import org.springframework.security.oauth2.client.endpoint.RestClientClientCredentialsTokenResponseClient;
48+
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
49+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
50+
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
51+
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
52+
import org.springframework.security.oauth2.client.web.client.OAuth2ClientHttpRequestInterceptor;
53+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
54+
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
55+
import org.springframework.util.LinkedMultiValueMap;
56+
import org.springframework.util.MultiValueMap;
57+
import org.springframework.web.client.RestClient;
3858

3959
/**
4060
* @author Joe Grandja
@@ -43,6 +63,91 @@
4363
@Configuration(proxyBeanMethods = false)
4464
public class RestClientConfig {
4565

66+
@Bean("default-client-rest-client")
67+
public RestClient defaultClientRestClient(
68+
OAuth2AuthorizedClientRepository authorizedClientRepository,
69+
OAuth2AuthorizedClientManager authorizedClientManager,
70+
@Qualifier("default-client-http-request-factory") Supplier<ClientHttpRequestFactory> clientHttpRequestFactory) {
71+
72+
OAuth2ClientHttpRequestInterceptor requestInterceptor =
73+
new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
74+
OAuth2AuthorizationFailureHandler authorizationFailureHandler =
75+
OAuth2ClientHttpRequestInterceptor.authorizationFailureHandler(authorizedClientRepository);
76+
requestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler);
77+
// @formatter:off
78+
return RestClient.builder()
79+
.requestFactory(clientHttpRequestFactory.get())
80+
.requestInterceptor(requestInterceptor)
81+
.build();
82+
// @formatter:on
83+
}
84+
85+
@Bean("self-signed-demo-client-rest-client")
86+
public RestClient selfSignedDemoClientRestClient(
87+
ClientRegistrationRepository clientRegistrationRepository,
88+
OAuth2AuthorizedClientRepository authorizedClientRepository,
89+
@Qualifier("self-signed-demo-client-http-request-factory") Supplier<ClientHttpRequestFactory> clientHttpRequestFactory) {
90+
91+
RestClient restClient = accessTokenRestClient(clientHttpRequestFactory);
92+
93+
// @formatter:off
94+
OAuth2AuthorizedClientProvider authorizedClientProvider =
95+
OAuth2AuthorizedClientProviderBuilder.builder()
96+
.clientCredentials(clientCredentials ->
97+
clientCredentials.accessTokenResponseClient(
98+
createClientCredentialsTokenResponseClient(restClient)))
99+
.build();
100+
// @formatter:on
101+
102+
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
103+
clientRegistrationRepository, authorizedClientRepository);
104+
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
105+
106+
OAuth2ClientHttpRequestInterceptor requestInterceptor =
107+
new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
108+
OAuth2AuthorizationFailureHandler authorizationFailureHandler =
109+
OAuth2ClientHttpRequestInterceptor.authorizationFailureHandler(authorizedClientRepository);
110+
requestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler);
111+
112+
// @formatter:off
113+
return RestClient.builder()
114+
.requestFactory(clientHttpRequestFactory.get())
115+
.requestInterceptor(requestInterceptor)
116+
.build();
117+
// @formatter:on
118+
}
119+
120+
@Bean
121+
public OAuth2AuthorizedClientManager authorizedClientManager(
122+
ClientRegistrationRepository clientRegistrationRepository,
123+
OAuth2AuthorizedClientRepository authorizedClientRepository,
124+
@Qualifier("default-client-http-request-factory") Supplier<ClientHttpRequestFactory> clientHttpRequestFactory) {
125+
126+
RestClient restClient = accessTokenRestClient(clientHttpRequestFactory);
127+
128+
// @formatter:off
129+
OAuth2AuthorizedClientProvider authorizedClientProvider =
130+
OAuth2AuthorizedClientProviderBuilder.builder()
131+
.authorizationCode()
132+
.refreshToken()
133+
.clientCredentials(clientCredentials ->
134+
clientCredentials.accessTokenResponseClient(
135+
createClientCredentialsTokenResponseClient(restClient)))
136+
.provider(new DeviceCodeOAuth2AuthorizedClientProvider())
137+
.build();
138+
// @formatter:on
139+
140+
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
141+
clientRegistrationRepository, authorizedClientRepository);
142+
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
143+
144+
// Set a contextAttributesMapper to obtain device_code from the request
145+
authorizedClientManager.setContextAttributesMapper(DeviceCodeOAuth2AuthorizedClientProvider
146+
.deviceCodeContextAttributesMapper());
147+
148+
return authorizedClientManager;
149+
}
150+
46151
@Bean("default-client-http-request-factory")
47152
Supplier<ClientHttpRequestFactory> defaultClientHttpRequestFactory(SslBundles sslBundles) {
48153
return () -> {
@@ -82,4 +187,34 @@ Supplier<ClientHttpRequestFactory> selfSignedDemoClientHttpRequestFactory(SslBun
82187
};
83188
}
84189

190+
private static RestClient accessTokenRestClient(Supplier<ClientHttpRequestFactory> clientHttpRequestFactory) {
191+
// @formatter:off
192+
return RestClient.builder()
193+
.requestFactory(clientHttpRequestFactory.get())
194+
.messageConverters((messageConverters) -> {
195+
messageConverters.clear();
196+
messageConverters.add(new FormHttpMessageConverter());
197+
messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
198+
})
199+
.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
200+
.build();
201+
// @formatter:on
202+
203+
}
204+
205+
private static OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> createClientCredentialsTokenResponseClient(
206+
RestClient restClient) {
207+
RestClientClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient =
208+
new RestClientClientCredentialsTokenResponseClient();
209+
clientCredentialsTokenResponseClient.addParametersConverter(authorizationGrantRequest -> {
210+
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
211+
// client_id parameter is required for tls_client_auth method
212+
parameters.add(OAuth2ParameterNames.CLIENT_ID, authorizationGrantRequest.getClientRegistration().getClientId());
213+
return parameters;
214+
});
215+
clientCredentialsTokenResponseClient.setRestClient(restClient);
216+
217+
return clientCredentialsTokenResponseClient;
218+
}
219+
85220
}

samples/demo-client/src/main/java/sample/config/SecurityConfig.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2024 the original author or authors.
2+
* Copyright 2020-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@
1919
import org.springframework.context.annotation.Configuration;
2020
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
2121
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
22-
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
2322
import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler;
2423
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
2524
import org.springframework.security.web.SecurityFilterChain;
@@ -37,19 +36,14 @@
3736
@Configuration(proxyBeanMethods = false)
3837
public class SecurityConfig {
3938

40-
@Bean
41-
public WebSecurityCustomizer webSecurityCustomizer() {
42-
return (web) -> web.ignoring().requestMatchers("/webjars/**", "/assets/**");
43-
}
44-
4539
// @formatter:off
4640
@Bean
4741
public SecurityFilterChain securityFilterChain(HttpSecurity http,
4842
ClientRegistrationRepository clientRegistrationRepository) throws Exception {
4943
http
5044
.authorizeHttpRequests(authorize ->
5145
authorize
52-
.requestMatchers("/jwks", "/logged-out").permitAll()
46+
.requestMatchers("/webjars/**", "/assets/**", "/jwks", "/logged-out").permitAll()
5347
.anyRequest().authenticated()
5448
)
5549
.oauth2Login(oauth2Login ->

0 commit comments

Comments
 (0)