How can I use my custom ConfigurationProperties in custom WebAuthenticationDetails? - java

In Spring 4.3.x, I have a custom class, call it MyWebAuthenticationDetails that extends WebAuthenticationDetails. I need to use properties in that class that are defined in application.properties. I get those properties via a custom class, called AuthenticationProperties, that uses #ConfigurationProperties. Normally I would autowire in AuthenticationProperties on the class constructor, but that is not possible for MyWebAuthenticationDetails. How can I access properties from within my extension of WebAuthenticationDetails?

Since your MyWebAuthenticationDetails custom details object will be constructed through an AuthenticationDetailsSource bean (which you should have already declared), you can access the AuthenticationProperties as an injected bean thus you will have all your properties accessible.
A simple Java configuration template would be as follows (note that this is not a full functional configuration and aims only to highlight the important configuration entries):
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authenticationDetailsSource(myAuthenticationDetailsSource())/* and all the missiong HTTP configuration*/;
}
#Bean
private AuthenticationDetailsSource<HttpServletRequest, MyWebAuthenticationDetails> myAuthenticationDetailsSource() {
return new MyAuthenticationDetailsSource<HttpServletRequest, MyWebAuthenticationDetails>();
}
private final class MyAuthenticationDetailsSource extends AuthenticationDetailsSourceImpl<HttpServletRequest, MyWebAuthenticationDetails> {
#Autowired
private AuthenticationProperties authenticationProperties;
#Override
public MyWebAuthenticationDetails buildDetails(HttpServletRequest request) {
return new MyWebAuthenticationDetails(request, this.authenticationProperties);
}
}
}

Related

Spring: Mixing of wiring in JavaConfig with wiring using #Autowired

I am totally confused about mixing of "wiring in JavaConfig" and "wiring using #Autowired". I will tell you my problems in 4 scenarios:
(I am ok with mixing of #Autowired and stereotype annotations and I don't have any question about that. my problem is Javaconfig and #autowired)
Scenario 1:
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer() {
cd = new CompactDisc() {
#Override
public void play() {
System.out.println("123456");
}
};
}
#Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer();
}
}
For Example in this scenario, I see that #Autowired is effectless and cannot make Spring to invoke and use the parameterized constructor and no-arg constructor will be executed (because it is invoked in the #Bean method) and the output is the text "123456".
=================================================================
SCENARIO 2:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
we wired those two beans in the config file. and I know that we do not need #Autowired at all.
=================================================================
SCENARIO 3:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean()
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer() {
return new CDPlayer();
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
#Autowired
public void setCd(CompactDisc cd) {
this.cd = cd;
}
}
I know that if #Autowired is above of parameterized constructor, that constructor will not be executed but now that is above of setCd(), this method will be executed.
=================================================================
SCENARIO 4:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer() {
}
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
#Autowired
public void doSomething(CompactDisc cd) {
this.cd = new CompactDisc() {
#Override
public void play() {
System.out.println("AAAAA");
}
};
}
}
and in this scenario, Although that we wired those two beans together, but #Autowired makes spring to execute the doSomething()method.
What is happening?! I can't see the Big Picture. I can't understand the pattern that is going on.
sometimes #Autowired works and sometimes doesn't work. what is the general pattern? do we need #Autowired at all when we wire beans together in JavaConfig file?
An autowired constructor is invoked if spring invokes the constructor by reflection, typically because you declare the bean using component scanning or XML config. If you manually invoke a constructor in a #Bean method, that constructor executes, and #Autowired has no effect.
An autowired method is invoked after the bean has been created, irrespective of how the bean was created.
The reason is that, in Java, each constructor call creates a new object, making it impossible to call two constructors for the same object. That's why Spring can't call a second constructor if you have already called a different one. In contrast, it is possible to call many methods on the same object, so Spring does support method autowiring just fine.
To conclude, you can use autowiring with JavaConfig, but you should autowire fields or methods rather than constructors. Or you can do without autowiring, and pass everything explicitly in your #Bean method. Or any mixture of the two.
The Spring Context contains all the beans you need in your program, and Spring do the rest of the job for you. But something to understand is that your beans comes from many parts of your application :
Internal beans (POJO from your domain).
External beans (POJO from other libraries or third partie classes).
Reading this from the spring documentation, you can find all the differents sources of beans :
#SpringBootApplication is a convenience annotation that adds all of
the following:
#Configuration: Tags the class as a source of bean definitions for the
application context.
#EnableAutoConfiguration: Tells Spring Boot to start adding beans
based on classpath settings, other beans, and various property
settings. For example, if spring-webmvc is on the classpath, this
annotation flags the application as a web application and activates
key behaviors, such as setting up a DispatcherServlet.
#ComponentScan: Tells Spring to look for other components,
configurations, and services in the com/example package, letting it
find the controllers.
Follow these rules :
In your domain classes (Controller, Service) : use #Autowired in your constructor. It is the recommanded way to inject your dependencies.
You want to use external classes : implements a Java Configuration with #Configuration annotation, to instanciate your external classes as beans.
You want to create custom utilities classes : decorate it with #Component.
When you have more than on implementation, use #Qualifier and define your beans in a #Configuration class.

