I used Spring Security 3.x to handle user authentication for my projects, and so far it has worked flawlessly.
I recently received requirements for a new project. This project requires two sets of user authentication: one for authenticating employees using LDAP, and the other for authenticating the client against the database. I am a little puzzled by how to configure this in Spring Security.
My initial idea was to create a login screen that has the following fields: -
- radio button - 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 validate them for LDAP, otherwise the credentials will be authenticated against the database. However, the problem is that the form will be submitted to /j_spring_security_check and I will not be able to submit the switch field to my implemented custom authentication provider. My initial thought is that I probably need two submit URLs, not the default /j_spring_security_check . Each URL will be handled by different authentication providers, but I'm not sure how to configure it in Spring Security.
I know in Spring security, I can configure return authentication, for example, if LDAP authentication fails, then it will return to database authentication, but this is not what I am shooting in this new project.
Can someone share how exactly should I configure this in Spring Security 3.x?
Thank.
UPDATE - 01-28-2011 - Technique @EasyAngel
I am trying to do the following: -
- Login for employee form is sent to
/j_spring_security_check_for_employee - Login to the client form is sent to
/j_spring_security_check_for_customer
The reason I want 2 different logins is to let me handle authentication differently based on the user, instead of having to authenticate with a return. Perhaps the employee and client have the same user ID, in my case.
I included the idea of ββ@EasyAngel, but I need to replace some obsolete classes. The problem I'm currently facing is neither filtering processes, nor URLs that look like URLs in Spring Security because I keep getting Error 404: SRVE0190E: File not found: /j_spring_security_check_for_employee . The feeling of my intestines springSecurityFilterChain bean is not connected properly, so my custom filters are not used at all.
By the way, I use WebSphere, and I have the com.ibm.ws.webcontainer.invokefilterscompatibility=true property set on the server. I can hit default /j_spring_security_check without problems.
Here is my full 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 start generosity here because I canβt get this work to work for several days ... disappointment is the word. I hope someone points out the problem (s), or if you can show me a better or cleaner way to handle this (in code).
I am using Spring Security 3.x.
Thank.
UPDATE 01-29-2011 - @Ritesh appliances
Well, I managed to get @Ritesh's approach to work very close to what I wanted. I have a radio alarm clock that allows the user to choose whether they are a client or an employee. This approach seems to work quite well, with one problem ...
- If an employee is registered with access rights, they are allowed in ... WORK AS EXPECTED .
- If an employee logs in with incorrect credentials, they are not allowed in ... WORK AS EXPECTED .
- If the client is registered with access rights, they are allowed in ... WORK AS EXPECTED .
- If the client registers with the wrong credentials, authentication returns to the employee check ... DOES NOT WORK . This is dangerous because if I select the client authentication and click on the credentials of the employee, it will also allow the user, 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 is my updated configuration. It must be some very small setting that I need to do to prevent authentication, but I canβt figure it out right now.
Thank.
UPDATE - SOLUTION to @Ritesh Method
Ok, I think I solved the problem here. Instead of having EmployeeCustomAuthenticationProvider rely on the default UsernamePasswordAuthenticationToken , I created an EmployeeUsernamePasswordAuthenticationToken for it, like the one I created CustomerUsernamePasswordAuthenticationToken for CustomerCustomAuthenticationProvider . These providers then override supports() : -
Class CustomerCustomAuthenticationProvider
@Override public boolean supports(Class<? extends Object> authentication) { return (CustomerUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); }
Class EmployeeCustomAuthenticationProvider
@Override public boolean supports(Class<? extends Object> authentication) { return (EmployeeUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); }
Class MyAuthenticationFilter
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 great after a few days of frustration!
Hope this post can help someone who does the same thing as me here.