Authentication with Spring oAuth2.0 - java

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!

Related

Failing to exclude some URLs from Spring Security protection

We have the following spring security configuration:
<bean id="authenticationSuccessHandler" class="***.JsonAuthenticationSuccessHandler"/>
<bean id="logoutSuccessHandler" class="***.web.security.***UrlLogoutSuccessHandler">
<property name="redirectStrategy" ref="noRedirectStrategy"/>
</bean>
<bean id="authenticationFailureHandler"
class="***.web.security.***UrlAuthenticationFailureHandler"/>
<bean id="httpStatusEntryPoint" class="org.springframework.security.web.authentication.HttpStatusEntryPoint">
<constructor-arg value="UNAUTHORIZED"/>
</bean>
<security:http auto-config="true" use-expressions="false" entry-point-ref="httpStatusEntryPoint">
<security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrentSessionFilter"/>
<security:form-login
authentication-success-handler-ref="authenticationSuccessHandler"
authentication-failure-handler-ref="authenticationFailureHandler"
/>
<security:intercept-url pattern="/api/**"/>
<security:anonymous enabled="false"/>
<security:logout logout-url="/logout" delete-cookies="JSESSIONID,sessionId"
success-handler-ref="logoutSuccessHandler"
/>
<security:csrf disabled="true"/>
<security:session-management session-authentication-strategy-ref="sessionAuthenticationStrategy"/>
</security:http>
<bean id="concurrentSessionFilter" class="***.***ConcurrentSessionFilter">
<constructor-arg ref="***SessionRegistry"/>
<constructor-arg ref="errorController"/>
</bean>
<bean id="sessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<constructor-arg>
<list>
<ref bean="registerSessionAuthenticationStrategy"/>
<ref bean="concurrentSessionControlAuthenticationStrategy"/>
</list>
</constructor-arg>
</bean>
<bean id="registerSessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
<constructor-arg name="sessionRegistry" ref="***SessionRegistry" />
</bean>
<bean id="concurrentSessionControlAuthenticationStrategy" class="***.web.security.***ConcurrentSessionControlAuthenticationStrategy">
<constructor-arg name="sessionRegistry" ref="***SessionRegistry" />
<constructor-arg name="logoutService" ref="logoutService"/>
<property name="maximumSessions" value="1" />
</bean>
<!-- enable spring security annotation processing -->
<security:global-method-security secured-annotations="enabled"/>
<bean id="***LdapAuthenticationProvider" class="***.web.***LdapAuthProvider">
<property name="url" value="${ldap.url}"/>
<property name="filter" value="${ldap.filter}"/>
<property name="domain" value="${ldap.domain}"/>
<property name="dn" value="${ldap.dn}"/>
<property name="ldapEnabled" value="${ldap.enable}"/>
</bean>
<security:authentication-manager>
<security:authentication-provider ref="***LdapAuthenticationProvider"/>
<security:authentication-provider user-service-ref="***UserDetailsService"/>
</security:authentication-manager>
<bean id="usersResource" class="org.springframework.core.io.ClassPathResource">
<constructor-arg value="/users.properties" />
</bean>
<util:property-path id="usersResourceFile" path="usersResource.file" />
<bean id="***UserDetailsService" class="***.web.security.***InMemoryUserDetailsManager">
<constructor-arg index="0" ref="usersResourceFile"/>
</bean>
I tried different ways But I can not find a way to exclude some specific URLs from authentication.
For example:
/api/url/available/without/login
should be available even user is not logged in.
P.S.
I have tried to apply this answer, but it doesn't work for me:
https://stackoverflow.com/a/5382178/2674303
UPD
I have tired
....
<bean id="httpStatusEntryPoint" class="org.springframework.security.web.authentication.HttpStatusEntryPoint">
<constructor-arg value="UNAUTHORIZED"/>
</bean>
<security:http pattern="/api/url/available/without/login" security="none"/>
<security:http auto-config="true" use-expressions="false" entry-point-ref="httpStatusEntryPoint">
....
but when I try to use - this url still locked and I get 401
because this code:
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
String name = authentication != null ? authentication.getName() : "";
throw new BadCredentialsException("Could not find user " + name);
}
throws exception
You just need to add a "default" http interceptor:
<security:http xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/" access="permitAll()"/>
<anonymous/>
<csrf disabled="true"/>
</security:http>
after your current security:http tag. It will handle all requests, which were not handled by the first http construction.

HTTP Basic with Spring Security XML configuration doesn't use HttpBasicConfigurer

