Spring Boot security - multiple authentication providers - spring

My problem is that I would like to have two Authentication providers
BEFORE:
I have my UserDetailServiceImpl and I validated the users against DB(not sure what provider is it) The user got role according to the data in DB
NOW:
I have used ActiveDirectoryLdapAuthentication provider as follows
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailService);
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
MyActiveDirectoryLdapAuthenticationProvider provider = new MyActiveDirectoryLdapAuthenticationProvider(domain, url, rootDN);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
I made it work, so I'm able to authenticate.
THE PROBLEM:
I'm unable now to login with the database users anymore, only LDAP now.
The UserDetailsService is not used, so which role the user has?
Is there way how to add some default role to LDAP authenticated user?
So how to enable both providers?
How to add roles to user, authenticated through LDAP auth.provider?
I would also really appreciate "big picture" description of what is going on here(under the hood). What is the role of each of the classes used here, it is really unclear how does it work(AuthenticationManager, AuthenticationManagerBuilder, AuthenticationProvider etc.) Maybe it is just hidden by autoconfiguration and so, but even looking to Spring Security directly does not make me any wiser.
Thanks for any hints
UPDATE
This code seems to be working properly
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailService);
}
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
MyActiveDirectoryLdapAuthenticationProvider provider = new MyActiveDirectoryLdapAuthenticationProvider(domain, url, rootDN);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setAuthoritiesMapper(new SimpleAuthorityMapper());
return provider;
}

Too many questions!
Both providers are enabled since you add them both to the AuthenticationManagerBuilder. But you add them both to the same filter chain and both accept the same kind of Authentication as inputs, so one of them always masks the other.
The standard LdapAuthenticationProvider has an authoritiesMapper that you can use to map authorities from the directory entries to your users (normally it is done out of the box using directory groups, e.g. see sample). I suppose if your directory doesn't contain groups you could make all users have the same authorities or something with a custom mapper.
Your #Beans of type AuthenticationManager and AuthenticationProvider look suspiciously redundant (and possibly harmful since they are global, and you are configuring the AuthenticationManagerBuilder for a single filter chain). I doubt you need that AuthenticationManager method at all and the other one doesn't need to be public or a #Bean (probably).

Related

LDAP authentication with AD LDP from Spring Boot application

I am trying to implement LDAP authentication in a Sprint Boot application. In the test environment I have installed an Active Directory LDP service with which to authenticate. I have created a user within the AD instance, enabled the account and set a password. I am then trying to authenticate using this account from the Spring login form.
When I try to log in using AD I get an error message:
Your login attempt was not successful, try again.
Reason: Bad credentials
As I am new to both AD and Spring it is quite possible I have mis-configured either (or both!).
Do you have any suggestions as to how I can further diagnose this problem or is there anything obvious I may have missed?
My Spring Boot code (I have tried a number of different variations on this code, this is one example):
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider =
new ActiveDirectoryLdapAuthenticationProvider("foo.bar", "ldap://servername:389");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
It turns out that there was nothing wrong with my Java implementation. The issue appears to be with the AD LDP configuration. I tried connecting to another, known good instance of AD LDP and authentication worked first time.
I am going to mark this as the answer as I am no longer interested in a solution to this question and wish to close it down...

LDAP searching AD finds no entries

My company has Active Directory set up. I am making an application whose login system should be linked to that AD, so I set up Spring LDAP authentication to implement it.
But when I try to log in to the application, I get
Failed to locate directory entry for authenticated user: my.name
javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-03100213, problem 2001 (NO_OBJECT), data 0, best match of:
'DC=dev,DC=company,DC=corp'
My Spring Security code looks like this:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("company.hr", "ldap://10.23.1.1:389/dc=dev,dc=company,dc=corp");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
One of the problems is that our AD structure is really branched out, and each of those organizational units has further subdivision:
Reading up on LDAP, I thought giving it only the path to the common parent folder should do recursive search through the tree and find the matching user, no matter what OU they are in, and indeed, if I do a dsquery from command prompt it does return the queried users, no matter where they are. However, that doesn't happen in Spring. I've read just about everything there is to find on the topic, and debugged for hours - but it seems my parameters are set correctly, yet I still get thrown the Exception.
My question would be - how to make Spring see that the user is, in fact, in Active Directory, without specifying the complete URL to that specific user (since there are dozens of different OUs)?
Found the solution. Sometimes, rereading the documentation for Xth time actually helps.
I changed the constructor call of the ActiveDirectoryLdapAuthenticationProvider from this
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("company.hr", "ldap://10.23.1.1:389/dc=dev,dc=company,dc=corp");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
to this
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("company.hr", "ldap://10.23.1.1:389", "dc=dev,dc=company,dc=corp");
The third parameter in the constrcutor is in fact "rootDn", so that's the place where it should be passed, not as an extension of the URL.

