Configuring Spring Security 3.x to have multiple entry points - java

I have been using Spring Security 3.x for handling user authentication for my projects, and so far, it has worked flawlessly.
I recently received the requirements for a new project. In this project, it requires 2 sets of user authentication: one to authenticate employees against LDAP, and another to authenticate customer against database. I'm a little stumped on how to configure that in Spring Security.
My initial idea was to create a login screen that has the following fields:-
radio button field - for users to choose whether they are employees or customers.
j_username user field.
j_password password field.
If the user selects "employee", then I want Spring Security to authenticate them against LDAP, otherwise the credential will be authenticated against database. However, the problem is the form will be submitted to /j_spring_security_check and there's no way for me to send the radio button field to my implemented custom authentication provider. My initial thought is I probably need two form submission URLs rather than relying on the default /j_spring_security_check. Each URL will be handled by different authentication providers, but I'm not sure how to configure that in Spring Security.
I know in Spring Security, I can configure fall back authentication, for example if LDAP authentication fails, then it will fall back to database authentication, but this is not what I'm shooting for in this new project.
Can someone share how exactly I should configure this in Spring Security 3.x?
Thank you.
UPDATE - 01-28-2011 - #EasyAngel's technique
I'm trying to do the following:-
Employee form login submits to /j_spring_security_check_for_employee
Customer form login submits to /j_spring_security_check_for_customer
The reason I want 2 different form logins is to allow me to handle the authentication differently based on the user, instead of doing a fall-back authentication. It is possible that employee and customer have same user ID, in my case.
I incorporated #EasyAngel's idea, but have to replace some deprecated classes. The problem I'm currently facing is neither filter processes URLS seem registered in Spring Security because I keep getting Error 404: SRVE0190E: File not found: /j_spring_security_check_for_employee. My gut feeling is the springSecurityFilterChain bean is not wired properly, thus my custom filters are not used at all.
By the way, I'm using WebSphere and I do have com.ibm.ws.webcontainer.invokefilterscompatibility=true property set in the server. I'm able to hit the default /j_spring_security_check without problem.
Here's my complete security configuration:-
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<sec:http auto-config="true">
<sec:form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" default-target-url="/welcome.jsp"
always-use-default-target="true" />
<sec:logout logout-success-url="/login.jsp" />
<sec:intercept-url pattern="/employee/**" access="ROLE_EMPLOYEE" />
<sec:intercept-url pattern="/customer/**" access="ROLE_CUSTOMER" />
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</sec:http>
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/**" filters="authenticationProcessingFilterForEmployee, authenticationProcessingFilterForCustomer" />
</sec:filter-chain-map>
</bean>
<bean id="authenticationProcessingFilterForEmployee" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManagerForEmployee" />
<property name="filterProcessesUrl" value="/j_spring_security_check_for_employee" />
</bean>
<bean id="authenticationProcessingFilterForCustomer" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManagerForCustomer" />
<property name="filterProcessesUrl" value="/j_spring_security_check_for_customer" />
</bean>
<bean id="authenticationManagerForEmployee" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="employeeCustomAuthenticationProvider" />
</list>
</property>
</bean>
<bean id="authenticationManagerForCustomer" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="customerCustomAuthenticationProvider" />
</list>
</property>
</bean>
<bean id="employeeCustomAuthenticationProvider" class="ss.EmployeeCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.EmployeeUserDetailsService"/>
</property>
</bean>
<bean id="customerCustomAuthenticationProvider" class="ss.CustomerCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.CustomerUserDetailsService"/>
</property>
</bean>
<sec:authentication-manager>
<sec:authentication-provider ref="employeeCustomAuthenticationProvider" />
<sec:authentication-provider ref="customerCustomAuthenticationProvider" />
</sec:authentication-manager>
</beans>
I'm starting a bounty here because I can't seem to get this working for several days already... frustration is the word. I'm hoping someone will point out the problem(s), or if you can show me a better or cleaner way to handle this (in code).
I'm using Spring Security 3.x.
Thank you.
UPDATE 01-29-2011 - #Ritesh's technique
Okay, I managed to get #Ritesh's approach to work very closely to what I wanted. I have the radiobutton that allows user to select whether they are customer or employee. It seems like this approach is working fairly well, with one problem...
If employee logs in with right credential, they are allowed in... WORK AS EXPECTED.
If employee logs in with wrong credential, they are not allowed in... WORK AS EXPECTED.
If customer logs in with right credential, they are allowed in... WORK AS EXPECTED.
If customer logs in with wrong credential, the authentication falls back to employee authentication... DOESN'T WORK. This is risky because if I select customer authentication, and punch it the employee credential, it will allow the user in too and this is not what I want.
<sec:http auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
<sec:logout logout-success-url="/login.jsp"/>
<sec:intercept-url pattern="/employee/**" access="ROLE_EMPLOYEE"/>
<sec:intercept-url pattern="/customer/**" access="ROLE_CUSTOMER"/>
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<sec:custom-filter position="FORM_LOGIN_FILTER" ref="myAuthenticationFilter"/>
</sec:http>
<bean id="myAuthenticationFilter" class="ss.MyAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler" ref="failureHandler"/>
<property name="authenticationSuccessHandler" ref="successHandler"/>
</bean>
<bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
</bean>
<bean id="successHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/welcome.jsp"/>
<property name="alwaysUseDefaultTargetUrl" value="true"/>
</bean>
<bean id="failureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login.jsp?login_error=1"/>
</bean>
<bean id="employeeCustomAuthenticationProvider" class="ss.EmployeeCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.EmployeeUserDetailsService"/>
</property>
</bean>
<bean id="customerCustomAuthenticationProvider" class="ss.CustomerCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="ss.CustomerUserDetailsService"/>
</property>
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="customerCustomAuthenticationProvider"/>
<sec:authentication-provider ref="employeeCustomAuthenticationProvider"/>
</sec:authentication-manager>
</beans>
Here's my updated configuration. It has to be some really small tweak I need to do to prevent the authentication fall back but I can't seem to figure it out now.
Thank you.
UPDATE - SOLUTION to #Ritesh's technique
Okay, I think I have solved the problem here. Instead of having EmployeeCustomAuthenticationProvider to rely on the default UsernamePasswordAuthenticationToken, I created EmployeeUsernamePasswordAuthenticationToken for it, just like the one I created CustomerUsernamePasswordAuthenticationToken for CustomerCustomAuthenticationProvider. These providers will then override the supports():-
CustomerCustomAuthenticationProvider class
#Override
public boolean supports(Class<? extends Object> authentication) {
return (CustomerUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
EmployeeCustomAuthenticationProvider class
#Override
public boolean supports(Class<? extends Object> authentication) {
return (EmployeeUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
MyAuthenticationFilter class
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
...
UsernamePasswordAuthenticationToken authRequest = null;
if ("customer".equals(request.getParameter("radioAuthenticationType"))) {
authRequest = new CustomerUsernamePasswordAuthenticationToken(username, password);
}
else {
authRequest = new EmployeeUsernamePasswordAuthenticationToken(username, password);
}
setDetails(request, authRequest);
return super.getAuthenticationManager().authenticate(authRequest);
}
... and WALAA! It works perfectly now after several days of frustration!
Hopefully, this post will be able to help somebody who is doing the same thing as I am here.

You don't need to create /j_spring_security_check_for_employee and /j_security_check_for_customer filterProcessingUrl.
The default one will work just fine with radio button field idea.
In the custom login LoginFilter, you need to create different tokens for employee and customer.
Here are the steps:
Use default UsernamePasswordAuthenticationToken for employee login.
Create CustomerAuthenticationToken for customer login. Extend AbstractAuthenticationToken so that its class type is distinct from UsernamePasswordAuthenticationToken.
Define a custom login filter:
<security:http>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="customFormLoginFilter" />
</security:http>
In customFormLoginFilter, override attemptAuthentication as follows (pseudo code):
if (radiobutton_param value employee) {
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
setDetails(whatever);
return getAuthenticationManager().authenticate(authRequest);
} else if (radiobutton_param value customer) {
CustomerAuthenticationToken authRequest = new CustomerAuthenticationToken(username, password);
setDetails(whatever);
return getAuthenticationManager().authenticate(authRequest);
}
Override supports method in EmployeeCustomAuthenticationProvider to support UsernamePasswordAuthenticationToken.
Override supports method in CustomerCustomAuthenticationProvider to support CustomerAuthenticationToken.
#Override
public boolean supports(Class<?> authentication) {
return (CustomerAuthenticationToken.class.isAssignableFrom(authentication));
}
Use both providers in authentication-manager:
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref='employeeCustomAuthenticationProvider ' />
<security:authentication-provider ref='customerCustomAuthenticationProvider ' />
</security:authentication-manager>

You can define several AuthenticationProcessingFilter filters. Each of them can have different URL like /j_security_check_for_employee and /j_security_check_for_customer. Here is example of the security application context that demonstrates this idea:
<bean id="myfilterChainProxy" class="org.springframework.security.util.FilterChainProxy">
<security:filter-chain-map pathType="ant">
<security:filter-chain pattern="/**" filters="authenticationProcessingFilterForCustomer, authenticationProcessingFilterForEmployee, ..." />
</security:filter-chain-map>
</bean>
<bean id="authenticationProcessingFilterForCustomer" class="org.springframework.security.web.authentication.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManagerForCustomer"/>
<property name="filterProcessesUrl" value="/j_security_check_for_customer"/>
</bean>
<bean id="authenticationProcessingFilterForEmployee" class="org.springframework.security.web.authentication.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManagerForEmployee"/>
<property name="filterProcessesUrl" value="/j_security_check_for_employee"/>
</bean>
<bean id="authenticationManagerForCustomer" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<bean class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<ref bean="customerUserDetailsServiceThatUsesDB"/>
</property>
</bean>
</list>
</property>
</bean>
<bean id="authenticationManagerForEmployee" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<bean class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService">
<ref bean="employeeUserDetailsServiceThatUsesLDAP"/>
</property>
</bean>
</list>
</property>
</bean>
As you can see, in this scenario you have also different UserDetailServices - for DB auth and LDAP.
I think it's good idea to have different auth URLs for customers and employee (especially if they use different authentication strategies). You can even have different login pages for them.

For Java Configuration reference
As i keen to write here java configuration way of implementing the same technique to help people who is not familiar with xml configuration but i don't want to hijack this thread beauty with such a long answer of java configuration code.
People who wants to achieve the same with java configuration(Annotation based) can refer my self answered question link is given below and also you can find my github repository link for the code in the answer.
For Annotation based configuration code refer my answer here
Multiple AuthenticationProvider with different UsernamePasswordAuthToken to authenticate different login forms without fallback authentication

You can store this information in DB. For example you can have column called ldap_auth in Users table. You can look at my other answer (as an example):
Spring login form example
If you carefully look at UserService class, you will notice, that I actually test this LDAP flag and take user password either from LDAP or database.

it's me again :) Can you try to use filters like this:
<sec:http auto-config="true">
...
<sec:custom-filter ref="authenticationProcessingFilterForCustomer" after="FIRST"/>
<sec:custom-filter ref="authenticationProcessingFilterForEmployee" after="FIRST"/>
</sec:http>
instead of defining bean springSecurityFilterChain.

Related

Pre-authentication in Spring Security + LDAP

Here is what I want to achieve:
I am using Websphere and I want to rely on the container to do the authentication (using Kerberos+SPNEGO). When it come to Spring Security, I want to rely on the pre-authentication, and use LDAP to retrieve user details (roles etc) for authorization checking.
Here is the part of Spring app context config I have (tried to only include related parts)
<s:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" proxy-target-class="true" />
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<s:filter-chain-map path-type="ant">
<s:filter-chain pattern="/**"
filters="securityContextPersistenceFilter,preAuthenticatedFilter" />
</s:filter-chain-map>
</bean>
<s:http use-expressions="true" create-session="stateless" auto-config="true">
<!--
<s:http-basic />
-->
</s:http>
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<property name='securityContextRepository'>
<bean class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
<property name='allowSessionCreation' value='true' />
</bean>
</property>
</bean>
<bean id="preAuthenticatedFilter"
class="org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<s:authentication-manager alias="authenticationManager">
<s:authentication-provider ref="preAuthenticatedAuthenticationProvider" />
</s:authentication-manager>
<bean id="preAuthenticatedAuthenticationProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService" >
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper" >
<property name="userDetailsService" ref="userDetailsService" />
</bean>
</property>
</bean>
<bean id="userDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService" >
<constructor-arg index="0" ref="ldapUserSearch"/>
<constructor-arg index="1" ref="ldapAuthoritiesPopulator"/>
<property name="userDetailsMapper" >
<bean class="com.foo.MyUserDetailsMapper" />
</property>
</bean>
<bean id="ldapContextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<!-- some setting skipped here -->
</bean>
<bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<!-- some setting skipped here -->
</bean>
<bean id="ldapAuthoritiesPopulator"
class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<!-- some setting skipped here -->
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.simple.SimpleLdapTemplate">
<constructor-arg ref="ldapContextSource" />
</bean>
It mostly worked, I can see correct user name and role coming in for my custom UserDetailsMapper (com.foo.MyUserDetailsMapper) which comes from LDAP, and inside that I am returning a new UserDetails with updated roles.
The problem is, in my controller, when I tried to do
SecurityContextHolder.getContext().getAuthentication()
It is returning null. (For which works before I change to pre-authentication)
Is there anything I missed?
Found the problem. Sorry that is mostly because of my own implementation fault which is not visible in the question itself.
My custom UserDetails impl is incorrectly having getEnabled() returning false. In LdapAuthenticationProvider, it is working fine as there is no checking on the user status.
However, in PreAuthenticatedAuthenticationProvider, by default there is a UserDetailsChecker which checks the status of user, for which getEnabled() returning false will cause the user details checker to fail silently, and causing authentication not populated to SecurityContext (i.e. treating that account as not authenticated)
Although it is mostly my implementation issue, I think still worth leaving here as a reference for difference of LdapAuthenticationProvider and PreAuthenticatedAuthenticationProvider

Spring security duplicated RemeberMeProvider

I build an application with GWT on client side (in future I rewrite interface to JS) and duplicate functionality by REST requests. Based on Spring framework.
Now I need to implement authorization.
Here is my security-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
..........>
<security:http auto-config="false">
<security:form-login login-page="/login.html"/>
<security:intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/mainGWT.html**" access="ROLE_USER"/>
<security:remember-me services-ref="rememberMeServices" />
</security:http>
<bean id="userDetailsService" class="com.damintsev.servlet.UserDetailsServiceImpl"/>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
<constructor-arg name="key" value="rock"/>
</bean>
<context:component-scan base-package="com.login"/>
<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="rock"/>
<property name="tokenValiditySeconds" value="5000"/>
<property name="alwaysRemember" value="true"/>
</bean>
<bean id="rememberMeFilter" class=
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="providerManager" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
<ref bean="org.springframework.security.authentication.RememberMeAuthenticationProvider#0"/>
</list>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
</security:authentication-manager>
But rememberMe services didnt work. So I start to debug this and I found that rememberMeAuthenticationProvider initializes twise!!
At first time initializes with name org.springframework.security.authentication.RememberMeAuthenticationProvider#0 and strange (autogenerated maybe) key.
The second provider initializes with propertly name rememberMeAuthenticationProvider with propertly key.
Later there is a problem because key in TokenBasedRememberMeServices didnt match with rememberMeAuthenticationProvider.
But when I change name of bean to org.springframework.security.authentication.RememberMeAuthenticationProvider#0 it works fine.
What am I doing wrong ? Who initialize provider twise ?
<spring.version>3.2.3.RELEASE</spring.version>
Second part of question: I dont understand how set RememberMe cookie to client so I write class. And if from client I receive remember = true I call that methood. And It works. If You provide some exmples with remeber me + REST it will helps.
#Component
public class Security {
#Autowired
private RememberMeServices rememberMeServices;
public void remeberMe(HttpServletRequest request, HttpServletResponse response, Authentication authentication1) {
rememberMeServices.setAlwaysRemember(true);
rememberMeServices.loginSuccess(request, response, authentication1);
}
}
You have a bean called rememberMeAuthenticationProvider and you are also using the namespace remember-me element which will also create one, hence the duplicate. You've also declared a filter which won't be used unless you actually insert it into the filter chain.
Either remove the namespace element and declare all the beans fully yourself, or stick with the namespace and let it do the work. If you want to customize the RememberMeServices you can you can retain that and the services-ref as you have it, but it's not clear from your question why you need to customize things.
If a remember-me cookie is sent by the client then the server will process it regardless of the type of client. How it is set in the first place depends on the login mechanism. Form login processing will automatically invoke the RememberMeServices and set the cookie if appropriate.

Authentication with Spring oAuth2.0

I want to authenticate users on my website by facebook/twitter accounts. But in spring examples, spring ask me to login first, and only after that, when user already has a role, I can connect to facebook and get some data. Even if there are no permissions at facebookData controller (intercept-url pattern="/**" access="permitAll") spring redirects me to the login page.
Does it mean, that OpenAuth can't be used for authentication
anonymouse user?
My spring-security:
<http use-expressions="true">
<form-login login-page="/login" />
<intercept-url pattern="/**" access="permitAll" />
<logout />
<custom-filter ref="oauth2ClientFilter" after="EXCEPTION_TRANSLATION_FILTER" />
</http>
<oauth:client id="oauth2ClientFilter" />
<oauth:resource id="facebook" type="authorization_code" client-id="..." client-secret="..." authentication-scheme="query"
access-token-uri="https://graph.facebook.com/oauth/access_token" user-authorization-uri="https://www.facebook.com/dialog/oauth" token-name="oauth_token" client-authentication-scheme="form" scope="email"/>
<bean id="facebookController" class="org.springframework.security.oauth.examples.tonr.mvc.FacebookController" />
<oauth:rest-template resource="facebook" id="fasebookRestTemplate">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<bean class="org.springframework.http.MediaType">
<!--facebook sends its json as text/javascript for some reason -->
<constructor-arg value="text" />
<constructor-arg value="javascript" />
</bean>
<bean class="org.springframework.http.MediaType">
<constructor-arg value="application" />
<constructor-arg value="json" />
</bean>
</list>
</property>
</bean>
</list>
</property>
</oauth:rest-template>
And facebook controller:
#Autowired
private RestOperations facebookRestTemplate;
#RequestMapping("/facebook/info")
public String photos(Model model) throws Exception {
ObjectNode resultNode = facebookRestTemplate.getForObject("https://graph.facebook.com/me/friends", ObjectNode.class);
ArrayNode data = (ArrayNode) resultNode.get("data");
ArrayList<String> friends = new ArrayList<String>();
for (JsonNode dataNode : data) {
friends.add(dataNode.get("name").getTextValue());
}
model.addAttribute("friends", friends);
return "facebook";
}
There is a cool library that does exactly this: https://github.com/socialsignin/spring-social-security
I have successfully made use of it - where all the user has to do is connect to Facebook to get authenticated. To create a new user upon the very first connect, you simply extend SpringSocialSecurityConnectionSignUp, overriding the execute method. The Github page explains nicely what you need to do. It also has some sample projects which are really useful as well.
The project is in Maven. Have fun!

How do I use a custom authorities populator with Spring Security and the ActiveDirectoryLdapAuthenticationProvider?

I've successfully connected to Active Directory through LDAP to authenticate, and with the following in my ldap.xml I've called my custom authorities populator:
<bean id="ldapAuthenticationProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg ref="ldapBindAuthenticator"/>
<constructor-arg ref="ldapAuthoritiesPopulator"/>
</bean>
<bean id="ldapBindAuthenticator"
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="ldapServer"/>
<property name="userSearch" ref="ldapSearch"/>
</bean>
<bean id="ldapSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg value="CN=Users"/>
<constructor-arg value="(sAMAccountName={0})"/>
<constructor-arg ref="ldapServer"/>
</bean>
<bean id="ldapAuthoritiesPopulator"
class="my.project.package.ActiveDirectoryLdapAuthoritiesPopulator"/>
<bean id="ldapServer"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://192.168.0.2/dc=test,dc=server"/>
<property name="userDn" value="ldap#test.server"/>
<property name="password" value="ldap"/>
<property name="baseEnvironmentProperties">
<map>
<entry key="java.naming.referral">
<value>follow</value>
</entry>
</map>
</property>
</bean>
This works fine, and I can ascertain the user's authorization based on her group membership, but I would rather do this through the built-in Active Directory LDAP Authentication Provider:
<bean id="ldapAuthenticationProvider"
class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<constructor-arg value="test.server"/>
<constructor-arg value="ldap://192.168.0.2:389"/>
<property name="convertSubErrorCodesToExceptions" value="true"/>
</bean>
The problem with the above is that my custom authorities populator is (obviously) not called, so while I can authenticate my users (which works with the above), I am left without the groups (which I need to determine authorization).
I feel like this is a simple question, but for the life of me I cannot find an answer here or anywhere else. Do I have to extend the ActiveDirectoryLdapAuthenticationProvider class, and call my authorities populator from there?
(Thanks for all the help this site has given me for several years running; the effectiveness of this site can be gauged by the fact that I've only recently bothered to create an account, and this is my first question. Thanks in advance for your help.)
Spring's ActiveDirectoryLdapAuthenticationProvider class is final, so my only real option (I'll entertain better ones if there are any takers) was to fork the class. I copied and pasted its contents, refactored slightly, and removed the final designation. Then, I created a separate subclass of the forked class, overriding the loadUserAuthorities() method, and added my own code for building a permissions mask.
I was then able to edit my ldap.xml file as follows:
<bean id="ldapAuthenticationProvider"
class="my.project.package.OverrideActiveDirectoryLdapAuthenticationProvider">
<constructor-arg value="test.server"/>
<constructor-arg value="ldap://192.168.0.2:389"/>
<property name="convertSubErrorCodesToExceptions" value="true"/>
</bean>
For any other n00bs like me, the subclass looks like this:
public class OverrideActiveDirectoryLdapAuthenticationProvider extends TestActiveDirectoryLdapAuthenticationProvider {
//my assignments
public OverrideActiveDirectoryLdapAuthenticationProvider(String domain,
String url) {
super(domain, url);
}
#Override
protected Collection<? extends GrantedAuthority> loadUserAuthorities(DirContextOperations userData, String username, String password) {
//original code with my own additions
//in my case, I injected code into the for(group : groups) loop
}
Worked like a charm.
Much thanks to zagyi for the assistance.

Retrieve password of current user from spring-security [duplicate]

This question already has answers here:
UserDetails getPassword returns null in spring security 3.1. How to get password of currently logged in user?
(3 answers)
Closed 8 years ago.
I'm using spring-security with HTTP Basic Auth to secure a java webapp. In my webapp I need to get the current user's username and password to further authenticate them against another service. I can get hold of the username, but not the password.
I've tried using SecurityContextHolder.getContext().getAuthentication() to access this information as suggested here How can I get plaintext password from spring-security? but the password is returned as null.
How can I get hold of the password?
Thanks.
This is my applicationContext-security.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<sec:http authentication-manager-ref='authenticationManager'>
<sec:intercept-url pattern="/spring/**" access="ROLE_USER" />
<sec:http-basic />
</sec:http>
<bean id="basicAuthenticationFilter"
class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
</bean>
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
<property name="realmName" value="Announcements Rest Realm" />
</bean>
<bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref local="authProvider" />
</list>
</property>
</bean>
<bean id="authProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService" />
</bean>
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<list>
<sec:filter-chain pattern="/spring/**" filters="basicAuthenticationFilter" />
</list>
</constructor-arg>
</bean>
<sec:user-service id="userDetailsService">
<sec:user name="admin" password="**" authorities="ROLE_USER, ROLE_ADMIN" />
<sec:user name="user" password="**" authorities="ROLE_USER" />
</sec:user-service>
I've figured it out.
I've changed my authentication manager config to use the authentication-manager element and added the attribute there:
<sec:authentication-manager alias="authenticationManager" erase-credentials="false">
<sec:authentication-provider ref="authProvider" />
</sec:authentication-manager>
I can then use SecurityContextHolder.getContext().getAuthentication().getCredentials() in my controller class to get the password.
Thanks for your help though Piotrek De
Do you use remember me/run as/switch user? In such case, password is null (as described in a thread you've mentioned). Also, probably you have to use basic form authentication, (post request to the j_spring_security_check)
If you are setting the entire details of the current user including password in session object, then you can get the password from that session object.
a better way is to implement your 'userDetailsService'. so you can control how the UserDetails could be inited when doing authentication. i implemented a form-based authentication and wrote my self-defined user service,
#Service("userService")
public class UserServiceImpl extends GenericService<User> implements UserDetailsService,UserService{
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
// Declare a null Spring User
UserDetails userDetails = null;
// Search database for a user that matches the specified username
// You can provide a custom DAO to access your persistence layer
// Or use JDBC to access your database
// DbUser is our custom domain user. This is not the same as Spring's User
User dbUser=this.findUserByUserName(username);
if (dbUser == null) {
throw new UsernameNotFoundException("HiMVC Security:: Error in retrieving user(username=" + username + ")");
}
userDetails = new org.springframework.security.core.userdetails.User(
dbUser.getUserName(),
dbUser.getPassword(),//here you can put a clear text password
true,
true,
true,
true,
loadUserAuthorities(username));
return userDetails;
}

Categories

Resources