diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java index 6825db2..13b26b9 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java @@ -17,21 +17,45 @@ package org.springframework.security.config.annotation.web.configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.WebSecurityConfigurer; -import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.OrRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; /** * {@link Configuration} for OAuth 2.0 Authorization Server support. * * @author Joe Grandja * @since 0.0.1 + * @see OAuth2AuthorizationServerConfigurer */ @Configuration(proxyBeanMethods = false) public class OAuth2AuthorizationServerConfiguration { @Bean - public WebSecurityConfigurer defaultOAuth2AuthorizationServerSecurity() { - return new OAuth2AuthorizationServerSecurity(); + @Order(Ordered.HIGHEST_PRECEDENCE) + public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { + applyDefaultSecurity(http); + return http.build(); } + // @formatter:off + public static void applyDefaultSecurity(HttpSecurity http) throws Exception { + OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = + new OAuth2AuthorizationServerConfigurer<>(); + RequestMatcher[] endpointMatchers = authorizationServerConfigurer + .getEndpointMatchers().toArray(new RequestMatcher[0]); + + http + .requestMatcher(new OrRequestMatcher(endpointMatchers)) + .authorizeRequests(authorizeRequests -> + authorizeRequests.anyRequest().authenticated() + ) + .csrf(csrf -> csrf.ignoringRequestMatchers(endpointMatchers)) + .apply(authorizationServerConfigurer); + } + // @formatter:on } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerSecurity.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerSecurity.java deleted file mode 100644 index 957bd19..0000000 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerSecurity.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 org.springframework.security.config.annotation.web.configuration; - -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer; -import org.springframework.security.web.util.matcher.OrRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; - -import static org.springframework.security.config.Customizer.withDefaults; - -/** - * {@link WebSecurityConfigurerAdapter} providing default security configuration for OAuth 2.0 Authorization Server. - * - * @author Joe Grandja - * @since 0.0.1 - */ -@Order(Ordered.HIGHEST_PRECEDENCE) -public class OAuth2AuthorizationServerSecurity extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - applyDefaultConfiguration(http); - } - - // @formatter:off - public static void applyDefaultConfiguration(HttpSecurity http) throws Exception { - OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = - new OAuth2AuthorizationServerConfigurer<>(); - RequestMatcher[] endpointMatchers = authorizationServerConfigurer - .getEndpointMatchers().toArray(new RequestMatcher[0]); - - http - .requestMatcher(new OrRequestMatcher(endpointMatchers)) - .authorizeRequests(authorizeRequests -> - authorizeRequests.anyRequest().authenticated() - ) - .formLogin(withDefaults()) - .csrf(csrf -> csrf.ignoringRequestMatchers(endpointMatchers)) - .apply(authorizationServerConfigurer); - } - // @formatter:on -} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java index 6f338b3..23dbef9 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java @@ -39,9 +39,13 @@ import org.springframework.security.oauth2.server.authorization.web.OAuth2Author import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter; import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter; +import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint; import org.springframework.security.web.authentication.HttpStatusEntryPoint; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; +import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -49,6 +53,7 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -163,10 +168,19 @@ public final class OAuth2AuthorizationServerConfigurer exceptionHandling = builder.getConfigurer(ExceptionHandlingConfigurer.class); if (exceptionHandling != null) { - // Register the default AuthenticationEntryPoint for the token endpoint and token revocation endpoint - exceptionHandling.defaultAuthenticationEntryPointFor( - new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), - new OrRequestMatcher(this.tokenEndpointMatcher, this.tokenRevocationEndpointMatcher)); + LinkedHashMap entryPoints = new LinkedHashMap<>(); + entryPoints.put( + new OrRequestMatcher(this.tokenEndpointMatcher, this.tokenRevocationEndpointMatcher), + new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); + DelegatingAuthenticationEntryPoint authenticationEntryPoint = + new DelegatingAuthenticationEntryPoint(entryPoints); + + // TODO This needs to change as the login page could be customized with a different URL + authenticationEntryPoint.setDefaultEntryPoint( + new LoginUrlAuthenticationEntryPoint( + DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL)); + + exceptionHandling.authenticationEntryPoint(authenticationEntryPoint); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerSecurityTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfigurationTests.java similarity index 60% rename from oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerSecurityTests.java rename to oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfigurationTests.java index 49e53d9..b2e1ac5 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerSecurityTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfigurationTests.java @@ -18,22 +18,29 @@ package org.springframework.security.config.annotation.web.configuration; import org.junit.Test; import org.springframework.core.Ordered; import org.springframework.core.annotation.OrderUtils; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.util.ClassUtils; + +import java.lang.reflect.Method; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link OAuth2AuthorizationServerSecurity}. + * Tests for {@link OAuth2AuthorizationServerConfiguration}. * * @author Joe Grandja */ -public class OAuth2AuthorizationServerSecurityTests { +public class OAuth2AuthorizationServerConfigurationTests { @Test public void assertOrderHighestPrecedence() { - Integer authorizationServerSecurityOrder = OrderUtils.getOrder(OAuth2AuthorizationServerSecurity.class); - Integer defaultSecurityOrder = OrderUtils.getOrder(WebSecurityConfigurerAdapter.class); - assertThat(authorizationServerSecurityOrder).isNotEqualTo(defaultSecurityOrder); - assertThat(authorizationServerSecurityOrder).isEqualTo(Ordered.HIGHEST_PRECEDENCE); + Method authorizationServerSecurityFilterChainMethod = + ClassUtils.getMethod( + OAuth2AuthorizationServerConfiguration.class, + "authorizationServerSecurityFilterChain", + HttpSecurity.class); + Integer order = OrderUtils.getOrder(authorizationServerSecurityFilterChainMethod); + assertThat(order).isEqualTo(Ordered.HIGHEST_PRECEDENCE); } } diff --git a/samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java b/samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java index 9c232ea..cf60661 100644 --- a/samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java +++ b/samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java @@ -16,12 +16,9 @@ package sample.config; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; 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.key.CryptoKeySource; import org.springframework.security.crypto.key.StaticKeyGeneratingCryptoKeySource; import org.springframework.security.oauth2.core.AuthorizationGrantType; @@ -29,7 +26,6 @@ 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; @@ -37,7 +33,7 @@ import java.util.UUID; * @author Joe Grandja * @since 0.0.1 */ -@EnableWebSecurity +@Configuration(proxyBeanMethods = false) @Import(OAuth2AuthorizationServerConfiguration.class) public class AuthorizationServerConfig { @@ -63,16 +59,4 @@ public class AuthorizationServerConfig { public CryptoKeySource keySource() { return new StaticKeyGeneratingCryptoKeySource(); } - - // @formatter:off - @Bean - public UserDetailsService users() { - UserDetails user = User.withDefaultPasswordEncoder() - .username("user1") - .password("password") - .roles("USER") - .build(); - return new InMemoryUserDetailsManager(user); - } - // @formatter:on } diff --git a/samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/config/DefaultSecurityConfig.java b/samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/config/DefaultSecurityConfig.java new file mode 100644 index 0000000..9d56919 --- /dev/null +++ b/samples/boot/oauth2-integration/authorizationserver/src/main/java/sample/config/DefaultSecurityConfig.java @@ -0,0 +1,60 @@ +/* + * 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.configuration.EnableWebSecurity; +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; +import org.springframework.security.web.SecurityFilterChain; + +import static org.springframework.security.config.Customizer.withDefaults; + +/** + * @author Joe Grandja + * @since 0.1.0 + */ +@EnableWebSecurity +public class DefaultSecurityConfig { + + // formatter:off + @Bean + SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeRequests(authorizeRequests -> + authorizeRequests.anyRequest().authenticated() + ) + .formLogin(withDefaults()); + return http.build(); + } + // formatter:on + + // @formatter:off + @Bean + UserDetailsService users() { + UserDetails user = User.withDefaultPasswordEncoder() + .username("user1") + .password("password") + .roles("USER") + .build(); + return new InMemoryUserDetailsManager(user); + } + // @formatter:on + +}