Spring security Java config custom AuthenticationProvider order - spring

Using the following code to configure Spring security, I can see in ProviderManager, List AuthenticationProvider has two elements.
MyAuthenticationProvider at index 0 and AnonymousAuthenticationProvider at index 1.
My question is that is there a way to make AnonymousAuthenticationProvider at index 0?
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyAuthenticationProvider myAuthenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().authenticationProvider(myAuthenticationProvider);
}
}
I can do http.anonymous().disable() and init and set AnonymousAuthenticationProvider manually but
I am not sure what key for constructor I need to provide.
it is more like a workaround.

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

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;
}
}

Customize LdapAuthoritiesPopulator in configuration

The DefaultLdapAuthoritiesPopulator sets a search scope of "ONE_LEVEL", but I need to search "SUBSCOPE" to get the list of groups a user is a member of.
I've been following the "configuration" style Spring setup (code, not XML). While there's tons of examples of how to configure a custom LdapAuthoritiesPopulator in XML, I'm kind of stuck on how to do it in code.
Here's what I have so far:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
.contextSource().url("ldap://ldap.company.org/")
.and()
.userSearchBase("o=company.org,c=us")
.userSearchFilter("(uid={0})")
.groupSearchBase("o=company.org,c=us")
.groupSearchFilter("(&(objectClass=groupOfUniqueNames)(uniqueMember={0}))");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().and().authorizeRequests()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll();
}
}
What's missing is that I need to be able to set the search scope on the DefaultLdapAuthoritiesPopulator. The class itself exposes a "setSearchSubtree" method, but the LdapAuthenticationProviderConfigurer does not provide a way of configuring it.
Any suggestions?
Solution is to set this property in LdapAuthoritiesPopulator and pass it to LdapAuthenticationProvider
Refer Example 1 in : https://www.programcreek.com/java-api-examples/?api=org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator
#Bean
public LdapAuthoritiesPopulator authoritiesPopulator(){
DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(
contextSource(),
groupSearchBase);
populator.setGroupSearchFilter("(uniqueMember={0})");
populator.setGroupRoleAttribute("cn");
**populator.setSearchSubtree(true);**
populator.setRolePrefix("");
return populator;
}
You need to add something like:
final SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
To before you begin your search.
Why it is called a "control" is beyond me (an LDAP guy), but that is what Spring does.
-jim

Spring Boot setup with multiple authentication providers (API+Browser)

My application serves both API and browser. I've implemented API Token authentication with all custom providers and filter. The configuration now seems to interfere with the browser version.
I have two questions that I need advice on how to solve, as I'm not getting anywhere after digging through the documentation and other examples.
1) My StatelessAuthenticationFilter is being called despite a request
coming from the browser. I have e.g. specified the request matcher to "/api/**". Why is that?
2) The AuthenticationManager have not registered two AuthenticationProviders. This is my conclusion after debugging my StatelessAuthenticationFilter that's being called wrongly.
Here's the configuration classes that I have
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Order(1)
#Configuration
public static class A extends WebSecurityConfigurerAdapter {
#Autowired
TokenAuthenticationProvider tokenAuthenticationProvider;
#Autowired
ApiEntryPoint apiEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
StatelessAuthenticationFilter filter = new StatelessAuthenticationFilter();
AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher("/api/**");
filter.setRequiresAuthenticationRequestMatcher(requestMatcher);
filter.setAuthenticationManager(super.authenticationManager());
http.csrf().disable()
.exceptionHandling().authenticationEntryPoint(apiEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/user/register");
}
}
#Configuration
public static class B extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new DaoAuthenticationProvider());
}
}
}
As you can see, B class doesn't specify anything, yet when I access localhost:8080 the StatelessAuthenticationFilter is called. What is going on here?
In class A you are configuring the StatelessAuthenticationFilter to use a requestMatcher. Whatever you do with that, spring does not know or care about that.
You must also restrict your security configuration using
http.antMatcher("/api/**")
otherwise its configured for every URI and the StatelessAuthenticationFilter will be invoked for every request, exactly as you described.
You should also annotate class A and B with #Order as shown in the example at multiple-httpsecurity

Where is better to put my custom authentication logic?

I want to add a bit of logic to my authentication in Spring Boot, check if an account have a specific logic, for example if a date in its account is before the current date.
Where is best placed in a custom filter or in UserDetailsService?
If it's in a filter, is better to extends from any spring class?
Explanation
As you can see bellow I use a custom userDetailsService() to get the users details (CuentaUser) in which there are the fields needed for the logic (for example the expiration date). So now I need to add the logic and comes to me two places where I can put it: in UserDetailsServices (throwing an exception if the logic fails) or as a custom filter.
Where is better to put my custom the authentication logic?
This is my actual security configuration:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CuentaRepository accountRepository;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
#Bean
public UserDetailsService userDetailsService() {
return (username) -> accountRepository.findByUsuario(username)
.map(a -> new CuentaUser(a, AuthorityUtils.createAuthorityList("USER", "write")))
.orElseThrow(() -> new UsernameNotFoundException("could not find the user '" + username + "'"));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
CsrfTokenResponseHeaderBindingFilter csrfTokenFilter = new CsrfTokenResponseHeaderBindingFilter();
http.addFilterAfter(csrfTokenFilter, CsrfFilter.class);
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
Edit: I found that for the example of expiration date, UserDetails have an attribute for it, so is better to use it. Anyway you need to check it with a custom AuthenticationProvider if you don't use the default.
You can use an AuthenticationProvider and put the login inside it.
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
You can see more here:
http://www.baeldung.com/spring-security-authentication-provider

Resources