Spring Boot - are single implementing classes automatically injected - java

I am relatively new to Spring in general but have read through the Apress Spring Rest text and gotten the examples running in Eclipse without problem.
Something that puzzles me in the examples is how objects appear to be automatically injected.
For example in chapter 8 security there is a QuickPollUserDetailsService class which implements the spring UserDetailsService class.
The text says the following:
"The SecurityConfig class declares a userDetailsService property, which gets injected with a QuickPollUserDetailsService instance at runtime."
#Configuration
#EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Inject
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService) .passwordEncoder(new BCryptPasswordEncoder());
}}
Nowhere in any file does it specify QuickPollUserDetailsService is to be set.
Is Spring being smart here and noticing that QuickPollUserDetailsService is the only implementer of UserDetailsService and therefore assuming that it must be injected?
If that is the case what if I had 2 or more implementer's of UserDetailsService

By default, Spring Boot will recursively scan the packages and available implementation will be automatically injected. If there is more than one implementation available, startup will fail.

Related

circular dependency in web application using spring boot security

Circular dependency error. As I'm new to this technology i couldn't solve this. i need some here to clear this issue.
This code is to secure few pages in the existing project. I have to secure only 3 pages to access only by admin. Any solution which can escape circular dependency or which can fulfil my task will help.
My task is to complete secure few pages from accessing the user. This code is taken from a stackoverflow snippet.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/editincident","editaccident","editreqeust").authenticated()
.anyRequest().permitAll()
.and()
.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
}
you can write one line in your application.properties file to remove this error.
spring.main.allow-circular-references= true
In spring.io docs there is
"If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection."
So you should change the way you create one of your beans with using constructor method

Intended way to add a new spring-securty chain Filter in the context of a spring boot library

I'm developing a library that introduces a new authentication filter, that should be used in the spring-security chain
I know I can add the filter via something like:
#EnableResourceServer
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
class AmazingSecurityConfiguration extends WebSecurityConfigurerAdapter {
// filter bean
#Autowired
private MyFilter myFilter;
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// ... bunch of requirements per path
// ... or ignore path "/bla"
// etc.
.anyRequest().authenticated();
http.addFilterBefore(myFilter.getFilter(),
AbstractPreAuthenticatedProcessingFilter.class);
}
}
However, this wouldn't work alongside a user defined config. Really, I want to be configuring this bean within my library AutoConfigure class, that's triggered by the spring factories.
Looking around online, I see examples where they extend the WebSecurityConfigurerAdapter class themselves, and have the user extend this new config. But I don't know if this blocks a user from doing something else, and it also relies on the user first calling super.configure(http) to have the filter loaded.
What's the correct way here?
I was able to configure my own http configuration by fiddling with the invocation order of the beans within the autoconfiguration
#Configuration
#ConditionalOnClass(WebSecurityConfigurerAdapter.class)
// Two annotations below ensure that this is not the only WebSecurityConfigurerAdapter,
// as it might otherwise disable security for the rest of the application
// The Order ensures that Spring default which uses ConditionalOnMissingBean is still configured before this class
#AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
#ConditionalOnBean(WebSecurityConfigurerAdapter.class)
class LibraryFooSecurityConfig {
#RequiredArgsConstructor
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// autowired in from elsewhere in the autoconfig
private final MyFilter filter;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(filter,
AbstractPreAuthenticatedProcessingFilter.class);
}
}
}
This allows a user-defined security config to extend WebSecurityConfigurerAdapter and apply their own rules, while still allowing our library to add it's own items after that of the actual spring-boot application

Spring security 4 - configure(AuthenticationManagerBuilder auth) works without #Autowired

When configuring Spring Security :
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
//#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
when running, with or without #Autowired, it works.
where AuthenticationManagerBuilder come from if it's not Autowired ?
There is no "injection" going on there. 'configure' is just a method that takes a AuthenticationManagerBuilder object.
Your SecurityConfig object implements WebSecurityConfigurerAdapter, and is a Spring Bean because of the annotations on it. You also enable security behavior via an annotation. All of this will cause Spring to be looking for beans of type WebSecurityConfigurerAdapter to serve a purpose in the set up of security. It finds your bean because it is one of these objects.
Spring knows what this type of bean is supposed to do, so it just calls the appropriate methods on that bean.
Because you have overloaded one of the methods of WebSecurityConfigurerAdapter, your version of that method will be called.
#Autowired is only for member variables that reference beans.
It is injected automatically in the method by the caller. Anyway #Autowiring has no role to play in a method argument.

How to debug spring dependencies order?

