I have an existing Java/Spring/Hibernate webapp, with a classic database authentication.
I just migrated it with success to a Crowd SSO platform.
Everything works as expected, but now I would like to configure Spring Security to fallback to my previous authentication system if the Crowd server is down.
I never configured such cascading authentication, and what I read with google didn't help me so far. Do you know how I can achieve that?
Here is my Spring security configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns="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.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd"
xmlns:util="http://www.springframework.org/schema/util"
default-autowire="byName">
<http entry-point-ref="crowdAuthenticationProcessingFilterEntryPoint">
<intercept-url pattern="/**/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**/logout" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**/login.html" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/admin/**" access="ROLE_ADMINISTRATOR"/>
<intercept-url pattern="/**" access="ROLE_ADMINISTRATOR"/>
<custom-filter position="FORM_LOGIN_FILTER" ref="authenticationProcessingFilter"/>
<custom-filter position="LOGOUT_FILTER" ref="logoutFilter"/>
</http>
<!-- My previous authentication filter -->
<beans:bean id="authenticationFilter"
class="my.package.security.CustomAuthenticationProcessingFilter">
<beans:property name="authenticationManager" ref="formAuthenticationManager"/>
<beans:property name="filterProcessesUrl" value="/login"/>
<beans:property name="continueChainBeforeSuccessfulAuthentication" value="false"/>
<beans:property name="postOnly" value="true"/>
<beans:property name="authenticationSuccessHandler" ref="authenticationHandler"/>
<beans:property name="authenticationFailureHandler" ref="authenticationHandler"/>
</beans:bean>
<beans:bean id="authenticationHandler" class="my.package.security.CustomAuthenticationHandler">
<beans:property name="alwaysUseDefaultTargetUrl" value="false"/>
</beans:bean>
<beans:bean id="customAuthenticationProvider"
class="my.package.security.MyDaoAuthenticationProvider">
<beans:property name="SaltSource">
<beans:bean class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<beans:property name="userPropertyToUse" value="salt"/>
</beans:bean>
</beans:property>
</beans:bean>
<!-- Crowd config -->
<beans:bean id="crowdUserDetailsService" class="my.package.security.CustomCrowdUserDetailsServiceImpl">
<beans:property name="authenticationManager" ref="crowdAuthenticationManager"/>
<beans:property name="groupMembershipManager" ref="crowdGroupMembershipManager"/>
<beans:property name="userManager" ref="crowdUserManager"/>
<beans:property name="authorityPrefix" value=""/>
<beans:property name="userController" ref="userController"/>
</beans:bean>
<beans:bean id="crowdAuthenticationProvider" class="com.atlassian.crowd.integration.springsecurity.RemoteCrowdAuthenticationProvider">
<beans:constructor-arg ref="crowdAuthenticationManager"/>
<beans:constructor-arg ref="httpAuthenticator"/>
<beans:constructor-arg ref="crowdUserDetailsService"/>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref='crowdAuthenticationProvider' />
</authentication-manager>
<beans:bean id="crowdAuthenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg value="/login.html"/>
</beans:bean>
<beans:bean id="authenticationProcessingFilter" class="com.atlassian.crowd.integration.springsecurity.CrowdSSOAuthenticationProcessingFilter">
<beans:property name="httpAuthenticator" ref="httpAuthenticator"/>
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="filterProcessesUrl" value="/login"/>
<beans:property name="authenticationFailureHandler">
<beans:bean class="com.atlassian.crowd.integration.springsecurity.UsernameStoringAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login.html?login_error=1"/>
</beans:bean>
</beans:property>
<beans:property name="authenticationSuccessHandler">
<beans:bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/flexibility.html"/>
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="crowdLogoutHandler" class="com.atlassian.crowd.integration.springsecurity.CrowdLogoutHandler">
<beans:property name="httpAuthenticator" ref="httpAuthenticator"/>
</beans:bean>
<beans:bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<beans:constructor-arg value="/login.html"/>
<beans:constructor-arg>
<beans:list>
<beans:ref bean="crowdLogoutHandler"/>
<beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</beans:list>
</beans:constructor-arg>
<beans:property name="filterProcessesUrl" value="/logout"/>
</beans:bean>
What you need is an authentication manager configured with multiple authentication providers. This
gives an example
Related
I'm using Spring Security 3.2.5 in a GWT application. I needed fine-grained control over security so I used the following configuration instead of element:
<beans:bean id="springSecurityFilterChain"
class="org.springframework.security.web.FilterChainProxy">
<beans:constructor-arg>
<beans:list>
<filter-chain pattern="/css/**" filters="none" />
<filter-chain pattern="/image/**" filters="none" />
<filter-chain pattern="/index.jsp" filters="none" />
<filter-chain pattern="/**/logout" filters="logoutFilter" />
<filter-chain pattern="/**"
filters="securityContextPersistenceFilterWithASCTrue, concurrentSessionFilter, usernamePasswordAuthenticationFilter, exceptionTranslationFilter, filterSecurityInterceptor" />
</beans:list>
</beans:constructor-arg>
</beans:bean>
I've omitted particular filter implementations.
I need to force most of filter-chain's above to use https like when using the tag as in the following example:
<security:intercept-url pattern="/reports" access="ROLE_ADMIN" requires-channel="https"/>
How can I achieve this?
EDIT 1: adding a ChannelProcessingFilter
Following #luke answer I modified my code so a channel filter is in the first position of the filter chain:
<filter-chain pattern="/**"
filters="channelProcessingFilter, securityContextPersistenceFilterWithASCTrue, ..." />
I also added the following beans configuration:
<!-- Ensure https channel -->
<beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
<beans:property name="securityMetadataSource">
<filter-security-metadata-source>
<intercept-url pattern="/**" access="ROLE_USER" />
</filter-security-metadata-source>
</beans:property>
</beans:bean>
<beans:bean id="channelProcessingFilter" class="org.springframework.security.web.access.channel.ChannelProcessingFilter">
<beans:property name="channelDecisionManager" ref="channelDecisionManager"/>
<beans:property name="securityMetadataSource">
<filter-security-metadata-source request-matcher="ant">
<intercept-url pattern="/**" access="REQUIRES_SECURE_CHANNEL"/>
</filter-security-metadata-source>
</beans:property>
</beans:bean>
<beans:bean id="channelDecisionManager" class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl">
<beans:property name="channelProcessors">
<beans:list>
<beans:ref bean="secureChannelProcessor"/>
<beans:ref bean="insecureChannelProcessor"/>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="secureChannelProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor" />
<beans:bean id="insecureChannelProcessor" class="org.springframework.security.web.access.channel.InsecureChannelProcessor" />
Now the problem is I'm getting an infinite loop after submitting my login form via http. Of course it is the case I want to avoid but an infinite loop is not right. This is the relevant log:
DEBUG o.s.s.w.FilterChainProxy 337 - /j_spring_security_check at
position 1 of 6 in additional filter chain; firing Filter:
'ChannelProcessingFilter'
DEBUG o.s.s.w.a.c.ChannelProcessingFilter 134
- Request: FilterInvocation: URL: /j_spring_security_check; ConfigAttributes: [REQUIRES_SECURE_CHANNEL] 2014-10-30 19:47:10,565
DEBUG o.s.s.w.a.c.RetryWithHttpsEntryPoint 55 - Redirecting to:
/j_spring_security_check 2014-10-30 19:47:10,567 DEBUG
o.s.s.w.DefaultRedirectStrategy 36 - Redirecting to
'/j_spring_security_check'
Any ideas please?
You need a ChannelProcesingFilter.
It's best if you just require HTTPS to access all of your site. HTTPS is only really secure if you use it from the start. Ideally also you want to use HSTS to communicate that to clients.
I have a web application which uses spring security for session based logins using username and password authentication with the following security application context xml.
<global-method-security pre-post-annotations="enabled" />
<http pattern="/css/**" security="none" />
<http pattern="/files/**" security="none" />
<http auto-config='true' entry-point-ref="authenticationEntryPoint" access-decision-manager-ref="accessDecisionManager">
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" method="OPTIONS" />
<intercept-url pattern="/login/*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/**" access="REGISTERED" />
<form-login login-page="/login" login-processing-url="/login_security_check" authentication-failure-handler-ref="xxxAuthenticationFailureHandler" authentication-success-handler-ref="xxxAuthenticationSuccessHandler" />
<logout invalidate-session="true" logout-url="/data/logout" success-handler-ref="xxxLogoutSuccessHandler" />
<remember-me key="xxxRem" />
</http>
<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<beans:property name="decisionVoters">
<beans:list>
<beans:ref bean="roleVoter" />
<beans:ref bean="authenticatedVoter" />
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter">
<beans:property name="rolePrefix" value="" />
</beans:bean>
<beans:bean id="authenticatedVoter" class="org.springframework.security.access.vote.AuthenticatedVoter">
</beans:bean>
<beans:bean id="userDetailsService" class="com.xxx.web.security.XXXUserDetailsService">
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref='userDetailsService'>
<password-encoder hash="md5">
<salt-source user-property="username" />
</password-encoder>
</authentication-provider>
</authentication-manager>
<beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg value="/login" />
</beans:bean>
<beans:bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
<beans:property name="defaultEntryPoint" ref="loginUrlAuthenticationEntryPoint" />
</beans:bean>
Now I want to expose my web services for a mobile app, so I am looking to implement OAuth2. I've read the examples provided on github.
I was wondering how these two security flows can co-exist as the intercept url pattern will be same for both the flows?
You would need to change the access="" rules for the resources that are shared between the UI and the OAuth resources. It's not very common for those two to really cross over very much, but I guess for simple apps it might be possible through content negotiation. It's probably easiest to use the SpEL support in XML (or switch to Java config). Example:
<intercept-url pattern="/** access="isFullyAuthenticated() or #oauth2.hasScope('read')"/>
For an alternative approach you could create aliases for your endpoints and protect them with separate filter chains, one for the token and one for cookie-based authentication. Example:
#RequestMapping({ "/user", "/api/user" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
where "/user" is protected as a normal resource (i.e. with WebSecurityConfigurerAdapter in Java config), and "/api/user" is separately configured (i.e. with a ResourceServerConfigurerAdapter in Java config).
I'm trying to have different login error messages such as 'Login Failed' or 'Internal System Error'. Or maybe even the ability to redirect to a different page. I tried to follow what was done here http://www.codemarvels.com/2010/12/spring-security-3-how-to-display-login-errors/ except that I don't have form-login tag, so I wasn't able to set authentication-failure-handler-ref as show on the page. However, I did set authenticationFailureHandler(to use ExceptionMappingAuthenticationFailureHandler) on FORM_LOGIN_FILTER which is a custom class that just extends UsernamePasswordAuthenticationFilter.
What am I missing here?
EDIT-09/19/2013: I did a little bit of debugging, and I'm asking the wrong question. It appears that it's working fine. However, I have multiple authentication providers, and the exception from the first one, is overwritten with the exception from the second one. Basically, I'm trying to simulate DB down exception, which is thrown, but then authentication against Active Directory is attempted which is up, and I just get user not found exc. Hence, I don't get my AuthenticationServiceException.
I guess my question at this point is how do I make the exception from the first one stick without changing the order of providers (the DB providers is used for majority of users).
<http pattern="/**" authentication-manager-ref='testAuthenticationManager' entry-point-ref="loginUrlAuthenticationEntryPoint" >
<intercept-url pattern="/**" access="ROLE_USER" />
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<custom-filter position="FORM_LOGIN_FILTER" ref="customFormLoginFilter" />
<session-management session-authentication-strategy-ref="sas"/>
</http>
<!-- This just extends UsernamePasswordAuthenticationFilter -->
<beans:bean id="customFormLoginFilter" class="com.acme.security.CustomAuthenticationFilter" >
<beans:property name="sessionAuthenticationStrategy" ref="sas" />
<beans:property name="filterProcessesUrl" value="/j_spring_security_check" />
<beans:property name="authenticationManager" ref="testAuthenticationManager"/>
<beans:property name="authenticationSuccessHandler" ref="successHandler"/>
<beans:property name="authenticationFailureHandler" ref="failureHandler" />
</beans:bean>
<beans:bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
<beans:property name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="expiredUrl" value="/login.jsp?errorCode=maxSessionsExceeded" />
</beans:bean>
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="maximumSessions" value="1" />
</beans:bean>
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
<beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/login.jsp"/>
</beans:bean>
<beans:bean id="successHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/login/setup.htm"/>
<beans:property name="alwaysUseDefaultTargetUrl" value="true"/>
</beans:bean>
<beans:bean id="failureHandler"
class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login.jsp" />
<beans:property name="exceptionMappings">
<beans:props>
<beans:prop key="org.springframework.security.authentication.AuthenticationServiceException">/login.jsp?errorCode=666</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
I'm trying to connect from a spring security application to a cas server. When I login in CAS, the request is redirected to my webapp but in my UserDetailsService I'm receiving _cas_stateful_ as the username and I cannot find my user in the webapp to load the permissions
The problem was the authenticationManager. As the documentation says, you must use CasAuthenticationProvider like this:
<beans:bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<beans:property name="authenticationUserDetailsService">
<beans:bean
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:constructor-arg ref="mongoUserDetailsService" />
</beans:bean>
</beans:property>
<beans:property name="serviceProperties" ref="serviceProperties" />
<beans:property name="ticketValidator">
<beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<beans:constructor-arg index="0" value="https://localhost:7443/cas" />
</beans:bean>
</beans:property>
<!-- Esta clave es única por aplicación -->
<beans:property name="key" value="your-provider-auth" />
</beans:bean>
Then you must to set the casAuthenticationProvider to the authenticationManager:
<authentication-manager alias="authenticationManager">
<!-- <authentication-provider user-service-ref='mongoUserDetailsService'/> -->
<authentication-provider ref="casAuthenticationProvider" />
</authentication-manager>
As you can see, the custom mongoUserDetailsService is not assigned to the authenticationManager but the new casAuthenticationProvider, then we set the casAuthenticationProvider to the authenticationManager
I have successfully setup LDAPS container-based authentication, and am now trying to get it working with spring security since I will also need to perform lookups/queries.
In WAS I have all the endpoints using the correct keystore (except for WC_DefaulHost). Additionally, I also setup Dynamic endpoint config for ldaps,host,port.
When i try to log in, I'm just getting "spring_security_login?login_error" and no system.out exceptions.
Am I missing something? Aren't endpoint configurations enough? Any way I can get more info to troubleshoot?
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" 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">
<authentication-manager>
<authentication-provider ref="ldapAuthProvider" />
</authentication-manager>
<beans:bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<!-- AD authenticator -->
<beans:constructor-arg value="ldaps://host:port/DC=" />
<beans:property name="userDn" value="CN=,OU=,DC=" />
<beans:property name="password" value="" />
</beans:bean>
<beans:bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean id="wimLdapAuthenticator"
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<beans:constructor-arg ref="contextSource" />
<beans:property name="userSearch">
<beans:bean id="userSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<beans:constructor-arg index="0" value="" />
<beans:constructor-arg index="1" value="CN={0}" />
<beans:constructor-arg index="2" ref="contextSource" />
</beans:bean>
</beans:property>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<http auto-config="true" pattern="/**">
<!-- Security zones -->
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<intercept-url pattern="/spring_security_login" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</http>
</beans:beans>
It's working now.. seems like it wasn't an SSL problem... I switched the order of the intercept-url so that /** is the last one and added a custom login form..
<form-login login-page="/login" default-target-url="/viewAllTeams" authentication-failure-url="/loginfailed" />
<logout logout-success-url="/logout" />
<form-login default-target-url="/viewAllTeams"/>
<intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/loginfailed" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
I also found that you can show exceptions using the following:
<div class="errorblock">
Your login attempt was not successful, try again.<br /> Caused :
${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
</div>