I’m setting up Spring Security to work with LDAP. The problem I’m running into is an “invalid DN” error when Spring’s org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator is trying to get authorities for a user. I’ve looked over many other posts on this site and others but nothing has helped. I’m guessing I’ve overlooked something but don’t know what.
This is the error:
org.springframework.dao.DataAccessResourceFailureException: Failed to borrow DirContext from pool.; nested exception is org.springframework.ldap.InvalidNameException: [LDAP: error code 34 - invalid DN]; nested exception is javax.naming.InvalidNameException: [LDAP: error code 34 - invalid DN]
at org.springframework.ldap.pool.factory.PoolingContextSource.getContext(PoolingContextSource.java:425)
at org.springframework.ldap.pool.factory.PoolingContextSource.getReadWriteContext(PoolingContextSource.java:408)
at org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy.getReadWriteContext(TransactionAwareContextSourceProxy.java:94)
at org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy.getReadOnlyContext(TransactionAwareContextSourceProxy.java:65)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:287)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:259)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:606)
at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:524)
at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleAttributeValues(SpringSecurityLdapTemplate.java:173)
at org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator.getGroupMembershipRoles(DefaultLdapAuthoritiesPopulator.java:215)
at org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator.getGrantedAuthorities(DefaultLdapAuthoritiesPopulator.java:185)
at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.loadUserAuthorities(LdapAuthenticationProvider.java:197)
at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:63)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:195)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.access.channel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:144)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:726)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:206)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:843)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:648)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)
Caused by: org.springframework.ldap.InvalidNameException: [LDAP: error code 34 - invalid DN]; nested exception is javax.naming.InvalidNameException: [LDAP: error code 34 - invalid DN]
at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:128)
at org.springframework.ldap.core.support.AbstractContextSource.createContext(AbstractContextSource.java:266)
at org.springframework.ldap.core.support.AbstractContextSource.getContext(AbstractContextSource.java:106)
at org.springframework.ldap.core.support.AbstractContextSource.getReadWriteContext(AbstractContextSource.java:138)
at org.springframework.ldap.pool.factory.DirContextPoolableObjectFactory.makeObject(DirContextPoolableObjectFactory.java:127)
at org.apache.commons.pool.impl.GenericKeyedObjectPool.borrowObject(GenericKeyedObjectPool.java:797)
at org.springframework.ldap.pool.factory.PoolingContextSource.getContext(PoolingContextSource.java:422)
... 41 more
Caused by: javax.naming.InvalidNameException: [LDAP: error code 34 - invalid DN]
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3008)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2815)
at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2729)
at com.sun.jndi.ldap.LdapCtx.(LdapCtx.java:296)
at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:175)
at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:193)
at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:136)
at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:66)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:667)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
at javax.naming.InitialContext.init(InitialContext.java:223)
at javax.naming.ldap.InitialLdapContext.(InitialLdapContext.java:134)
at org.springframework.ldap.core.support.LdapContextSource.getDirContextInstance(LdapContextSource.java:43)
at org.springframework.ldap.core.support.AbstractContextSource.createContext(AbstractContextSource.java:254)
... 46 more
My ldap server version is OpenLDAP: slapd 2.4.23. The version of spring I’m using is 3.1.4.RELEASE. These are the security/ldap dependencies my pom.xml file.
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core-tiger</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-odm</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-ldif-core</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-ldif-batch</artifactId>
<version>1.3.1.RELEASE</version>
</dependency>
This is my security-context.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<bean id="configurer" class="com.bamnetworks.ooo.util.EnvPlaceholderConfigurer" />
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<property name="contextSource" ref="contextSource"/>
</bean>
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="${admin.portal.ldap.url}"/>
<property name="userDn" value="${admin.portal.ldap.userDn}"/>
<property name="base" value="${admin.portal.ldap.base}" />
<property name="password" value="${admin.portal.ldap.password}"/>
<property name="pooled" value="false"/>
</bean>
<bean id="pooledContextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource">
<property name="contextSource" ref="contextSourceTarget"/>
<property name="dirContextValidator" ref="dirContextValidator"/>
<!-- property name="testOnBorrow" value="true"/ -->
<!-- property name="testWhileIdle" value="true"/ -->
<property name="minIdle" value="${admin.portal.ldap.minIdle}"/>
<property name="maxIdle" value="${admin.portal.ldap.maxIdle}"/>
<property name="maxActive" value="${admin.portal.ldap.maxActive}"/>
<property name="maxTotal" value="${admin.portal.ldap.maxTotal}"/>
<property name="maxWait" value="${admin.portal.ldap.maxWait}"/>
</bean>
<bean id="contextSource"
class="org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy">
<constructor-arg ref="pooledContextSource"/>
</bean>
<bean id="dirContextValidator"
class="org.springframework.ldap.pool.validation.DefaultDirContextValidator"/>
<bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="contextSourceTarget"/>
<property name="userDnPatterns">
<list><value>cn={0},ou=user</value></list>
</property>
</bean>
</constructor-arg>
<constructor-arg>
<bean
class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg index="0" ref="contextSource"/>
<constructor-arg index="1" value="ou=user,dc=company,dc=com"/>
<property name="groupRoleAttribute" value="ou"/>
<property name="searchSubtree" value="true"/>
<property name="rolePrefix" value="ROLE_"/>
<property name="convertToUpperCase" value="true"/>
<property name="groupSearchFilter" value="(&(member={0}))"/>
</bean>
</constructor-arg>
</bean>
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg>
<list>
<ref local="ldapAuthenticationProvider" />
</list>
</constructor-arg>
</bean>
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.html"/>
</bean>
<bean id="accessDeniedHandler" class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/login.html"/>
</bean>
<bean id="form_login_filter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="filterProcessesUrl" value="/login/process"/>
</bean>
<security:http pattern="/admin_portal/css/**" security="none"/>
<security:http pattern="/admin_portal/images/**" security="none"/>
<security:http auto-config="false"
authentication-manager-ref="authenticationManager"
entry-point-ref="authenticationEntryPoint"
access-denied-page="/login.html">
<security:intercept-url pattern="/login.html*" requires-channel="http" access="ROLE_ANONYMOUS"/>
<security:intercept-url pattern="/login/process" requires-channel="http" access="ROLE_ANONYMOUS"/>
<security:intercept-url pattern="/search.html*" requires-channel="http" access="ROLE_ACME_ADMIN"/>
<security:intercept-url pattern="/ws/**" requires-channel="http" access="ROLE_ACME_ADMIN"/>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="form_login_filter" />
</security:http>
</beans>
This is my (for the moment) simple login page.
<form method="POST" action="/admin_portal/login/process">
Username: <input type="text" name="j_username" size="15" /><br />
Password: <input type="password" name="j_password" size="15" /><br />
<div align="center">
<p><input type="submit" value="Login" /></p>
</div>
</form>
These are the objects in LDAP:
# user, company.com
dn: ou=user,dc=company,dc=com
ou: user
objectClass: organizationalUnit
# ACME_ADMIN, user, company.com
dn: cn=ACME_ADMIN,ou=user,dc=company,dc=com
cn: ACME_ADMIN
objectClass: groupOfNames
objectClass: top
ou: ACME_ADMIN
member: cn=LucyVanPelt,ou=user,dc=company,dc=com
# LucyVanPelt, user, company.com
dn: cn=LucyVanPelt,ou=user,dc=company,dc=com
givenName: Lucy
sn: VanPelt
userPassword:: MTIzNDU=
uidNumber: 1002
gidNumber: 500
homeDirectory: /home/users/lvanpelt
loginShell: /bin/sh
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
cn: LucyVanPelt
ou: ACME_ADMIN
uid: LucyVanPelt
When I run this search on the command line.
/usr/bin/ldapsearch -x -b "ou=user,dc=company,dc=com" -D "cn=Manager,dc=company,dc=com" -w xxxxxxxxx "(&(member=cn=LucyVanPelt,ou=user,dc=company,dc=com))"
I get this result:
# ACME_ADMIN, user, company.com
dn: cn=ACME_ADMIN,ou=user,dc=company,dc=com
cn: ACME_ADMIN
objectClass: groupOfNames
objectClass: top
ou: ACME_ADMIN
member: cn=LucyVanPelt,ou=user,dc=company,dc=com
This is some debugging output in the log files:
2013-12-06 10:57:43,831 [btpool0-0] DEBUG AntPathRequestMatcher - Checking match of request : '/login/process'; against '/admin_portal/css/**'
2013-12-06 10:57:43,832 [btpool0-0] DEBUG AntPathRequestMatcher - Checking match of request : '/login/process'; against '/admin_portal/images/**'
2013-12-06 10:57:43,833 [btpool0-0] DEBUG FilterChainProxy - /login/process at position 1 of 9 in additional filter chain; firing Filter: 'ChannelProcessingFilter'
2013-12-06 10:57:43,833 [btpool0-0] DEBUG AntPathRequestMatcher - Checking match of request : '/login/process'; against '/login.html*'
2013-12-06 10:57:43,834 [btpool0-0] DEBUG AntPathRequestMatcher - Checking match of request : '/login/process'; against '/login/process'
2013-12-06 10:57:43,834 [btpool0-0] DEBUG ChannelProcessingFilter - Request: FilterInvocation: URL: /login/process; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL]
2013-12-06 10:57:43,836 [btpool0-0] DEBUG FilterChainProxy - /login/process at position 2 of 9 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2013-12-06 10:57:43,836 [btpool0-0] DEBUG HttpSessionSecurityContextRepository - No HttpSession currently exists
2013-12-06 10:57:43,836 [btpool0-0] DEBUG HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
2013-12-06 10:57:43,839 [btpool0-0] DEBUG FilterChainProxy - /login/process at position 3 of 9 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2013-12-06 10:57:43,839 [btpool0-0] DEBUG UsernamePasswordAuthenticationFilter - Request is to process authentication
2013-12-06 10:57:43,846 [btpool0-0] DEBUG ProviderManager - Authentication attempt using org.springframework.security.ldap.authentication.LdapAuthenticationProvider
2013-12-06 10:57:43,853 [btpool0-0] DEBUG LdapAuthenticationProvider - Processing authentication request for user: LucyVanPelt
2013-12-06 10:57:43,861 [btpool0-0] DEBUG BindAuthenticator - Attempting to bind as cn=LucyVanPelt,ou=user,dc=company,dc=com
2013-12-06 10:57:44,149 [btpool0-0] DEBUG AbstractContextSource - Got Ldap context on server 'ldap://m1devldap01.mm3.company.com:389/dc=company,dc=com'
2013-12-06 10:57:44,153 [btpool0-0] DEBUG BindAuthenticator - Retrieving attributes...
2013-12-06 10:57:44,265 [btpool0-0] DEBUG DefaultLdapAuthoritiesPopulator - Getting authorities for user cn=LucyVanPelt,ou=user,dc=company,dc=com
2013-12-06 10:57:44,265 [btpool0-0] DEBUG DefaultLdapAuthoritiesPopulator - Searching for roles for user 'LucyVanPelt', DN = 'cn=LucyVanPelt,ou=user,dc=company,dc=com', with filter (&(member={0})) in search base 'ou=user,dc=company,dc=com'
2013-12-06 10:57:44,266 [btpool0-0] DEBUG SpringSecurityLdapTemplate - Using filter: (&(member=cn=LucyVanPelt,ou=user,dc=company,dc=com))
2013-12-06 10:57:44,266 [btpool0-0] INFO LdapTemplate - The returnObjFlag of supplied SearchControls is not set but a ContextMapper is used - setting flag to true
2013-12-06 10:57:44,273 [btpool0-0] DEBUG DirContextPoolableObjectFactory - Creating a new READ_WRITE DirContext
It looks like Spring is using the same filter and search base as I do when using the ldapsearch tool from the command line. It works with the tool. Not sure what I’m missing with Spring tho. Any help is greatly appreciated.
Thanks
Snow
In my config files I had extra quote marks around the admin user dn that I was using which makes it invalid. Odd that the BindAuthenticator had no issue with it.
Related
I've read several blogs about this subject and all the stackoverflow.com articles I can get my hands on, but nothing seems to answer the problem I've been encountering.
I've followed several Okta examples online, including the main blog post that was posted this year. I still run into the issue below, which when I trace through the spring-security code gets me to the voting process in spring and it fails.
TRACE 2017-08-22 16:45:11,128 [org.springframework.web.context.support.XmlWebApplicationContext] - Publishing event in Root WebApplicationContext: org.springframework.security.access.event.AuthorizationFailureEvent[source=FilterInvocation: URL: /SSO]
DEBUG 2017-08-22 16:45:11,129 [org.springframework.security.web.access.ExceptionTranslationFilter] - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206)
In my setup I'm not using spring boot or spring annotations, but rather good ol' spring xml configuration. I've look at the configuration enough that I don't see where the issue lies.
versions:
spring: 3.2.18.RELEASE
spring security: 3.2.7.RELEASE
spring saml2: 1.0.2.RELEASE
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- Enable auto-wiring
<context:annotation-config/>-->
<!-- Scan for auto-wiring classes in spring saml packages -->
<context:component-scan base-package="org.springframework.security.saml"/>
<!-- Unsecured pages -->
<security:http security="none" pattern="/favicon.ico"/>
<security:http security="none" pattern="/images/**"/>
<security:http security="none" pattern="/style/**"/>
<security:http security="none" pattern="/scripts/**"/>
<security:http security="none" pattern="/logout.jsp"/>
<!-- Security for the administration UI -->
<security:http pattern="/saml/web/**" use-expressions="false">
<security:access-denied-handler error-page="/saml/web/metadata/login"/>
<security:form-login login-processing-url="/saml/web/login" login-page="/saml/web/metadata/login" default-target-url="/saml/web/metadata"/>
<security:intercept-url pattern="/saml/web/metadata/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/saml/web/**" access="ROLE_ADMIN"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
</security:http>
<!-- Secured pages with SAML as entry point -->
<security:http entry-point-ref="samlEntryPoint" use-expressions="false">
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</security:http>
<!-- Filters for processing of SAML messages -->
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map request-matcher="ant">
<security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
<security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
<security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
<!-- <security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/> -->
</security:filter-chain-map>
</bean>
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/app/buildingInformation"/>
</bean>
<!--
Use the following for interpreting RelayState coming from unsolicited response as redirect URL:
<bean id="successRedirectHandler" class="org.springframework.security.saml.SAMLRelayStateSuccessHandler">
<property name="defaultTargetUrl" value="/" />
</bean>-->
<!-- Handler deciding where to redirect user after failed login -->
<bean id="failureRedirectHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="useForward" value="true"/>
<property name="defaultFailureUrl" value="/error.jsp"/>
</bean>
<!-- Handler for successful logout -->
<bean id="successLogoutHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
<property name="defaultTargetUrl" value="/logout.jsp"/>
</bean>
<!-- <bean id="mySecurityUserService" class="com.my.security.service.MySecurityUserService">
<property name="application" ref="application"/>
<property name="allowAllAccess" ref="allowAllAccess"/>
</bean> -->
<security:authentication-manager alias="authenticationManager">
<!-- Register authentication manager for SAML provider -->
<security:authentication-provider ref="samlAuthenticationProvider"/>
<!-- Register authentication manager for administration UI
<security:authentication-provider user-service-ref="mySecurityUserService"/>-->
</security:authentication-manager>
<!-- Logger for SAML messages and events -->
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>
<!-- Central storage of cryptographic keys -->
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="classpath:security/mykeystore.jks"/>
<constructor-arg type="java.lang.String" value="secret"/>
<constructor-arg>
<map>
<entry key="spring" value="secret"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="spring"/>
</bean>
<!-- Entry point to initialize authentication, default values taken from properties file -->
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint">
<property name="defaultProfileOptions">
<bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
<property name="includeScoping" value="false"/>
</bean>
</property>
</bean>
<!-- IDP Discovery Service -->
<bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery">
<property name="idpSelectionPath" value="/WEB-INF/security/idpSelection.jsp"/>
</bean>
<!-- Filter automatically generates default SP metadata -->
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<!-- <property name="idpDiscoveryEnabled" value="true"/> -->
<property name="idpDiscoveryEnabled" value="false"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<!-- The filter is waiting for connections on URL suffixed with filterSuffix and presents SP metadata there -->
<bean id="metadataDisplayFilter" class="org.springframework.security.saml.metadata.MetadataDisplayFilter"/>
<!-- Configure HTTP Client to accept certificates from the keystore for HTTPS verification -->
<!--
<bean class="org.springframework.security.saml.trust.httpclient.TLSProtocolConfigurer">
<property name="sslHostnameVerification" value="default"/>
</bean>
-->
<!-- IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here -->
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg>
<list>
<!-- Example of classpath metadata with Extended Metadata -->
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<constructor-arg>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<constructor-arg>
<bean class="java.util.Timer"/>
</constructor-arg>
<constructor-arg>
<bean class="org.opensaml.util.resource.ClasspathResource">
<constructor-arg value="/metadata/idp.xml"/>
</bean>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
</bean>
</constructor-arg>
</bean>
<!-- Example of HTTP metadata without Extended Metadata -->
<!-- <bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
URL containing the metadata
<constructor-arg>
<value type="java.lang.String">https://dev-738438.oktapreview.com/app/exkbq3s0o4HnCFf3U0h7/sso/saml/metadata</value>
</constructor-arg>
Timeout for metadata loading in ms
<constructor-arg>
<value type="int">15000</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean> -->
<!-- Example of file system metadata without Extended Metadata -->
<!--
<bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
<constructor-arg>
<value type="java.io.File">/usr/local/metadata/idp.xml</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
-->
</list>
</constructor-arg>
<!-- OPTIONAL used when one of the metadata files contains information about this service provider -->
<!-- <property name="hostedSPName" value=""/> -->
<!-- OPTIONAL property: can tell the system which IDP should be used for authenticating user by default. -->
<!-- <property name="defaultIDP" value="http://localhost:8080/opensso"/> -->
</bean>
<!-- SAML Authentication Provider responsible for validating of received SAML messages -->
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
<!-- OPTIONAL property: can be used to store/load user data after login
<property name="userDetails" ref="mySecurityUserService"/> -->
</bean>
<!-- Provider of default SAML Context -->
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/>
<!-- Processing filter for WebSSO profile messages -->
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<!-- Processing filter for WebSSO Holder-of-Key profile -->
<bean id="samlWebSSOHoKProcessingFilter" class="org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<!-- Logout handler terminating local session -->
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
<property name="invalidateHttpSession" value="false"/>
</bean>
<!-- Override default logout processing filter with the one processing SAML messages -->
<bean id="samlLogoutFilter" class="org.springframework.security.saml.SAMLLogoutFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
<constructor-arg index="2" ref="logoutHandler"/>
</bean>
<!-- Filter processing incoming logout messages -->
<!-- First argument determines URL user will be redirected to after successful global logout -->
<bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
</bean>
<!-- Class loading incoming SAML messages from httpRequest stream -->
<bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg>
<list>
<ref bean="redirectBinding"/>
<ref bean="postBinding"/>
<ref bean="artifactBinding"/>
<ref bean="soapBinding"/>
<ref bean="paosBinding"/>
</list>
</constructor-arg>
</bean>
<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>
<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 Web SSO profile -->
<bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/>
<!-- SAML 2.0 Holder-of-Key Web SSO profile -->
<bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 ECP profile -->
<bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/>
<!-- SAML 2.0 Logout Profile -->
<bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/>
<!-- Bindings, encoders and decoders used for creating and parsing messages -->
<bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
</bean>
<bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
<constructor-arg>
<bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl">
<constructor-arg>
<bean class="org.apache.commons.httpclient.HttpClient">
<constructor-arg>
<bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
</constructor-arg>
</bean>
</constructor-arg>
<property name="processor">
<bean class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg ref="soapBinding"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<!-- Initialization of OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap"/>
<!-- Initialization of the velocity engine -->
<bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/>
<!--
XML parser pool needed for OpenSAML parsing
WARNING: If customizing a ParserPool implementation See https://shibboleth.net/community/advisories/secadv_20131213.txt
Specifically the following should be explicitly set to avoid exploits:
1) set pool property 'expandEntityReferences' to 'false'
2) set feature 'javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING' to true
3) set feature 'http://apache.org/xml/features/disallow-doctype-decl' to true. This is a Xerces-specific feature,
including derivatives such as the internal JAXP implementations supplied with the Oracle and OpenJDK JREs. For
other JAXP implementations, consult the documentation for the implementation for guidance on how to achieve a
similar configuration.
-->
<bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize"/>
<bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/>
Thanks in advance for any help or for pointing me in the right direction.
This guide is a bit old, but it shows how to make things work with good ol' Spring XML: https://developer.okta.com/code/java/spring_security_saml.html
I am using the Spring Saml Sample application but with a load balancer in front. I have change the contextProvider as following. Requests are being redirected to IDP and after authentication, I keep getting error "Caused by: org.opensaml.common.SAMLException: InResponseToField of the Response doesn't correspond to sent message a1agabe66iba055b26j9i0gac8ah48h"
Can someone help me out please.
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- Enable auto-wiring -->
<context:annotation-config/>
<!-- Scan for auto-wiring classes in spring saml packages -->
<context:component-scan base-package="org.springframework.security.saml"/>
<!-- Unsecured pages -->
<security:http security="none" pattern="/favicon.ico"/>
<security:http security="none" pattern="/images/**"/>
<security:http security="none" pattern="/css/**"/>
<security:http security="none" pattern="/logout.jsp"/>
<!-- Security for the administration UI -->
<security:http pattern="/saml/web/**" use-expressions="false">
<security:access-denied-handler error-page="/saml/web/metadata/login"/>
<security:form-login login-processing-url="/saml/web/login" login-page="/saml/web/metadata/login" default-target-url="/saml/web/metadata"/>
<security:intercept-url pattern="/saml/web/metadata/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/saml/web/**" access="ROLE_ADMIN"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
</security:http>
<!-- Secured pages with SAML as entry point -->
<security:http entry-point-ref="samlEntryPoint" use-expressions="false">
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</security:http>
<!-- Filters for processing of SAML messages -->
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map request-matcher="ant">
<security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
<security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
<security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
<security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/>
</security:filter-chain-map>
</bean>
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/"/>
</bean>
<!--
Use the following for interpreting RelayState coming from unsolicited response as redirect URL:
<bean id="successRedirectHandler" class="org.springframework.security.saml.SAMLRelayStateSuccessHandler">
<property name="defaultTargetUrl" value="/" />
</bean>
-->
<!-- Handler deciding where to redirect user after failed login -->
<bean id="failureRedirectHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="useForward" value="true"/>
<property name="defaultFailureUrl" value="/error.jsp"/>
</bean>
<!-- Handler for successful logout -->
<bean id="successLogoutHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
<property name="defaultTargetUrl" value="/logout.jsp"/>
</bean>
<security:authentication-manager alias="authenticationManager">
<!-- Register authentication manager for SAML provider -->
<security:authentication-provider ref="samlAuthenticationProvider"/>
<!-- Register authentication manager for administration UI -->
<security:authentication-provider>
<security:user-service id="adminInterfaceService">
<security:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
<!-- Logger for SAML messages and events -->
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>
<!-- Central storage of cryptographic keys -->
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="classpath:security/samlKeystore.jks"/>
<constructor-arg type="java.lang.String" value="nalle123"/>
<constructor-arg>
<map>
<entry key="apollo" value="nalle123"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="apollo"/>
</bean>
<!-- Entry point to initialize authentication, default values taken from properties file -->
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint">
<property name="defaultProfileOptions">
<bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
<property name="includeScoping" value="false"/>
</bean>
</property>
</bean>
<!-- IDP Discovery Service -->
<bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery">
<property name="idpSelectionPath" value="/WEB-INF/security/idpSelection.jsp"/>
</bean>
<!-- Filter automatically generates default SP metadata -->
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="true"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<!-- The filter is waiting for connections on URL suffixed with filterSuffix and presents SP metadata there -->
<bean id="metadataDisplayFilter" class="org.springframework.security.saml.metadata.MetadataDisplayFilter"/>
<!-- Configure HTTP Client to accept certificates from the keystore for HTTPS verification -->
<!--
<bean class="org.springframework.security.saml.trust.httpclient.TLSProtocolConfigurer">
<property name="sslHostnameVerification" value="default"/>
</bean>
-->
<!-- IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here -->
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg>
<list>
<!-- Example of classpath metadata with Extended Metadata -->
<bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
<!-- URL containing the metadata -->
<constructor-arg>
<value type="java.lang.String">http://localhost/idp/idp-meta.xml</value>
</constructor-arg>
<!-- Timeout for metadata loading in ms -->
<constructor-arg>
<value type="int">15000</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
<!-- Example of HTTP metadata without Extended Metadata -->
<bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
<!-- URL containing the metadata -->
<constructor-arg>
<value type="java.lang.String">http://idp.ssocircle.com/idp-meta.xml</value>
</constructor-arg>
<!-- Timeout for metadata loading in ms -->
<constructor-arg>
<value type="int">15000</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
<!-- Example of file system metadata without Extended Metadata -->
<!--
<bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
<constructor-arg>
<value type="java.io.File">/usr/local/metadata/idp.xml</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
-->
</list>
</constructor-arg>
<!-- OPTIONAL used when one of the metadata files contains information about this service provider -->
<!-- <property name="hostedSPName" value=""/> -->
<!-- OPTIONAL property: can tell the system which IDP should be used for authenticating user by default. -->
<!-- <property name="defaultIDP" value="http://localhost:8080/opensso"/> -->
</bean>
<!-- SAML Authentication Provider responsible for validating of received SAML messages -->
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
<!-- OPTIONAL property: can be used to store/load user data after login -->
<!--
<property name="userDetails" ref="bean" />
-->
</bean>
<!-- Provider of default SAML Context -->
-->
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB">
<property name="scheme" value="http"/>
<property name="serverName" value="localhost"/>
<property name="includeServerPortInRequestURL" value="false"/>
<property name="contextPath" value="/spring-security-saml2-sample"/>
</bean>
<!-- Processing filter for WebSSO profile messages -->
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<!-- Processing filter for WebSSO Holder-of-Key profile -->
<bean id="samlWebSSOHoKProcessingFilter" class="org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<!-- Logout handler terminating local session -->
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
<property name="invalidateHttpSession" value="false"/>
</bean>
<!-- Override default logout processing filter with the one processing SAML messages -->
<bean id="samlLogoutFilter" class="org.springframework.security.saml.SAMLLogoutFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
<constructor-arg index="2" ref="logoutHandler"/>
</bean>
<!-- Filter processing incoming logout messages -->
<!-- First argument determines URL user will be redirected to after successful global logout -->
<bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
</bean>
<!-- Class loading incoming SAML messages from httpRequest stream -->
<bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg>
<list>
<ref bean="redirectBinding"/>
<ref bean="postBinding"/>
<ref bean="artifactBinding"/>
<ref bean="soapBinding"/>
<ref bean="paosBinding"/>
</list>
</constructor-arg>
</bean>
<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>
<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 Web SSO profile -->
<bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/>
<!-- SAML 2.0 Holder-of-Key Web SSO profile -->
<bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 ECP profile -->
<bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/>
<!-- SAML 2.0 Logout Profile -->
<bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/>
<!-- Bindings, encoders and decoders used for creating and parsing messages -->
<bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
</bean>
<bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
<constructor-arg>
<bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl">
<constructor-arg>
<bean class="org.apache.commons.httpclient.HttpClient">
<constructor-arg>
<bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
</constructor-arg>
</bean>
</constructor-arg>
<property name="processor">
<bean class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg ref="soapBinding"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<!-- Initialization of OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap"/>
<!-- Initialization of the velocity engine -->
<bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/>
<!--
XML parser pool needed for OpenSAML parsing
WARNING: If customizing a ParserPool implementation See https://shibboleth.net/community/advisories/secadv_20131213.txt
Specifically the following should be explicitly set to avoid exploits:
1) set pool property 'expandEntityReferences' to 'false'
2) set feature 'javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING' to true
3) set feature 'http://apache.org/xml/features/disallow-doctype-decl' to true. This is a Xerces-specific feature,
including derivatives such as the internal JAXP implementations supplied with the Oracle and OpenJDK JREs. For
other JAXP implementations, consult the documentation for the implementation for guidance on how to achieve a
similar configuration.
-->
<bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize"/>
<bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/>
I found the solution for problem. It was explained very well at Spring SAML integration with WSO2 Identity server, SAML Message ID not reconised
I went with updating the cookie name approach using org.springframework.session.web.http.CookieHttpSessionStrategy.
<bean id="sessionRepositoryFilter"
class="org.springframework.session.web.http.SessionRepositoryFilter">
<constructor-arg ref="sessionRepository"/>
<property name="httpSessionStrategy">
<bean class="org.springframework.session.web.http.CookieHttpSessionStrategy">
<property name="cookieName" value="myCookieName" />
</bean>
</property>
</bean>
I am seeking solutions to migrate current holistic system into microservices architecture. I want to use Spring Integration and Spring Security to integrate and secure the services. According to my understanding, to secure backend services is more like Single Sign On (SSO). I use Jasig CAS 4.2.7 (seems working fine with Spring Security) to authenticate users centrally, Spring Integration 4.2.11.RELEASE and Spring Security 4.0.4.RELEASE.
I have created a Maven project with two modules named web and service which are both web application. I deploy the three war files on same local Tomcat (version 7.0.36) and just add jimi and bob into CAS properties file to ensure them passing the authentication of CAS. When I try to access URL http://localhost:8080/prototype-integration-security-web/user, I got authenticated into front-end application but access forbidden on backend services.
The POM file looks as below.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>prototype.integration.security</groupId>
<artifactId>prototype-integration-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>prototype-integration-security</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warName>${project.name}</warName>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-http</artifactId>
<version>4.2.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.7.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-security</artifactId>
<version>4.2.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.4</version>
</dependency>
</dependencies>
<modules>
<module>prototype-integration-security-web</module>
<module>prototype-integration-security-service</module>
</modules>
</project>
The deployment description files web.xml of two modules look same except the display name as following.
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="IntegrationSecurityWeb" version="3.0">
<display-name>Integration Security Web Prototype</display-name>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
In the Spring application context configuration file of web module, dispatcher-servlet.xml looks as below.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:int-security="http://www.springframework.org/schema/integration/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-4.2.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http-4.2.xsd
http://www.springframework.org/schema/integration/security
http://www.springframework.org/schema/integration/security/spring-integration-security-4.2.xsd">
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<constructor-arg>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.apache.http.impl.client.HttpClients"/>
<property name="targetMethod" value="createMinimal"/>
</bean>
</constructor-arg>
</bean>
</constructor-arg>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
</bean>
</list>
</property>
</bean>
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="http://localhost:8080/prototype-integration-security-web/login/cas" />
<property name="sendRenew" value="false" />
</bean>
<!-- Access voters -->
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleHierarchyVoter">
<constructor-arg>
<bean class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
</value>
</property>
</bean>
</constructor-arg>
</bean>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:8443/cas/login" />
<property name="serviceProperties" ref="serviceProperties" />
</bean>
<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<!-- This filter handles a Single Logout Request from the CAS Server -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
<bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="http://localhost:8080/cas/logout" />
<constructor-arg>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</constructor-arg>
<property name="filterProcessesUrl" value="/logout/cas" />
</bean>
<security:http entry-point-ref="casEntryPoint" access-decision-manager-ref="accessDecisionManager" use-expressions="false">
<security:intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:form-login />
<security:logout />
<security:custom-filter before="LOGOUT_FILTER" ref="requestSingleLogoutFilter"/>
<security:custom-filter before="CAS_FILTER" ref="singleLogoutFilter"/>
<security:custom-filter position="CAS_FILTER" ref="casFilter" />
</security:http>
<security:user-service id="userService">
<security:user name="jimi" password="jimi" authorities="ROLE_ADMIN" />
<security:user name="bob" password="bob" authorities="ROLE_USER" />
</security:user-service>
<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="authenticationUserDetailsService">
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<constructor-arg index="0" ref="userService" />
</bean>
</property>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://localhost:8443/cas" />
</bean>
</property>
<property name="key" value="localCAS" />
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider" />
</security:authentication-manager>
<int:channel-interceptor order="99">
<bean class="org.springframework.integration.security.channel.SecurityContextPropagationChannelInterceptor"/>
</int:channel-interceptor>
<task:executor id="pool" pool-size="5"/>
<int:poller id="poller" default="true" fixed-rate="1000"/>
<int-security:secured-channels>
<int-security:access-policy pattern="user*" send-access="ROLE_USER" />
<int-security:access-policy pattern="admin*" send-access="ROLE_ADMIN" />
</int-security:secured-channels>
<int-http:inbound-channel-adapter path="/user*" supported-methods="GET, POST" channel="userRequestChannel" />
<int:channel id="userRequestChannel">
<int:queue/>
</int:channel>
<int-http:outbound-channel-adapter url="http://localhost:8080/prototype-integration-security-service/query?ticket={ticket}"
http-method="GET"
rest-template="restTemplate"
channel="userRequestChannel">
<int-http:uri-variable name="ticket" expression="T(org.springframework.security.core.context.SecurityContextHolder).context.authentication.credentials"/>
</int-http:outbound-channel-adapter>
<int-http:inbound-channel-adapter path="/admin/callback*"
supported-methods="GET, POST"
channel="adminRequestChannel" />
<int:channel id="adminRequestChannel">
<int:queue/>
</int:channel>
<int:logging-channel-adapter id="logging" channel="adminRequestChannel" level="DEBUG" />
</beans>
In the context configuration file of service module, dispatcher-servlet.xml looks as following.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:int-security="http://www.springframework.org/schema/integration/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-4.2.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http-4.2.xsd
http://www.springframework.org/schema/integration/security
http://www.springframework.org/schema/integration/security/spring-integration-security-4.2.xsd">
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<constructor-arg>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.apache.http.impl.client.HttpClients"/>
<property name="targetMethod" value="createMinimal"/>
</bean>
</constructor-arg>
</bean>
</constructor-arg>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
</bean>
</list>
</property>
</bean>
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="http://localhost:8080/prototype-integration-security-service/login/cas" />
<property name="sendRenew" value="false" />
</bean>
<!-- Access voters -->
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleHierarchyVoter">
<constructor-arg>
<bean class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
</value>
</property>
</bean>
</constructor-arg>
</bean>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
<!-- <bean class="org.springframework.security.web.access.expression.WebExpressionVoter" /> -->
</list>
</constructor-arg>
</bean>
<bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:8443/cas/login" />
<property name="serviceProperties" ref="serviceProperties" />
</bean>
<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<!-- This filter handles a Single Logout Request from the CAS Server -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
<bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="https://localhost:8443/cas/logout" />
<constructor-arg>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</constructor-arg>
<property name="filterProcessesUrl" value="/logout/cas" />
</bean>
<security:http entry-point-ref="casEntryPoint" access-decision-manager-ref="accessDecisionManager" use-expressions="false">
<security:intercept-url pattern="/**" access="ROLE_ADMIN"/>
<security:form-login />
<security:logout />
<security:custom-filter before="LOGOUT_FILTER" ref="requestSingleLogoutFilter"/>
<security:custom-filter before="CAS_FILTER" ref="singleLogoutFilter"/>
<security:custom-filter position="CAS_FILTER" ref="casFilter" />
</security:http>
<security:user-service id="userService">
<security:user name="jimi" password="jimi" authorities="ROLE_ADMIN" />
<security:user name="bob" password="bob" authorities="ROLE_USER" />
</security:user-service>
<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="authenticationUserDetailsService">
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<constructor-arg index="0" ref="userService" />
</bean>
</property>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://localhost:8443/cas" />
</bean>
</property>
<property name="key" value="localCAS" />
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider" />
</security:authentication-manager>
<int:channel-interceptor order="99">
<bean class="org.springframework.integration.security.channel.SecurityContextPropagationChannelInterceptor"/>
</int:channel-interceptor>
<task:executor id="pool" pool-size="5"/>
<int:poller id="poller" default="true" fixed-rate="1000"/>
<int-security:secured-channels>
<int-security:access-policy pattern=".*" send-access="ROLE_ADMIN" />
</int-security:secured-channels>
<int-http:inbound-channel-adapter path="/query*" supported-methods="GET, POST" channel="requestChannel" />
<int:channel id="requestChannel">
<int:queue/>
</int:channel>
<int-http:outbound-channel-adapter url="http://localhost:8080/prototype-integration-security-web/admin/callback?ticket={ticket}"
http-method="GET"
rest-template="restTemplate"
channel="requestChannel">
<int-http:uri-variable name="ticket" expression="T(org.springframework.security.core.context.SecurityContextHolder).context.authentication.credentials" />
</int-http:outbound-channel-adapter>
</beans>
No additional code is required, this is why I am fond of Spring Integration. Did I do anything wrong or miss some configurations? Please share your ideas, opinions and suggestions. Thanks in advance.
I have never used CAS before, but looks like you don't share how you get headers.serviceTicket.
I think you idea to propagate ticket via URL param is good, but first of all we have to extract it from the incoming URL:
Upon successful login, CAS will redirect the user’s browser back to the original service. It will also include a ticket parameter, which is an opaque string representing the "service ticket". Continuing our earlier example, the URL the browser is redirected to might be https://server3.company.com/webapp/login/cas?ticket=ST-0-ER94xMJmn6pha35CQRoZ.
http://docs.spring.io/spring-security/site/docs/4.2.0.RELEASE/reference/htmlsingle/#cas
For this purpose we can do like this:
<int-http:inbound-channel-adapter path="/user*" supported-methods="GET, POST" channel="userRequestChannel">
<int-http:header name="serviceTicket" expression="#requestParams.ticket"/>
</int-http:inbound-channel-adapter>
Otherwise, please, share exception on the matter and try to trace network traffic to determine a gap.
UPDATE
According to the description on that Spring Security page for CAS we have:
... The principal will be equal to CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, whilst the credentials will be the service ticket opaque value...
So, looks like we don't need to worry about request param in the <int-http:inbound-channel-adapter> and just rely on the SecurityContext in the <int-http:outbound-gateway>:
<int-http:uri-variable name="ticket"
expression="T(org.springframework.security.core.context.SecurityContextHolder).context.authentication.credentials"/>
I have set up an ldap authentication using the following configuration. I need user to authenticate using LDAP data store, and in that I have set the pwdMaxFailure to 2.
Authentication is working correctly however every time I login with wrong password I'm getting the following exception instead of Password lock exception. I don't think Spring LDAP is checking the PwdPolicy (Counting the password attempts) while authenticating the user.
ne = (javax.naming.AuthenticationException)
javax.naming.AuthenticationException: [LDAP: error code 49 -
INVALID_CREDENTIALS: Bind failed: ERR_229 Cannot authenticate user
cn=admin,ou=users,o=organisation]
My LDIF file is
dn: cn=admin,ou=users,o=organization objectClass: inetOrgPerson
objectClass: organizationalPerson objectClass: person objectClass:
top cn: admin sn: Admin uid: admin userPassword::
e1NTSEF9bEtlTUNzLy9OK1JsV2hCWEM2U2ZZNDh0Lzd0OHBlbjFrdjkxN3c9P Q==
createTimestamp: 20141003000008.689Z creatorsName:
0.9.2342.19200300.100.1.1=admin,2.5.4.11=system entryCSN: 20141020004319.002000Z#000000#001#000000 entryDN: cn=admin,ou=users,o=organization entryParentId:
8204b2df-ff5a-413a-a063-4ac30d35bee4 entryUUID::
N2I1MTFlNjYtMDhjZS00YjA3LWIxYzItNTkyOTI3ZGE3ZTBi modifiersName:
0.9.2342.19200300.100.1.1=admin,2.5.4.11=system modifyTimestamp: 20141020004319.002Z pwdFailureTime: 20141020003207.120Z pwdHistory::
MjAxNDEwMDMwMDAwMDguNjgxWiMxLjMuNi4xLjQuMS4xNDY2LjExNS4xMjEuMS4
0MCM4I1lXUnRhVzQ9 pwdPolicySubentry:
cn=default,ou=pwdPolicy,o=organization
dn: cn=default,ou=pwdPolicy,o=organization objectClass: device
objectClass: pwdPolicy objectClass: top cn: default
pwdAttribute: userPassword pwdExpireWarning: 3600 pwdGraceExpire:
1 pwdLockout: TRUE pwdLockoutDuration: 120 pwdMaxAge: 2592000
pwdMaxFailure: 2
And the spring configuration file is as follows
<authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider ref="jdbcProviderManager" />
<authentication-provider ref="ldapProviderManager" />
</authentication-manager>
<bean id="jdbcProviderManager" class="au.com.spring.handler.DBLoginAuthentication">
<property name="userDetailsService" ref="daoAuthenticationProvider" />
</bean>
<bean id="ldapProviderManager" class="au.com.spring.handler.LDAPLoginAuthentication">
<property name="userDetailsService" ref="ldapAuthenticationProvider" />
</bean>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="JdbcUserDetailsManager" />
<property name="passwordEncoder" ref="encoder" />
</bean>
<bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="contextSource" />
<property name="userSearch" ref="ldapUserSearch" />
</bean>
</constructor-arg>
<constructor-arg>
<bean class="au.com.spring.handler.CustomLDAPAuthoritiesPopulator">
<constructor-arg ref="contextSource" />
<constructor-arg value="${group.search}" />
<property name="groupSearchFilter" value="${group.search.filter}" />
<property name="groupRoleAttribute" value="${group.role.att}" />
<property name="rolePrefix" value="ROLE_" />
<property name="searchSubtree" value="true" />
<property name="convertToUpperCase" value="true" />
</bean>
</constructor-arg>
<property name="hideUserNotFoundExceptions" value="false" />
<property name="useAuthenticationRequestCredentials" value="true" />
<property name="userDetailsContextMapper" ref="inetOrgPersonContextMapper" />
</bean>
<bean id="inetOrgPersonContextMapper" class="org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper" />
<bean id="defaultLdapUsernameToDnMapper" class="org.springframework.security.ldap.DefaultLdapUsernameToDnMapper">
<constructor-arg value="${users.search}" />
<constructor-arg value="${uid.att}" />
</bean>
<!--<bean id="authenticationSuccessListener" class="prpa.athos.security.listener.AuthenticationSuccessListener" />-->
<!--<bean id="contextSource" class="org.springframework.security.ldap.ppolicy.PasswordPolicyAwareContextSource">-->
<bean id="contextSource" class="org.springframework.security.ldap.ppolicy.PasswordPolicyAwareContextSource">
<constructor-arg value="ldap://localhost:10389/o=organization" />
<!--<property name=""/>-->
</bean>
I'm getting the following exception instead of Password lock exception.
From the LDAP password policy draft 10:
8.1.1. Fail if the account is locked
If the account is locked as specified in Section 7.1, the server
fails the operation with an appropriate resultCode (i.e.
invalidCredentials (49) in the case of a bind operation, compareFalse
(5) in the case of a compare operation, etc.). The server MAY set
the error: accountLocked (1) in the passwordPolicyResponse in the
controls field of the message.
So the LDAP server is behaving correctly.
I don't think Spring LDAP is checking the PwdPolicy (Counting the password attempts) while authenticating the user
It is the LDAP server that does the checking. Spring would need to provide the extra request control that enables it to see the password-lock status described above. But in any case you never want to disclose to the user why his login failed at login time: that's an information leak. It amounts to telling the attacker that the username is correct, which is never desirable. Let the user enquire why the login failed if he thinks his password is correct, or let him go through the lost-password sequence.
I am trying to connect Ldap from spring security, getting connection errors. Could some one suggest what is wrong with this configuration,
UsernamePasswordAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: Uncategorized exception occured during LDAP processing; nested exception is javax.naming.NamingException: [LDAP: error code 1 - 000004DC: LdapErr: DSID-0C0906E8, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v1db1]; remaining name 'ou=Users,dc=aaa,dc=bbb,dc=ccc,dc=dddd'
at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:191)
config file has,
<sec:authentication-manager alias="myAuthenticationManager">
<sec:authentication-provider ref="myAuthenticationProvider"/>
</sec:authentication-manager>
<bean id="myAuthenticationProvider" 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="contextSource" />
<property name="userSearch" ref="userSearch"/>
</bean>
<bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0" value="ou=Users,dc=aaa,dc=bbb,dc=ccc,dc=dddd"/>
<constructor-arg index="1" value="(sAMAccountName={0})"/>
<constructor-arg index="2" ref="contextSource"/>
<property name="searchSubtree" value="true"/>
</bean>
<bean id="ldapAuthoritiesPopulator" class="com.xxxx.MyLdapAuthoritiesPopulator">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldaps://aaa.com:123/DC=aa,DC=bb,DC=cc,DC=dd"/>
<property name="base" value="DC=aa,DC=bb,DC=cc,DC=dd" />
<!-- <property name="anonymousReadOnly" value="true"/> -->
</bean>
Lets assume user is trying to login with username XXX and password YYY. Usually LDAP authentication works like this:
Bind to the LDAP with technical account
Search for the user with the username XXX => get his DN
Try to bind to the LDAP using found DN and password YYY
Your error is suggesting that you didnt't do the first step (technical account binding) correctly.
Try adding userDn and password to your context source (this is from the official JavaDoc):
<bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://monkeymachine:389/dc=springframework,dc=org"/>
<property name="userDn" value="cn=manager,dc=springframework,dc=org"/>
<property name="password" value="password"/>
</bean>