SimpUserRegistry doesnot contain any session objects

Iam new to Websockets. I have been trying to use SimpUserRegistry to find session object by Principal. I wrote a custom handshake handler to convert Anonymous users to authenticated users and Iam able to access the Principal name from Websocket session object.
The code for custom handshake handler is shown below
import java.security.Principal;
public class StompPrincipal implements Principal {
private String name;
public StompPrincipal(String name) {
this.name = name;
}
#Override
public String getName() {
return name;
}
}
Handler
class CustomHandshakeHandlerTwo extends DefaultHandshakeHandler {
// Custom class for storing principal
#Override
protected Principal determineUser(
ServerHttpRequest request,
WebSocketHandler wsHandler,
Map<String, Object> attributes
) {
// Generate principal with UUID as name
return new StompPrincipal(UUID.randomUUID().toString());
}
}
But as specified in many questions like this I'am not able to inject the SimpUserRegistry directly.
It throws error
Field simpUserRegistry required a bean of type 'org.springframework.messaging.simp.user.SimpUserRegistry' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.messaging.simp.user.SimpUserRegistry' in your configuration.
So I created a configuration class as shown below.
#Configuration
public class UsersConfig {
final private SimpUserRegistry userRegistry = new DefaultSimpUserRegistry();
#Bean
#Primary
public SimpUserRegistry userRegistry() {
return userRegistry;
}
}
Now I can autowire and use it but everytime I try to acess the SimpUserRegistry it is empty.
What could be the cause of this problem?
EDIT:
Showing websocket config
#Configuration
#EnableWebSocket
#Controller
#Slf4j
public class WebSocketConfig implements WebSocketConfigurer {
#Autowired
EventTextHandler2 handler;
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
log.info("Registering websocket handler SocketTextHandler");
registry.addHandler(handler, "/event").setHandshakeHandler(new CustomHandshakeHandlerTwo());
}
}
SimpUserRegistry is an "infrastructure bean" registered/provided by Spring WebSocket, you should not instantiate it directly.
Is your WebSocket Spring configuration correct?
Make sure your application is well configured (ie. your configuration class is being scanned).
SimpUserRegistry is imported by spring-messaging dependency: make sure your configuration class is annotated with #EnableWebSocketMessageBroker.
Official documentation: https://docs.spring.io/spring-framework/docs/5.3.6/reference/html/web.html#websocket-stomp-enable
To back the connected users in Redis, you may want to create a new SimpUserRegistry implementation:
public class RedisSimpUserRegistry implements SimpUserRegistry, SmartApplicationListener {
private final RedisTemplate redisTemplate;
public RedisSimpUserRegistry(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
[...]
#Override
public void onApplicationEvent(ApplicationEvent event) {
// Maintain Redis collection on event type
// ie. SessionConnectedEvent / SessionDisconnectEvent
}
[...]
}
PS: The #Controller annotation on your config class is not necessary unless you have an endpoint defined in it.
Edit after new comments:
You can see the DefaultSimpUserRegistry implementation to get an idea of how to do it.
To intercept an application event, you have to implement the ApplicationListener interface (in this case SmartApplicationListener).
The supportsEventType method is important to define which event types you want to intercept:
#Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return AbstractSubProtocolEvent.class.isAssignableFrom(eventType);
}
The AbstractSubProtocolEvent have multiple implementations. The most important ones are SessionConnectEvent, SessionDisconnectEvent.
Intercepting (see onApplicationEvent method) these event types will allow your implementation to maintain the desired state in your Redis cache. You could then store users (ids, etc.).

Is it possible to turn on and off #Secured annotation [duplicate]

