Add Support for Custom Default Configuration in Web Security
Fixes gh-4102
This commit is contained in:
parent
af9139b613
commit
94e580fe64
|
@ -34,6 +34,7 @@ import org.springframework.beans.factory.BeanFactoryUtils;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||
|
@ -44,6 +45,7 @@ import org.springframework.security.config.annotation.authentication.configurati
|
|||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
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.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
@ -59,8 +61,23 @@ import org.springframework.web.accept.ContentNegotiationStrategy;
|
|||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
|
||||
/**
|
||||
* Provides a convenient base class for creating a {@link WebSecurityConfigurer} instance.
|
||||
* The implementation allows customization by overriding methods.
|
||||
* Provides a convenient base class for creating a {@link WebSecurityConfigurer}
|
||||
* instance. The implementation allows customization by overriding methods.
|
||||
*
|
||||
* <p>
|
||||
* Will automatically apply the result of looking up
|
||||
* {@link AbstractHttpConfigurer} from {@link SpringFactoriesLoader} to allow
|
||||
* developers to extend the defaults.
|
||||
* To do this, you must create a class that extends AbstractHttpConfigurer and then create a file in the classpath at "META-INF/spring.factories" that looks something like:
|
||||
* </p>
|
||||
* <pre>
|
||||
* org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer
|
||||
* </pre>
|
||||
* If you have multiple classes that should be added you can use "," to separate the values. For example:
|
||||
*
|
||||
* <pre>
|
||||
* org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer, sample.OtherThatExtendsAbstractHttpConfigurer
|
||||
* </pre>
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
*
|
||||
|
@ -165,6 +182,7 @@ public abstract class WebSecurityConfigurerAdapter implements
|
|||
* ] * @return the {@link HttpSecurity}
|
||||
* @throws Exception
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
protected final HttpSecurity getHttp() throws Exception {
|
||||
if (http != null) {
|
||||
return http;
|
||||
|
@ -195,6 +213,13 @@ public abstract class WebSecurityConfigurerAdapter implements
|
|||
.apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()
|
||||
.logout();
|
||||
// @formatter:on
|
||||
ClassLoader classLoader = this.context.getClassLoader();
|
||||
List<AbstractHttpConfigurer> defaultHttpConfigurers =
|
||||
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
|
||||
|
||||
for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
|
||||
http.apply(configurer);
|
||||
}
|
||||
}
|
||||
configure(http);
|
||||
return http;
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
|
|||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
|
||||
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
|
||||
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2002-2016 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 org.springframework.security.config.annotation.web;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.powermock.api.mockito.PowerMockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
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;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({ SpringFactoriesLoader.class })
|
||||
public class WebSecurityConfigurerAdapterPowermockTests {
|
||||
ConfigurableWebApplicationContext context;
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if(context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenDefaultConfigurerAsSpringFactoryhenDefaultConfigurerApplied() {
|
||||
spy(SpringFactoriesLoader.class);
|
||||
DefaultConfigurer configurer = new DefaultConfigurer();
|
||||
when(SpringFactoriesLoader
|
||||
.loadFactories(AbstractHttpConfigurer.class, getClass().getClassLoader()))
|
||||
.thenReturn(Arrays.<AbstractHttpConfigurer>asList(configurer));
|
||||
|
||||
loadConfig(Config.class);
|
||||
|
||||
assertThat(configurer.init).isTrue();
|
||||
assertThat(configurer.configure).isTrue();
|
||||
}
|
||||
|
||||
private void loadConfig(Class<?>... classes) {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.setClassLoader(getClass().getClassLoader());
|
||||
context.register(classes);
|
||||
context.refresh();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class Config extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
static class DefaultConfigurer extends AbstractHttpConfigurer<DefaultConfigurer,HttpSecurity> {
|
||||
boolean init;
|
||||
boolean configure;
|
||||
|
||||
@Override
|
||||
public void init(HttpSecurity builder) throws Exception {
|
||||
this.init = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity builder) throws Exception {
|
||||
this.configure = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1101,6 +1101,93 @@ protected void configure(HttpSecurity http) throws Exception {
|
|||
}
|
||||
----
|
||||
|
||||
[[jc-custom-dsls]]
|
||||
=== Custom DSLs
|
||||
|
||||
You can provide your own custom DSLs in Spring Security.
|
||||
For example, you might have something that looks like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public class MyCustomDsl extends AbstractHttpConfigurer<CorsConfigurerMyCustomDsl, HttpSecurity> {
|
||||
private boolean flag;
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
// any method that adds another configurer
|
||||
// must be done in the init method
|
||||
http.csrf().disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
|
||||
|
||||
// here we lookup from the ApplicationContext. You can also just create a new instance.
|
||||
MyFilter myFilter = context.getBean(MyFilter.class);
|
||||
myFilter.setFlag(flag);
|
||||
http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
public MyCustomDsl flag(boolean value) {
|
||||
this.flag = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static MyCustomDsl customDsl() {
|
||||
return new MyCustomDsl();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: This is actually how methods like `HttpSecurity.authorizeRequests()` are implemented.
|
||||
|
||||
The custom DSL can then be used like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@EnableWebSecurity
|
||||
public class Config extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.apply(customDsl())
|
||||
.flag(true)
|
||||
.and()
|
||||
...;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The code is invoked in the following order:
|
||||
|
||||
* Code in `Config`s configure method is invoked
|
||||
* Code in `MyCustomDsl`s init method is invoked
|
||||
* Code in `MyCustomDsl`s configure method is invoked
|
||||
|
||||
If you want, you can have `WebSecurityConfiguerAdapter` add `MyCustomDsl` by default by using `SpringFactories`.
|
||||
For example, you would create a resource on the classpath named `META-INF/spring.factories` with the following contents:
|
||||
|
||||
.META-INF/spring.factories
|
||||
----
|
||||
org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl
|
||||
----
|
||||
|
||||
Users wishing to disable the default can do so explicitly.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@EnableWebSecurity
|
||||
public class Config extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.apply(customDsl()).disable()
|
||||
...;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[[ns-config]]
|
||||
== Security Namespace Configuration
|
||||
|
||||
|
|
Loading…
Reference in New Issue