Behind this issue, I want to understand and debug how Spring beans are instantiated (in which order) and how dependencies are resolved.
Context
I'm using:
spring boot
spring security
spring data JPA
I added to my DAO security annotations like:
#PreAuthorize("hasRole('ROLE_ADMIN') OR hasPermission(#entity, 'save')")
<S extends T> S save(S s);
And I have a custom security configuration:
#Configuration
#Slf4j
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) throws Exception {
log.info("Configuring Authentication...");
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
Obviously, autowired beans doesn't depends on DAO which have security annotations.
It works fine if I run main method (which use Spring Boot), no matter if it's compiled jar or in my IDE). But in some case (junit tests for example), I get this exception:
java.lang.IllegalStateException: Cannot apply org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer#362b384c to already built object
I assume method AuthenticationManagerBuilder.build() has been called before Spring create and configure my SecurityConfiguration. But why? Order of WebSecurityConfigurerAdapter is set to 100, it should be done before everything else!
Question
How can I see why Spring create other beans (DAO and services) before configuration? How can I check there is no dependency from my configuration service to my services / dao?
Thanks,
The #Autowired method is going to be called immediately after that configuration class is instantiated (which will be triggered by the security filter being created when the web container starts). That's pretty early, but not early enough for your use case, since you need to be the first to use the AuthenticationManagerBuilder. I recommend you use a GlobalAuthenticationConfigurerAdapter (i.e. a new bean) like in the Spring Boot samples (Spring Security will look for beans of that type even earlier).

How Spring Security add/configure AuthenticationManagerBuilder?

I am working on Spring Security Java-based configuration.
I have created my own MyAuthenticationProvider which I want to register in the ProviderManager (single instance of AuthenticationManager).
I have found that ProviderManager has a list of providers to which I can register my single
MyAuthenticationProvider.
Here is the part of my Configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(MyAuthenticationProvider);
}
}
I found out that AuthenticationManagerBuilder has parentAuthenticationManager, defaultUserDetailsService and many other fields.
My questions are:
Where is this #Autowired annotation adding AuthenticationManagerBuilder auth from?
Is the AuthenticationManagerBuilder already created in the application context?
What would be the default state of AuthenticationManagerBuilder which is being injected? By default state I mean will there be some parentAuthenticationManager, authenticationProviders already registered in the AuthenticationManagerBuilder?
If I am adding auth.authenticationProvider(MyAuthenticationProvider), does this mean that I am adding one more provider in the AuthenticationManagerBuilder?
What does this mean? Taken from Spring Documentation
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.
Answer for 1:
#EnableWebSecurity is meta-annotated with #EnableGlobalAuthentication
...
#EnableGlobalAuthentication
#Configuration
public #interface EnableWebSecurity {
...
and #EnableGlobalAuthentication imports AuthenticationConfiguration:
...
#Import(AuthenticationConfiguration.class)
#Configuration
public #interface EnableGlobalAuthentication {
}
In AuthenticationConfiguration, you'll see that an AuthenticationManagerBuilder bean is declared:
...
#Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
...
}
When you #Autowire an AuthenticationManagerBuilder, this is the one that you will get. You have several methods at your disposal to easily configure in-memory, jdbc, ldap,... authentication.
Answer for 2:
Background:
The Spring Security Java config goes through several stages to seamlessly incorporate your configurations with the ApplicationContext.One place where this comes together is in the getHttp() method in WebSecurityConfigurerAdapter.
For example, this is an excerpt:
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
To give you an idea of how "not-straightforward" the sequence of configuration is, the authenticationManager variable above will be either:
The authentication manager you added by overriding configure(AuthenticationManagerBuilder auth)
OR: The authentication manager you added in the method that #Autowired the AuthenticationManagerBuilder bean from AuthenticationConfiguration
OR: an AuthenticationManager bean found in the context
By default state I mean will there be some [...] authenticationProviders already registered in the AuthenticationManagerBuilder
If you look at AuthenticationConfiguration, you'll see that by default, the InitializeUserDetailsBeanManagerConfigurer is applied to the AuthenticationManagerBuilder bean. As long as it finds a UserDetailsService bean in the context and no other provider has been added, it will add a DaoAuthenticationProvider. This is why in the Spring Security reference, only providing a #Bean UserDetailsService bean is sufficient.
But once you add an authentication provider as you did, the "default" provider is not registered.
Answer for 3:
Yes. The code of AuthenticationManagerBuilder adds your provider:
public AuthenticationManagerBuilder authenticationProvider(AuthenticationProvider authenticationProvider) {
this.authenticationProviders.add(authenticationProvider);
return this;
}
Answer for 4 is simple:
It means that, once you have one of that annotations, you can name your method as you wish:
#Configuration
#EnableWebSecurity //or #EnableWebMvcSecurity or #EnableGlobalMethodSecurity....
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void myCoolMethodName(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(MyAuthenticationProvider);
}
}
"Doing otherwise has unpredictable results"
If you keep the name but not the annotations, it may not work.

Categories