Is there a way I can disable the global method security using the boolean securityEnabled from my config.properties? Any other approach?
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled=true)
#PropertySource("classpath:config.properties")
public class SecurityConfig
extends WebSecurityConfigurerAdapter {
#Value("${securityconfig.enabled}")
private boolean securityEnabled;
...
}
The easiest way to do this is:
Extract method security to its own class
Remove the securedEnabled attribute entirely
Override the customMethodSecurityMetadataSource method and return the result based on the configured value.
For example:
#EnableWebSecurity
#Configuration
#PropertySource("classpath:config.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
#EnableGlobalMethodSecurity
#Configuration
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Value("${securityconfig.enabled}")
private boolean securityEnabled;
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return securityEnabled ? new SecuredAnnotationSecurityMetadataSource() : null;
}
}
I've managed this by defining a Spring "securityDisabled" profile and conditionally applying security config based off that. I'm using Spring Boot 2.0.2. I believe this should work if not using Spring Boot and in previous versions of Spring Boot, but I have not tested. It's possible some tweaks may be required to property and class names because I know in Spring 2.0 some of that changed.
// In application.properties
spring.profiles.include=securityDisabled
Then my security config looks like this:
#Configuration
public class SecurityConfig {
// When the securityDisabled profile is applied the following configuration gets used
#Profile("securityDisabled")
#EnableWebSecurity
public class SecurityDisabledConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Configure http as needed from Spring Security defaults when
// NO security is desired
}
}
// When the securityDisabled profile is NOT applied the following configuration gets used
#Profile("!securityDisabled")
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityEnabledConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Configure http as needed from Spring Security defaults when
// security is desired
}
}
}
In Springboot2, a simple solution consists in replacing the security method interceptor by a dummy one when the security is off :
#EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Value("${disableSecurity}")
private boolean disableSecurity;
public MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {
return disableSecurity ? new SimpleTraceInterceptor()
: super.methodSecurityInterceptor(methodSecurityMetadataSource);
}
}
Thanks to Rob Winch for the solution. For folks who would like to do something similar but with prePostEnabled i have tried and tested the below similar approach and works just fine.
#EnableGlobalMethodSecurity(securedEnabled = true)
#Configuration
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Value("${security.prePostEnabled}")
private boolean prePostEnabled;
#Autowired
private DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler;
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return prePostEnabled ? new PrePostAnnotationSecurityMetadataSource(new ExpressionBasedAnnotationAttributeFactory(defaultMethodSecurityExpressionHandler)) : null ;
}}
EDIT: In addition to above i realized it is required to add following beans to the class. The below will help using the expression based pre invocation checks along with avoiding "ROLE_" prefix that is defaulted in all the handlers
protected AccessDecisionManager accessDecisionManager() {
AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(getExpressionHandler());
//This is required in order to allow expression based Voter to allow access
accessDecisionManager.getDecisionVoters()
.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
//Remove the ROLE_ prefix from RoleVoter for #Secured and hasRole checks on methods
accessDecisionManager.getDecisionVoters().stream()
.filter(RoleVoter.class::isInstance)
.map(RoleVoter.class::cast)
.forEach(it -> it.setRolePrefix(""));
return accessDecisionManager;
}
/**
* Allow skip ROLE_ when check permission using #PreAuthorize, like:
* #PreAuthorize("hasAnyRole('USER', 'SYSTEM_ADMIN')")
* Added all the Beans
*/
#Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
defaultMethodSecurityExpressionHandler.setDefaultRolePrefix("");
return defaultMethodSecurityExpressionHandler;
}

spring security java config - multiple authentication manager

I'm working on a java spring mvc application. Here is a part of my SecurityConfig class for set AuthenticationManager:
...
#Autowired
private SecurityDAO securityDAO;
...
#Override
protected void configure(AuthenticationManagerBuilder registry) throws Exception {
registry.userDetailsService(securityDAO).passwordEncoder(new BCryptPasswordEncoder());
}
The SecurityDAO is a class that implements UserDetailsService interface.
Now, I need to have two different implementation of UserDetailsService interface. One for admin users with url /admin/login and one for customer with url /customer/login.
I found some samples that implements multiple authentication manager with spring, but all of them use XML config and I can't find an example with java config.
Here is a sample of xml config. In fact, I want to convert this config into java config.
Extend AbstractAutowiringFactoryBean and add your implementation in that class.
It has two methods,doCreateInstance() to know whose object needs to be instantiated and getObjectType() to know which interface implementation is this.
For example
#Configuration
public class CLass extends AbstractAutowiringFactoryBean<Object> {
public enum Env {
devloper, production
}
Env envType = Env.devloper;
#Override
protected Object doCreateInstance() {
switch (envType) {
case devloper:
return new ClassImpl1();
case production:
rreturn new ClassImpl2();
}
throw new RuntimeException("Unsupported implementation type");
}
#Override
public Class<?> getObjectType() {
return SecurityDAO.class;
}
}

Difference between registerGlobal(), configure(), configureGlobal(),configureGlobalSecurity in Spring security

