How to get the AuthenticationManager when using the AuthenticationManagerBuilder to add custom provider? - spring

I'm using Spring Boot 2.0 (leveraging Spring Security 5.0). I'm trying to add a custom AuthenticationProvider to the AuthenticationManager in my WebSecurityConfigurerAdapter subclass. If I override the configure(AuthenticationManagerBuilder) method to supply my new provider, then I do not know how to retrieve the AuthenticationManager as a bean.
Ex:
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.authenticationProvider(customAuthenticationProvider);
}
}
where customAuthenticationProvider implements AuthenticationProvider.
In the Spring docs, it seems to specify that the two are incompatible:
5.8.4 AuthenticationProvider You can define custom authentication by exposing a custom AuthenticationProvider as a bean. For example, the
following will customize authentication assuming that
SpringAuthenticationProvider implements AuthenticationProvider:
[Note] This is only used if the AuthenticationManagerBuilder has not
been populated
Indeed, if I try to retrieve the AuthenticationManager bean using:
#Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManagerBean();
}
then the configure() method is never even called.
So how can I add my own custom provider to the default list of providers, and still be able to retrieve the AuthenticationManager?

You can just override WebSecurityConfigurerAdapter.authenticationManagerBean() method and annotate it with #Bean annotation
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
And there is no Spring Boot 5.0, The latest release is Spring Boot 2.0. I believe you are talking about Spring Security 5.0.

Related

spring boot using 2 different oauth providers at the same time

I'm facing a use where I have to check oauth token against 2 different oauth provider given an input context (private call to may api vs public call)
Is there a simple way to define 2 oauth provider in spring boot and how to configure this balancing between the 2 providers ?
First you will need to implement 2 AuthenticationProvider then in your configuration class that implements WebSecurityConfigurerAdapter you would autowire those providers. Finally override the public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { to add those providers.
#Configuration
public class SampleAuthConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider1 provider1;
#Autowired
private CustomAuthenticationProvider2 provider2;
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.authenticationProvider(this.provider1)
.authenticationProvider(this.provider2);
}
}
Below are some tutorials. The may be outdated but may help you to figure it out.
https://dzone.com/articles/spring-security-authentication
https://www.baeldung.com/spring-security-multiple-auth-providers

Required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found. message from spring security

I am trying to implement one sample demo for Spring Security with Spring Boot for checking the authentication. I am trying to implement a basic workout for Spring Security and getting the following message,
Description:
Parameter 0 of constructor in com.spacestudy.service.CustomAuthenticationProvider required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.web.AuthenticationEntryPoint' in your configuration.
My security config class SecurityConfig.java,
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationEntryPoint authEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new ApplicationContextHeaderFilter((ApplicationContext) authenticationManager()));
}
}
And my BasicAuthenticationFilter implementation like the following,
#Component
public class CustomAuthenticationProvider extends BasicAuthenticationFilter {
public CustomAuthenticationProvider(AuthenticationManager authenticationManager) {
super(authenticationManager);
// TODO Auto-generated constructor stub
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String bearerToken = request.getHeader("accessToken");
String username = "test";
String password = "test";
if (username != null && !username.isEmpty()) {
return new UsernamePasswordAuthenticationToken(username, null, null);
}
return null;
}
}
How can I resolve this issue?
Lot of problems are there in your code.
(ApplicationContext) authenticationManager()
you can not cast AuthenticationManager to ApplicationContext
.addFilter(new ApplicationContextHeaderFilter(...))
I don't know Why you are using ApplicationContextHeaderFilter for simple demo application.
You should have preferred BasicAuthenticationFilter or even simple default configuration provided for HttpSecurity with .httpBasic()
You should have preferred UsernamePasswordAuthenticationFilter or even simple default configuration provided in HttpSecurity with .formLogin()
CustomAuthenticationProvider extends BasicAuthenticationFilter
An authentication provider is one which implements AuthenticationProvider interface. In your case naming should be xxxAuthFilter.
You have done nothing in below code.(got existing authentication object and set it back without creating an valid authentication object.)
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
Coming to the AuthenticationManager and AuthFilters implementation point of view,
For add filter you can add any implementation of Spring Security provided filters as given below
.addFilter(AnyFilterImplementationFromThisLink)
(But not all filters are auth filters. Where auth filters will attempt to authenticate with the authenticationManager configured)
For example If you consider UsernamePasswordAuthenticationFilter or BasicAuthenticationFilter
you should take care of setting AuthenticationManager where your auth manager should override authenticate() method and it should return Authentication object(where Authentication object will have auth principal, credentials and granted authorities list)
Or
If you don't want to implement authentication manager...
In simple way in your filters(implementation of OncePerRequestFilter) doFilterInternal() method set the `Authentication` object in `SecurityContext`
List<GrantedAuthority> authorityList = new ArrayList<>();
GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
authorityList.add(authority);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password, authorityList);
//Note UsernamePasswordAuthenticationToken implements Authentication
SecurityContextHolder.getContext().setAuthentication(authToken);
How any auth filter works is if there is a valid Authentication object then filter chain will continue without attempting authentication otherwise it will attemptAuthentication by overrided attemptAuthentication() method.
But your ApplicationContextHeaderFilter is a implementation of OncePerRequestFilter where it has no attemptAuthentication() and i don't know the order of ApplicationContextHeaderFilter if it's order is after creating security context then you can set the authentication object to security context.
Your error seems to be that the AuthenticationManager is not present as a Spring Bean.
Option 1
Register an AuthenticationManager in Spring Bean. All is provided by Spring for do this directly in your SecurityConfig class by overriding the WebSecurityConfigurerAdapter#authenticationManagerBean method like explain in the documentation of it
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Option 2
Avoid to register an AuthenticationManager in Spring, but directly your CustomAuthenticationProvider classs.
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public CustomAuthenticationProvider authenticationProvider() throws Exception {
return new CustomAuthenticationProvider(authenticationManager());
}
}
Don't forget to remove the #Component annotation on the CustomAuthenticationProvider class with this method.
I am not sure but shoudn't the CustomAuthenticationProvider implement AuthenticationProvider and AuthenticationManager is just a container for authentication providers and it seems that you dont have any.
Check this site for more info
https://www.baeldung.com/spring-security-authentication-provider
You can try on this, put it in config security file
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
Create a new java class. And Configure like below:
#Configuration
#RequiredArgsConstructor
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
}