Combining Spring Security and Active Directory with JWT-Tokens

I am currently working on building an Angular2-Application accessing a Spring Boot backend server for authentication and editing database data via REST-APIs. My current basic configuration to authenticate against our Active Directory looks like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin();
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(AD_DOMAIN, "ldap://" + AD_URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
This works so that I can access the basic Spring Boot Actuator-APIs after logging in using the default Boot Login Form.
The next step would be intertwining the activeDirectoryLdapAuthenticationProvider with a token-based authentication solution that can be accessed by Angular2 - for that, i found an example repository on gitHub. It implements HMAC-based authentication using JWT tokens with a custom service structure.
My issue now comes in understanding how the components of Spring Security work together in the background. As i understand the example, it implements a customised version of UserDetailsService and its environment using the UserDTO and LoginDTO. These access a MockUser-Service for example user data - which is what I want to avoid, instead accessing our Active Directory to authenticate users.
I am still digging through the repository trying to understand every customised class implementation and if I can customise the AuthenticationService to fit my needs, but I feel relatively lost.
What I am looking for are experiences in combining these three components, or with Spring Security and how exactly the default UserDetailsService and AuthenticationProviders interact, so I can try and fit the ActiveDirectory-logic into the token solution.
Alternatively, is there a way to combine the default-LDAP/AD-Configuration with a default JWT-Solution such as this?

Spring security java config basic authentication filter

I am trying to add basic authentication for my APIs where the users will be authenticated based on their credential stored in MongoDB. I want to use java config instead of XML based config. So far what I have learnt is I have to create #Configuration by extending WebSecurityConfigurerAdapter and override configure method. In that I can add a custom filter by addFilterBefore().
But how can I get a Authentication header information in the filter, how to proceed if the user is authenticated. I have been googling a lot but didn't find any good example that will help a novice like me whose been into spring just for 1 week.
Does any one have a good tutorial or sample that can help me get started with this? Thanks in advance.
As example, you can use next solution.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().authenticationEntryPoint(getBasicAuthenticationEntryPoint());
}
#Bean
public BasicAuthenticationEntryPoint getBasicAuthenticationEntryPoint(){
BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
basicAuthenticationEntryPoint.setRealmName("Basic Authentication");
return basicAuthenticationEntryPoint;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
It works for me. But you need to implement UserDetailsService interface.
Spring automatically checks is user authenticated and tries to proceed authentication if not.

With Spring Security are both encrypted and unencrypted passwords accepted in the same program?

SUMMARY: In this Spring Security program I can login with (sam/abc125) and (neal/$ENCRYPTED_abc125). Shouldn't the (sam/abc125) login fail?
DETAILS:
In the process of learning something new I copied a tutorial Spring Security project from Websystique Spring Security 4 Hibernate Role Based Login Example . Works fine. In particular there are login/password combos of (sam/abc125) and (kenny/abc127). These logins work fine and the web page directs you to the proper place.
I wanted to add Bcrypt password encryption (from Websystique Spring Security 4 Hibernate Password Encoder Bcrypt Example) to this example -- adding it to the role based example seems preferable to adding role based stuff to the encrypted project version. So I carefully copied the code from the encryption-enabled project to my role-based project.
I made slight adjustments to some JSP pages -- missing some DIVs needed for formatting. I then added one new user record (neal/$ENCRYPTED_abc125). The $ENCRYPTED_abc125 is "abc125" properly encrypted with BCryptPasswordEncoder. I checked the "quick encoder" program results with what the web page provided.
The web site is accepting the (sam/abc125) login, even though the password "abc125" is stored in the clear in the user table. It is also accepting the (neal/$ENCRYPTED_abc125) login, even though the password $ENCRYPTED_abc125 is stored encrypted in the user table. It also accepts (kenny/abc127).
Since all of the authentication is hidden within Spring Security I can't debug my way through it. I'm left with expert knowledge for my question, which is:
Is there something within Spring Security that detects that the user table has an unencrypted password and encrypts it before comparing it with the user-supplied password text?
Note that the clear-text passwords predate the conversion to an encrypted storage and wouldn't be there for a live version. My question here is in trying to figure if something is broken or if it is working as designed.
Here is the security configuration from the project.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
#Autowired
CustomSuccessHandler customSuccessHandler;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Bean
public PasswordEncoder psswordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//.antMatchers("/", "/home").permitAll()
.antMatchers("/", "/home").access("hasRole('USER')")
.antMatchers("/admin/**", "/newuser").access("hasRole('ADMIN')")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
//.and().formLogin().loginPage("/login")
.and().formLogin().loginPage("/login").successHandler(customSuccessHandler)
.usernameParameter("ssoId").passwordParameter("password")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/Access_Denied");
}
}

Resources