General support for Java Configuration was added to Spring Framework in Spring 3.1. Since Spring Security 3.2 there has been Spring Security Java Configuration support which enables users to easily configure Spring Security without the use of any XML.
If you are familiar with the ??? then you should find quite a few similarities between it and the Security Java Configuration support.
Note | |
---|---|
Spring Security provides lots of sample applications which demonstrate the use of Spring Security Java Configuration. |
Thus far our WebSecurityConfig only contains information about how to authenticate our users.
How does Spring Security know that we want to require all users to be authenticated? How does Spring Security know we want to support form based authentication? The reason for this is that the WebSecurityConfigurerAdapter
provides a default configuration in the configure(HttpSecurity http)
method that looks like:
protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .httpBasic(); }
The default configuration above:
You will notice that this configuration is quite similar the XML Namespace configuration:
<http> <intercept-url pattern="/**" access="authenticated"/> <form-login /> <http-basic /> </http>
The Java Configuration equivalent of closing an XML tag is expressed using the and()
method which allows us to continue configuring the parent.
If you read the code it also makes sense.
I want to configure authorized requests and configure form login and configure HTTP Basic authentication.
We can configure multiple HttpSecurity instances just as we can have multiple <http>
blocks.
The key is to extend the WebSecurityConfigurationAdapter
multiple times.
For example, the following is an example of having a different configuration for URL’s that start with /api/
.
@EnableWebSecurity public class MultiHttpSecurityConfig { @Bean public UserDetailsService userDetailsService() throws Exception { // ensure the passwords are encoded properly UserBuilder users = User.withDefaultPasswordEncoder(); InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(users.username("user").password("password").roles("USER").build()); manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build()); return manager; } @Configuration @Order(1) public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { http .antMatcher("/api/**") .authorizeRequests() .anyRequest().hasRole("ADMIN") .and() .httpBasic(); } } @Configuration public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin(); } } }
Configure Authentication as normal | |
Create an instance of | |
The | |
Create another instance of |
You can provide your own custom DSLs in Spring Security. For example, you might have something that looks like this:
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, 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 |
The custom DSL can then be used like this:
@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:
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.
@EnableWebSecurity public class Config extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .apply(customDsl()).disable() ...; } }
Spring Security’s Java Configuration does not expose every property of every object that it configures. This simplifies the configuration for a majority of users. Afterall, if every property was exposed, users could use standard bean configuration.
While there are good reasons to not directly expose every property, users may still need more advanced configuration options.
To address this Spring Security introduces the concept of an ObjectPostProcessor
which can be used to modify or replace many of the Object instances created by the Java Configuration.
For example, if you wanted to configure the filterSecurityPublishAuthorizationSuccess
property on FilterSecurityInterceptor
you could use the following:
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { public <O extends FilterSecurityInterceptor> O postProcess( O fsi) { fsi.setPublishAuthorizationSuccess(true); return fsi; } }); }
If you’ve used Spring Security before, you’ll know that the framework maintains a chain of filters in order to apply its services.
You may want to add your own filters to the stack at particular locations or use a Spring Security filter for which there isn’t currently a namespace configuration option (CAS, for example).
Or you might want to use a customized version of a standard namespace filter, such as the UsernamePasswordAuthenticationFilter
which is created by the <form-login>
element, taking advantage of some of the extra configuration options which are available by using the bean explicitly.
How can you do this with namespace configuration, since the filter chain is not directly exposed?
The order of the filters is always strictly enforced when using the namespace. When the application context is being created, the filter beans are sorted by the namespace handling code and the standard Spring Security filters each have an alias in the namespace and a well-known position.
Note | |
---|---|
===
In previous versions, the sorting took place after the filter instances had been created, during post-processing of the application context.
In version 3.0+ the sorting is now done at the bean metadata level, before the classes have been instantiated.
This has implications for how you add your own filters to the stack as the entire filter list must be known during the parsing of the |
The filters, aliases and namespace elements/attributes which create the filters are shown in Table 14.1, “Standard Filter Aliases and Ordering”. The filters are listed in the order in which they occur in the filter chain.
Table 14.1. Standard Filter Aliases and Ordering
Alias | Filter Class | Namespace Element or Attribute |
---|---|---|
CHANNEL_FILTER |
|
|
SECURITY_CONTEXT_FILTER |
|
|
CONCURRENT_SESSION_FILTER |
|
|
HEADERS_FILTER |
|
|
CSRF_FILTER |
|
|
LOGOUT_FILTER |
|
|
X509_FILTER |
|
|
PRE_AUTH_FILTER |
| N/A |
CAS_FILTER |
| N/A |
FORM_LOGIN_FILTER |
|
|
BASIC_AUTH_FILTER |
|
|
SERVLET_API_SUPPORT_FILTER |
|
|
JAAS_API_SUPPORT_FILTER |
|
|
REMEMBER_ME_FILTER |
|
|
ANONYMOUS_FILTER |
|
|
SESSION_MANAGEMENT_FILTER |
|
|
EXCEPTION_TRANSLATION_FILTER |
|
|
FILTER_SECURITY_INTERCEPTOR |
|
|
SWITCH_USER_FILTER |
| N/A |
You can add your own filter to the stack, using the custom-filter
element and one of these names to specify the position your filter should appear at:
<http> <custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" /> </http> <beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter"/>
You can also use the after
or before
attributes if you want your filter to be inserted before or after another filter in the stack.
The names "FIRST" and "LAST" can be used with the position
attribute to indicate that you want your filter to appear before or after the entire stack, respectively.
Avoiding filter position conflicts | |
---|---|
If you are inserting a custom filter which may occupy the same position as one of the standard filters created by the namespace then it’s important that you don’t include the namespace versions by mistake. Remove any elements which create filters whose functionality you want to replace. Note that you can’t replace filters which are created by the use of the |
If you’re replacing a namespace filter which requires an authentication entry point (i.e. where the authentication process is triggered by an attempt by an unauthenticated user to access to a secured resource), you will need to add a custom entry point bean too.