Spring Security : configure(AuthenticationManagerBuilder auth) vs authenticationManagerBean()

I am configuring Spring Security. To authenticate and authorize users, I override configure(AuthenticationManagerBuilder auth) of WebSecurityConfigurerAdapter. This works fine. Below is my code:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(customUserDetailsService)
.passwordEncoder(getPasswordEncoder());
}
But when I try to to enable method level security, per action, using #EnableGlobalMethodSecurity(securedEnabled = true) it throws an exception:
No AuthenticationManager found
As per my understanding AuthenticationManager is used to authenticate and authorize users, which I was already doing using configure(AuthenticationManagerBuilder auth) and Spring was injecting auth object itself.
Why I need to register AuthenticationManager manually?
#Bean #Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
What are the differennt purposes configure(AuthenticationManagerBuilder auth) and authenticationManagerBean() serves?
I am extending WebSecurityConfigurerAdapter. Why I need to provide a custom AuthenticationManager by overriding authenticationManagerBean().
Your configuration class extends WebSecurityConfigurerAdapter, which only configures web security (not method security):
Provides a convenient base class for creating a WebSecurityConfigurer instance. The implementation allows customization by overriding methods.
So your AuthenticationManager is only used for web security.
If you want to configure (change the defaults) method security, you can extend GlobalMethodSecurityConfiguration:
Base Configuration for enabling global method security. Classes may extend this class to customize the defaults, but must be sure to specify the EnableGlobalMethodSecurity annotation on the subclass.
To configure AuthenticationManager for method security, you can
overwrite GlobalMethodSecurityConfiguration#configure:
Sub classes can override this method to register different types of authentication. If not overridden, configure(AuthenticationManagerBuilder) will attempt to autowire by type.
expose your AuthenticationManager as a bean that can be autowired by GlobalMethodSecurityConfiguration, see WebSecurityConfigurerAdapter#authenticationManagerBean:
Override this method to expose the AuthenticationManager from configure(AuthenticationManagerBuilder) to be exposed as a Bean.
use only one global AuthenticationManager by autowiring the global AuthenticationManagerBuild, see Spring Security 3.2.0.RC2 Released:
For example, if you want to configure global authentication (i.e. you only have a single AuthenticationManager) you should autowire the AuthenticationMangerBuilder:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
// ... configure it ...
}

Understanding Spring Boot's Oauth2 starter

