Register SecurityFilterChain instead of WebSecurityConfigurerAdapter

Closes gh-163
This commit is contained in:
Joe Grandja 2020-11-19 08:29:34 -05:00
parent 9f246fc304
commit d97235d0bb
6 changed files with 121 additions and 90 deletions

View File

@ -17,21 +17,45 @@ package org.springframework.security.config.annotation.web.configuration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer; import org.springframework.core.Ordered;
import org.springframework.security.config.annotation.web.builders.WebSecurity; 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. * {@link Configuration} for OAuth 2.0 Authorization Server support.
* *
* @author Joe Grandja * @author Joe Grandja
* @since 0.0.1 * @since 0.0.1
* @see OAuth2AuthorizationServerConfigurer
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
public class OAuth2AuthorizationServerConfiguration { public class OAuth2AuthorizationServerConfiguration {
@Bean @Bean
public WebSecurityConfigurer<WebSecurity> defaultOAuth2AuthorizationServerSecurity() { @Order(Ordered.HIGHEST_PRECEDENCE)
return new OAuth2AuthorizationServerSecurity(); public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
applyDefaultSecurity(http);
return http.build();
} }
// @formatter:off
public static void applyDefaultSecurity(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer<HttpSecurity> 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
} }

View File

@ -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<HttpSecurity> 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
}

View File

@ -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.OAuth2ClientAuthenticationFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter; 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.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
import org.springframework.security.web.authentication.HttpStatusEntryPoint; 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.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.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
@ -49,6 +53,7 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -163,10 +168,19 @@ public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBui
ExceptionHandlingConfigurer<B> exceptionHandling = builder.getConfigurer(ExceptionHandlingConfigurer.class); ExceptionHandlingConfigurer<B> exceptionHandling = builder.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptionHandling != null) { if (exceptionHandling != null) {
// Register the default AuthenticationEntryPoint for the token endpoint and token revocation endpoint LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
exceptionHandling.defaultAuthenticationEntryPointFor( entryPoints.put(
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), new OrRequestMatcher(this.tokenEndpointMatcher, this.tokenRevocationEndpointMatcher),
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);
} }
} }

View File

@ -18,22 +18,29 @@ package org.springframework.security.config.annotation.web.configuration;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.OrderUtils; 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; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link OAuth2AuthorizationServerSecurity}. * Tests for {@link OAuth2AuthorizationServerConfiguration}.
* *
* @author Joe Grandja * @author Joe Grandja
*/ */
public class OAuth2AuthorizationServerSecurityTests { public class OAuth2AuthorizationServerConfigurationTests {
@Test @Test
public void assertOrderHighestPrecedence() { public void assertOrderHighestPrecedence() {
Integer authorizationServerSecurityOrder = OrderUtils.getOrder(OAuth2AuthorizationServerSecurity.class); Method authorizationServerSecurityFilterChainMethod =
Integer defaultSecurityOrder = OrderUtils.getOrder(WebSecurityConfigurerAdapter.class); ClassUtils.getMethod(
assertThat(authorizationServerSecurityOrder).isNotEqualTo(defaultSecurityOrder); OAuth2AuthorizationServerConfiguration.class,
assertThat(authorizationServerSecurityOrder).isEqualTo(Ordered.HIGHEST_PRECEDENCE); "authorizationServerSecurityFilterChain",
HttpSecurity.class);
Integer order = OrderUtils.getOrder(authorizationServerSecurityFilterChainMethod);
assertThat(order).isEqualTo(Ordered.HIGHEST_PRECEDENCE);
} }
} }

View File

@ -16,12 +16,9 @@
package sample.config; package sample.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; 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.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.CryptoKeySource;
import org.springframework.security.crypto.key.StaticKeyGeneratingCryptoKeySource; import org.springframework.security.crypto.key.StaticKeyGeneratingCryptoKeySource;
import org.springframework.security.oauth2.core.AuthorizationGrantType; 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.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import java.util.UUID; import java.util.UUID;
@ -37,7 +33,7 @@ import java.util.UUID;
* @author Joe Grandja * @author Joe Grandja
* @since 0.0.1 * @since 0.0.1
*/ */
@EnableWebSecurity @Configuration(proxyBeanMethods = false)
@Import(OAuth2AuthorizationServerConfiguration.class) @Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig { public class AuthorizationServerConfig {
@ -63,16 +59,4 @@ public class AuthorizationServerConfig {
public CryptoKeySource keySource() { public CryptoKeySource keySource() {
return new StaticKeyGeneratingCryptoKeySource(); 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
} }

View File

@ -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
}