It seems that XML configuration and Java are not performing the same tasks in Spring Security, regarding the HTTP Basic configuration.
When using the following Java configuration:
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
A HttpBasicConfigurer is used in order to use a different EntryPoint, when the request HTTP header X-Requested-With is XMLHttpRequest.
When using the configuration
<s:http use-expressions="true" create-session="ifRequired">
<s:intercept-url pattern='/**' access='isAuthenticated()' />
<s:http-basic />
<s:http />
The HTTPBassicConfigurer is not used.
Anybody knows how to add it using the XML configuration ?
The final solution based on the remarks provided by the people in this post, is that it is not possible to use HTTPBasicConfigurer with XML configuration. But there are other ways to perform almost the same thing that is implemented now in HTTPBasicConfigurer. My final solution used is mainly based on the remarks provided by Lea:
<s:http use-expressions="true" create-session="ifRequired" >
<s:intercept-url pattern='/**' access='isAuthenticated()' />
<s:http-basic entry-point-ref="entryPoint" />
</s:http>
<bean id="entryPoint"
class="org.springframework.security.web.authentication
.DelegatingAuthenticationEntryPoint">
<constructor-arg>
<map>
<entry key="hasHeader('X-Requested-With','XMLHttpRequest')"
value-ref="ajaxEntyPoint" />
</map>
</constructor-arg>
<property name="defaultEntryPoint" ref="defaultEntryPoint"/>
</bean>
<bean id="ajaxEntyPoint"
class="org.springframework.security.web.authentication.HttpStatusEntryPoint">
<constructor-arg name="httpStatus"
value="#{T(org.springframework.http.HttpStatus).UNAUTHORIZED}"/>
</bean>
<bean id="defaultEntryPoint"
class="org.springframework.security.web.authentication.www
.BasicAuthenticationEntryPoint">
<property name="realmName" value="My webservices"/>
</bean>
Basic authentication parameters can be declared explicitly with basic authentication filter:
<security:http use-expressions="true" entry-point-ref="entryPoint" authentication-manager-ref="authManager">
<security:custom-filter ref="advancedBasicFilter" position="BASIC_AUTH_FILTER"/>
<security:intercept-url pattern="/info/**" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()"/>
</security:http>
<bean id="advancedBasicFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<constructor-arg name="authenticationEntryPoint" ref="entryPoint"/>
<constructor-arg name="authenticationManager" ref="authManager"/>
</bean>
<bean id="entryPoint" class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
<property name="realmName" value="My Realm"/>
</bean>
<security:authentication-manager id="authManager">
<security:authentication-provider user-service-ref="myOwnUserService" />
</security:authentication-manager>
If you are using xml config with the namespace, you do not use a HTTPBasicConfigurer, but attributes of the <http-basic> tag.
Extract from Spring Security Reference Manual Appendix The security namespace about <http-basic> tag :
Attributes
authentication-details-source-ref : Reference to an AuthenticationDetailsSource which will be used by the authentication filter
entry-point-ref Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter

CAS + Spring Security Detect localhost