I started off looking at the Oauth2 starter project and minimal configuration.
https://github.com/spring-projects/spring-security-oauth/blob/master/tests/annotation/jdbc/src/main/java/demo/Application.java
All the examples either use in memory configuration or jdbc configuration for storing client roles (e.g ClientDetailsServiceConfigurer) . In my case the details should come in LDAP. So I have two questions.
How do override the default to go to ldap instead of memory or jdbc.
In general , where how do I unravel the Spring Boot thread and read the starter source code and how to change default config ? All I see is a high level annotation.
org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer
This indirection in Spring Boot makes it extremely difficult to follow and scant documentation doesn't help. Or maybe I am missing something?
thanks !!! this has been bugging me for a while.
To implement Oauth2 with LDAP, you may follow this tutorial : https://raymondhlee.wordpress.com/2015/12/05/oauth2-authorization-server-with-spring-security.
You may also take a look a this other question: spring-security-oauth2 2.0.7 refresh token UserDetailsService Configuration - UserDetailsService is required
As for your other question "I want to follow the request and see what components get invoked and when": I suggest you add logging.
(1) Add logging in every method
(2) Set log level for security package in application.properties:
logging.level.org.springframework.security=DEBUG
(3) Add CommonsRequestLoggingFilter:
#Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
LOGGER.info("Creating CommonsRequestLoggingFilter");
CommonsRequestLoggingFilter crlf = new CommonsRequestLoggingFilter();
crlf.setIncludeClientInfo(true);
crlf.setIncludeQueryString(true);
crlf.setIncludePayload(true);
return crlf;
}
(4) Add log level for CommonsRequestLoggingFilter (in application.properties):
logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG
For the OAuth/LDAP tutorial, here's the notable parts (quoted from https://raymondhlee.wordpress.com/2015/12/05/oauth2-authorization-server-with-spring-security):
Authorization Server Configuration Below is my implementation of the
AuthorizationServerConfigurerAdapter. The database schema for JDBC
client details and token services can be found in here.
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new JdbcTokenStore(dataSource)).authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
}
Login Security Configuration Below is the security configuration
handling user authorization.
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE) // note 1
public class LoginConfig extends WebSecurityConfigurerAdapter {
#Value("${ldap.domain}")
private String DOMAIN;
#Value("${ldap.url}")
private String URL;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel().anyRequest().requiresSecure();
// Only requests matching regex are handled by this security configurer
http.requestMatchers().regexMatchers("/login", "/login.+", "/oauth/.+", "/j_spring_security_check", "/logout"); //
AuthenticationEntryPoint entryPoint = entryPoint();
http.exceptionHandling().authenticationEntryPoint(entryPoint);
http.formLogin(); // note 3i
http.addFilter(usernamePasswordAuthenticationFilter());
http.authorizeRequests().antMatchers("/login").permitAll();
http.authorizeRequests().antMatchers("/oauth/**").authenticated();
http.authorizeRequests().antMatchers("/j_spring_security_check").anonymous().and().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception { // note 4
authManagerBuilder.parentAuthenticationManager(authenticationManager());
}
protected AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(DOMAIN, URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
private AuthenticationEntryPoint entryPoint() {
return new LoginUrlAuthenticationEntryPoint("/login");
}
private UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() {
UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager();
AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler("/login?login_error=true");
filter.setAuthenticationFailureHandler(failureHandler);
return filter;
}
}

spring security not able to add custom authentication providers

I have created additional authentication providers. I am registering them like following:
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(tokenAP());
auth.authenticationProvider(usernameAndPasswordAP());
auth.userDetailsService(getUserDetailsService());
}
Later in my code I am using AuthenticationManager to authenticate users. The issue is that I have only one authentication provider registered in authentication manager which is DaoAuthenticationProvider. It looks like my authentication providers are not registered at all. Should I do some additional config to make it work? I am using spring boot 1.2.6 Thanks in advance for any tips. Best Regards
When you override the configure(AuthenticationManagerBuilder auth), the underlying AuthenticationManager is exposed in one of 2 ways:
1) within SecurityConfig, you can simply call authenticationManager()
2) if you need AuthenticationManager outside of your SecurityConfig you will need to expose it as a bean, for example:
class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(tokenAP());
auth.authenticationProvider(usernameAndPasswordAP());
auth.userDetailsService(getUserDetailsService());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
The way we have authentication providers configured in our Spring Boot web applications is similar to what is discussed in the example Spring Security Java configuration from the current release reference guide, which modifies the default autowired AuthenticationManagerBuilder. Using your methods, it might look like:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(tokenAP())
.authenticationProvider(usernameAndPasswordAP())
.userDetailsService(getUserDetailsService());
}
If I am reading the Javadocs for the configure(AuthenticationManagerBuilder) method correctly, when you override this method you must specify your own AuthenticationManager. By using the autowired instance as described above, the default AuthenticationManager (which is ProviderManager, which in turn delegates to one or more configured AuthorizationProvider instances).
You may also need to annotate your configuration class with:
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
So that your access control rules are applied before the defaults that Spring Boot will otherwise configure for you.

Resources