I have below three code snippets all doing the same thing: creating in-memory authentication. So how it impacts defining it in different method names?
registerGlobal
configure
configureGlobal
configureGlobalSecurity
First one:
public void registerGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER","ADMIN");
}
}
Second one:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
Third one:
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
Fourth:
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("user").roles("USER");
}
UPDATE 1 :
One more thing I would like to add:
configure() method is present in WebSecurityConfigurerAdapter class while others are not present.
UPDATE 2:
I renamed the method in my sample project to below and to my surprise it is working and authenticating the users.
you name it anything and it works
#Autowired
public void anyMethodName(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("user").roles("USER");
}
In fact, you only have 2 different options.
Option 1: using annotations only (it cover your example 1, 3 and 4 - note that you didn't include relevant annotations in your samples)
registerGlobal, configureGlobal, configureGlobalSecurity are exact same way of doing things. You can name the method according your tastes. The only constraints are :
annotate the method with #Autowired
the method MUST be in a class annotated with one of the following : #EnableWebSecurity, #EnableWebMvcSecurity, #EnableGlobalMethodSecurity, or #EnableGlobalAuthentication
(and of course the method have an argument of type AuthenticationManagerBuilder)
(as you can see the name of the method is not important, that is why you found so many different method name when googling for code samples)
Here is an example of how it looks like :
#EnableWebSecurity
public class MyConfiguration {
#Autowired
public void whatever(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
...
}
Option 2: using annotations + method overriding (it cover your example 2)
Overriding configure is a convenient approach in a subclass of WebSecurityConfigurerAdapter (or any #Configuration class implementing WebSecurityConfigurer) but it have the same effect as the other option.
How to choose the correct approach?
It's only a question of taste/programming-style because both approachs have the same effect.
The first option make sense when you want/need to keep your configuration in a single class, but your #Configuration class already extends some other class (and you don't want to implement the whole WebSecurityConfigurer interface).
Let's explain my last point in more details. Spring provides many Adapter classes that you can extends to speed up the development of your Spring configuration.
As an example, let's take a commonly used Adapter : WebMvcConfigurerAdapter. You will start with a very simple configuration like this :
#EnableWebMvc
#Configuration
#ComponentScan({ "com.company.mypackage" })
public class SpringWebConfig extends WebMvcConfigurerAdapter {
}
What's important here : your class already extends an Adapter class, so you can't extends another one
Now, you need to add security configuration. You have the choice between including it in your existing SpringWebConfig configuration class or create a new security specific configuration class. Here is a sample of both approaches:
1) Single #Configuration class approach
What's important to note here : SpringWebConfig extends WebMvcConfigurerAdapter + #EnableWebSecurity
#EnableWebMvc
#Configuration
#ComponentScan({ "com.company.mypackage" })
#EnableWebSecurity
public class SpringWebConfig extends WebMvcConfigurerAdapter {
#Autowired
public void whatever(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
}
2) Specific security #Configuration class
What's important to note here : MySecurityConfig extends WebSecurityConfigurerAdapter
Keep your SpringWebConfig as it was and create a new #Configuration class :
#Configuration
#EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
#Overide
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
}
For the difference between: registerGlobal(AuthenticationManagerBuilder auth) and configureGlobal(AuthenticationManagerBuilder auth)
The name of the configureGlobal method is not important. However, it is important to only configure AuthenticationManagerBuilder in a class annotated with either #EnableWebSecurity, #EnableWebMvcSecurity, #EnableGlobalMethodSecurity, or #EnableGlobalAuthentication. Doing otherwise has unpredictable results.
Source:
Chapter "Creating your Spring Security configuration" from the "Hello Spring Security Java Config" guide.
protected void configure(AuthenticationManagerBuilder auth) is a method that is likely provided by WebSecurityConfigurer (and its interface WebSecurityConfigurer) - I would say that is just a more type save approach, but does not differ in its result.
Very good answers here. Lemme talk some vanilla spring (no boot) in this regard. When we extend GlobalMethodSecurityConfiguration in our security configurations class, the difference between:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception{...}
and:
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {...}
is HUGE!
First of all, it's important to realize that the name of the method configureGlobal is meaningless. From the #Autowired JavaDoc we learn:
Config methods may have an arbitrary name and any number of arguments; each of those arguments will be autowired with a matching bean in the Spring container.
What's important in this regard is that configureGlobal does not override any method of the GlobalMethodSecurityConfiguration super class and that it's annotated with #Autowired. If we ask what exactly is auto-wired in this method - the answer would be the AuthenticationManagerBuilder - 'auth' argument.
We could try to declare the method as:
protected void configureGlobal(#Autowired AuthenticationManagerBuilder auth) throws Exception {...}
but again, the #Autowired annotation JavaDocs says:
Although #Autowired can technically be declared on individual method or constructor parameters since Spring Framework 5.0, most parts of the framework ignore such declarations. The only part of the core Spring Framework that actively supports autowired parameters is the JUnit Jupiter support in the spring-test module (see the TestContext framework reference documentation for details).
In essence, we won't get any auto-wiring for the 'auth' argument, so there goes this wrong attempt down the drain.
We're left to declare auto-wiring on the method level, which auto-wires the single argument 'auth'.
(proper) Auto-wiring means that spring is able to find a bean of the type AuthenticationManagerBuilder in the context and inject it into the method argument. A bean of this type must be already instantiated in order for spring to inject it. I won't go into the question who instantiates this particular bean but it must exist. Anyhoo... If auto-wiring worked - it's there!
However, when we use the #Override protected void configure(AuthenticationManagerBuilder auth) and override a super-class method, it becomes responsible for instantiating the AuthenticationManagerBuilder object. Whether the super-class obtained it from the application context or instantiated it using the new operator, is none of our concern in this case.
These are just nuances and they are not so important as the big question - when does all of this happen? This brings me to the most important difference which is lifecycle.
When we override spring configuration class methods, we are unwittingly (or wittingly - depends...) taking an active part in the Hollywood Principal of 'don't call us...'.
The super class is responsible for calling our overridden methods in a predefined order. This goes hand in hand with GoF's Template Design Pattern and their concept of hooks, along with the Builder pattern. Overriding methods of a Spring configuration super-class is just fine-tuning the build process without changing the construction order.
So why is this so important?
Let me show you an example by which we want to configure a Spring Security InMemoryUserDetailsManager without resorting to coding our own bean (which is really not an issue, but not the demonstrated case here). We will need to keep a reference to the InMemoryUserDetailsManager that spring build for us, for a later custom MethodSecurityExpressionHandler. The expression handler augments the expression context with nifty little Pre/Post security expressions that are used, for example in methods such as:
#PreAuthorize("isFamilyMember(#username) or isCatOnMouse(#username)")
public void changePassword(String username) throws AuthenticationException;
Here goes option 2: Overriding method configure
#Configuration
#EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true)
public class ExampleSecurityConfig extends GlobalMethodSecurityConfiguration {
private InMemoryUserDetailsManager inMemoryUserDetailsManager;
#Override #Bean protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Bean public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
inMemoryUserDetailsManager // we want this reference!
= (InMemoryUserDetailsManager) auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("tom")
.password("password")
.authorities("ROLE_CAT")
.and()
.getUserDetailsService();
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
CustomExpressionHandler expressionHandler =
new CustomExpressionHandler(inMemoryUserDetailsManager); //cannot be null!
return expressionHandler;
}
}
On one hand, we want to avoid coding redundant beans - but on the other we need to be able to reference those beans/components/services that Spring builds for us with it's nice fluent (and too complex for my liking) API. But we fail miserably!
The Hollywood Principal bites us in the rear. When we use this configuration, the super-class calls the overridden methods in it's own algorithm and the result in this case is that method createExpressionHandler() is called before method configure(AuthenticationManagerBuilder auth). Naturally, this triggers a null reference to inMemoryUserDetailsManager and obviously we'll see a NullPointerException that will crash the container startup with a BeanInsantiationException or an IllegalState... and a stack trace that will ruin our day. Not a pretty console site!
option 1: Not overriding method configure:
#Configuration
#EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true)
public class ExampleSecurityConfig extends GlobalMethodSecurityConfiguration {
#Autowired DataSource dataSource;
private InMemoryUserDetailsManager inMemoryUserDetailsManager;
#Override #Bean protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Bean public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
protected void configureBlah(AuthenticationManagerBuilder auth) throws Exception {
inMemoryUserDetailsManager // we want this reference!
= (InMemoryUserDetailsManager) auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("tom").password("password").authorities("ROLE_CAT")
.and()
.getUserDetailsService();
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
CustomExpressionHandler expressionHandler =
new CustomExpressionHandler(inMemoryUserDetailsManager); // this arg cannot be null!
return expressionHandler;
}
}
In this case, the method configureBlah(...), call it whatever you want, belongs to to our own ExampleSecurityConfig class, it is #Autowired, so spring will inject the AuthenticationManagerBuilder argument and it will be invoked before any of the super-class methods that are not a #PostConstruct or defined in some XML beans file as an init-method. This time around, we get the desired reference to inMemoryUserDetailsManager and our application works as expected.
It's all about lifecycle.

Categories

Resources