I am trying to implement an application with Spring security and CAS, it works fine on localhost but when I try to access it from an outside machine it and the application needs authentication it redirect to localhost too.
meaning
I access the application using https://172.16.1.50:8443/isxannouncements/
and when it needs authentication it should go to https://172.16.1.50:8443/cas/login/
but instead it goes to https://localhost:8443/isxannouncements/
which ofcourse breaks the application flow.
my config is
security-cas.xml
<bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="https://localhost:8443/isxannouncements/login"/>
</bean>
<!--
Allows changing where the CAS Server and CAS Service are easily
by specifying System Arguments or replacing the values only in one place.
Could also use external properties file -->
<context:property-placeholder
system-properties-mode="OVERRIDE" properties-ref="environment"/>
<util:properties id="environment">
<prop key="cas.service.host">localhost:8443</prop>
<prop key="cas.server.host">localhost:8443</prop>
</util:properties>
<!-- sends to the CAS Server, must be in entry-point-ref of security.xml -->
<bean id="casEntryPoint"
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="serviceProperties" ref="serviceProperties"/>
<property name="loginUrl" value="https://localhost:8443/cas/login" />
</bean>
<!-- authenticates CAS tickets, must be in custom-filter of security.xml -->
<bean id="casFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="filterProcessesUrl" value="/login"/>
</bean>
<bean id="casAuthProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="ticketValidator" ref="ticketValidator"/>
<property name="serviceProperties" ref="serviceProperties"/>
<property name="key" value="isxannouncements"/>
<property name="authenticationUserDetailsService" ref="DBUserServiceDetails"/>
<property name="statelessTicketCache" ref="statelessTicketCache"/>
</bean>
<bean id="statelessTicketCache" class="org.springframework.security.cas.authentication.EhCacheBasedTicketCache">
<property name="cache">
<bean class="net.sf.ehcache.Cache"
init-method="initialise" destroy-method="dispose">
<constructor-arg value="casTickets"/>
<constructor-arg value="50"/>
<constructor-arg value="true"/>
<constructor-arg value="false"/>
<constructor-arg value="3600"/>
<constructor-arg value="900"/>
</bean>
</property>
</bean>
<bean id="ticketValidator" class="org.jasig.cas.client.validation.Saml11TicketValidator">
<constructor-arg value="https://localhost:8443/cas" />
<property name="encoding" value="utf8" />
</bean>
<!-- Handles a Single Logout Request from the CAS Server must be in custom-filter of security.xml -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
and my security.xml
<security:http pattern="/resources/images" security="none"/>
<security:http use-expressions="true" entry-point-ref="casEntryPoint">
<security:intercept-url pattern="/login/*"
access="permitAll"/>
<security:intercept-url pattern="/resources/**"
access="permitAll"/>
<security:intercept-url pattern="/logout"
access="permitAll"/>
<security:intercept-url pattern="/errors/**"
access="permitAll"/>
<security:intercept-url pattern="/approve-announcement**"
access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/delete-announcement**"
access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/edit-announcement**"
access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/admin/**"
access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/META-INF"
access="hasRole('ROLE_USER')"/>
<security:access-denied-handler error-page="/errors/403"/>
<security:custom-filter ref="singleLogoutFilter" before="LOGOUT_FILTER"/>
<security:custom-filter ref="casFilter" position="CAS_FILTER"/>
<security:port-mappings>
<security:port-mapping http="8080" https="8443"/>
</security:port-mappings>
<security:logout logout-url="/logout"
logout-success-url="https://localhost:8443/cas/logout"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthProvider" />
</security:authentication-manager>
how to fix this ??
Ok I found a workaround but I did not test it properly,
Inspired by this answer I did the following
since I have two domains that the user might use to access my application, the only way to get the domain the user used was to get from the request and then return it to the security provider.
I created a bean named serviceProperties and used it instead of the serviceproperties of spring, and I overrided the method of getService to return the service based on the domain name that the user to access the application.
Then I made this bean available at Web Application Context, and I passed in the session, I had already extracted the domain from the Request and put it in the Session.
So when the CasAuthenticationEntryPoint tries to get the service, I pass the service URL that I created from the session appended to it is the service name.
We handle this with property files. Anything that is specific to a certain environment (i.e. your local machine versus your test server) should be in a property file.
For example, create properties files for each environment with something like this:
localhost.properties:
cas.service.url=http://localhost/login
test.properties:
cas.service.url=http://mytestserver/login
And then configure spring security with the value from the properties file instead of directly as you have it above:
Your build process would then have a target for each environment to shuffle the appropriate files into place in the final artifact.
CAS works under domain. so you should use cas.example.com and define it in the cas.properties

Configuring Spring Security 3.x to have multiple entry points

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.

how to create spring cas login link

I have Spring Security 3.0.3 with CAS. Some of my conf follows:
<security:http entry-point-ref="casAuthenticationEntryPoint" auto-config="true" >
<security:intercept-url pattern="/*/secure/**" access="ROLE_USER" />
<security:custom-filter position="CAS_FILTER" ref="casAuthenticationFilter" />
<security:anonymous enabled="false"/>
<security:logout invalidate-session="true" logout-url="/logout" logout-success-url="/web/auth?logout" />
</security:http>
<bean id="casAuthenticationEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="${cas.app.url}/login"></property>
<property name="serviceProperties" ref="serviceProperties"></property>
</bean>
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="${application.stack.hostname}/ctx/j_spring_cas_security_check" />
<property name="sendRenew" value="false" />
</bean>
If I access some of the secured content I get redirected to CAS login. After authentication I'm redirected back to the secure url I tried to access.
On every page I need to add "Log in" link that does the same thing. Where should this link point to? I tried ${cas.app.url}/login?service=${application.stack.hostname}/ctx/j_spring_cas_security_check but that does not seem to work.
Of course all the properties are replaced with appropriate full hostnames :)
It can point to any URL matching the pattern <security:intercept-url pattern="/*/secure/**" access="ROLE_USER" />
If I understand what you are looking for you should just be able to point them to ${cas.app.url}/login. Leave out the service parameter. This will take them to the login page of your cas server which will authenticate them to all of your secure services.

Categories

Resources