I've been wondering if there's a way to configure Spring Security LDAP plugin to perform the authentication not in the standard way but as follows:
If one is able to connect and login to the LDAP server then
the user is authenticated.
Read the authorization from this user's account on LDAP
(this is probably the default behavior)
So basically instead of having the master account configured one uses the user/pass passed on by the user to actually perform the login (which if successfull allows the user to fetch the other data).
Thanks in advance!
Hope you're still looking for this. What it sounds like is a good step in the right direction is the BindAuthenticator. You'd have to change the authorities populator to not use the security context source though. I believe the default populator uses a connection pool with the appropriate admin account.
Here is a sample of a setup with a BindAuthenticator and a custom AuthoritiesPopulator.
<bean id="authPopulator" class="org.springframework.security.ldap.populator.CustomLdapAuthoritiesPopulator">
<constructor-arg ref="securityContextSource"/>
<constructor-arg value="ou=Roles,o=data"/>
<property name="groupRoleAttribute" value="resourceGroupType"/>
<property name="groupSearchFilter" value="member={0}" />
</bean>
<bean id="ldap-authentication-provider"
class="org.springframework.security.providers.ldap.LdapAuthenticationProvider" >
<constructor-arg>
<bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg ref="securityContextSource"/>
<property name="userDnPatterns">
<list><value>cn={0},ou=users,o=system</value>
<value>cn={0},ou=users,o=xyz</value>
<value>cn={0},ou=users,ou=external,o=xyz</value>
</list>
</property>
<property name="userSearch" ref="userSearch">
</property>
</bean>
</constructor-arg>
<constructor-arg ref="authPopulator"/>
<s:custom-authentication-provider />
</bean>
Here is my context source def:
<bean id="securityContextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://192.168.254.254:389"/>
<property name="userDn" value="cn=admin,ou=users,o=xyz"/>
<property name="password" value="password"/>
</bean>
I decided to test the context source without a username or password and it appears to work partially. Here is my log output.
[java] - Authentication success: org.springframework.security.providers.UsernamePasswordAuthenticationToken#79107ad5: Principal: org.springframework.security.userdetails.ldap.LdapUserDetailsImpl#3d1a70a7: Username: internalUser; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Password: [PROTECTED]; Authenticated: true; Details: org.springframework.security.ui.WebAuthenticationDetails#0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: a2a3a505521919d529e75c6d14081f6b; Granted Authorities: ROLE_USER
[java] - Updated SecurityContextHolder to contain the following Authentication: 'org.springframework.security.providers.UsernamePasswordAuthenticationToken#79107ad5: Principal: org.springframework.security.userdetails.ldap.LdapUserDetailsImpl#3d1a70a7: Username: internalUser; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Password: [PROTECTED]; Authenticated: true; Details: org.springframework.security.ui.WebAuthenticationDetails#0: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: a2a3a505521919d529e75c6d14081f6b; Granted Authorities: ROLE_USER'
I don't get any errors, but it doesn't populate all of the roles. That might be an eDirectory permissions issue or you might have to create your own authorities populator. The populator does get passed the user dirContext.
Related
Our application used JSP and acegisecurity with spring 2.5. We are trying to port it to use spring-security 3.2.4 with spring 3.
The original application used XML based configuration, so I opted to continue with that. The application was setup to use a DAOAuthProvider, and if the login failed there, to use the LDAP authentication provdider.
I ported the application over, and it now starts up. I can login to the application using the DaoAuthenticationProvider with an InMemoryDaoImpl bean that holds the credentials and roles.
However, I am facing the following problem:
1) I start the webapp.
2) After startup, I login using credentials. This works.
3) Click logout
4) Enter the same credentials as #1, but the login fails in the DAO provider. It then tries to use the LDAP provider, and fails there as well, obviously, because the user does not exist in the LDAP server.
Log Snippets:
2014-07-11 11:50:28,089 DEBUG [:]
[org.springframework.security.web.context.SecurityContextPersistenceFilter]
SecurityContextHolder now cleared, as request processing completed
2014-07-11 11:50:42,744 DEBUG [:]
[org.springframework.security.web.util.AntPathRequestMatcher] Request
'/j_spring_security_check' matched by universal pattern '/**'
2014-07-11 11:50:42,744 DEBUG [:]
[org.springframework.security.web.FilterChainProxy]
/j_spring_security_check at position 1 of 8 in additional filter
chain; firing Filter: 'SecurityCo ntextPersistenceFilter' 2014-07-11
11:50:42,744 DEBUG [:]
[org.springframework.security.web.context.HttpSessionSecurityContextRepository]
HttpSession returned null object for SPRING_SECURITY_CONTEXT
2014-07-11 11:50:42,744 DEBUG [:]
[org.springframework.security.web.context.HttpSessionSecurityContextRepository]
No SecurityContext was available from the HttpSession: org.apache.cata
lina.session.StandardSessionFacade#4bbaa9f0. A new one will be
created. 2014-07-11 11:50:42,744 DEBUG [:]
[org.springframework.security.web.FilterChainProxy]
/j_spring_security_check at position 2 of 8 in additional filter
chain; firing Filter: 'LogoutFilt er' 2014-07-11 11:50:42,744 DEBUG
[:] [org.springframework.security.web.FilterChainProxy]
/j_spring_security_check at position 3 of 8 in additional filter
chain; firing Filter: 'UsernamePa sswordAuthenticationFilter'
2014-07-11 11:50:42,744 DEBUG [:]
[org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter]
Request is to process authentication 2014-07-11 11:50:42,748 DEBUG [:]
[org.springframework.security.authentication.ProviderManager]
Authentication attempt using
org.springframework.security.authentication.dao.DaoAuthenti
cationProvider
Logout Transaction:
2014-07-11 11:52:31,058 DEBUG [:]
[org.springframework.security.web.util.AntPathRequestMatcher] Request
'/j_spring_security_logout' matched by universal pattern '/**'
2014-07-11 11:52:31,058 DEBUG [:]
[org.springframework.security.web.FilterChainProxy]
/j_spring_security_logout at position 1 of 8 in additional filter
chain; firing Filter: 'SecurityContextPersistenceFilter' 2014-07-11
11:52:31,058 DEBUG [:]
[org.springframework.security.web.context.HttpSessionSecurityContextRepository]
Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT:
'org.springframework.security.core.context.SecurityContextImpl#873a5ff1:
Authentication:
org.springframework.security.authentication.UsernamePasswordAuthenticationToken#873a5ff1:
Principal:
org.springframework.security.core.userdetails.User#e6d32b12: Username:
importer; Password: [PROTECTED]; Enabled: true; AccountNonExpired:
true; credentialsNonExpired: true; AccountNonLocked: true; Granted
Authorities: ROLE_IMPORTER_GROUP; Credentials: [PROTECTED];
Authenticated: true; Details:
org.springframework.security.web.authentication.WebAuthenticationDetails#12afc:
RemoteIpAddress: 127.0.0.1; SessionId:
1B2AA0FD58F4509952385C13D2A84D56; Granted Authorities:
ROLE_IMPORTER_GROUP' 2014-07-11 11:52:31,058 DEBUG [:]
[org.springframework.security.web.FilterChainProxy]
/j_spring_security_logout at position 2 of 8 in additional filter
chain; firing Filter: 'LogoutFilter' 2014-07-11 11:52:31,059 DEBUG [:]
[org.springframework.security.web.authentication.logout.LogoutFilter]
Logging out user
'org.springframework.security.authentication.UsernamePasswordAuthenticationToken#873a5ff1:
Principal:
org.springframework.security.core.userdetails.User#e6d32b12: Username:
importer; Password: [PROTECTED]; Enabled: true; AccountNonExpired:
true; credentialsNonExpired: true; AccountNonLocked: true; Granted
Authorities: ROLE_IMPORTER_GROUP; Credentials: [PROTECTED];
Authenticated: true; Details:
org.springframework.security.web.authentication.WebAuthenticationDetails#12afc:
RemoteIpAddress: 127.0.0.1; SessionId:
1B2AA0FD58F4509952385C13D2A84D56; Granted Authorities:
ROLE_IMPORTER_GROUP' and transferring to logout destination 2014-07-11
11:52:31,059 DEBUG [:]
[org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices]
Logout of user importer 2014-07-11 11:52:31,059 DEBUG [:]
[org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices]
Cancelling cookie 2014-07-11 11:52:31,059 DEBUG [:]
[org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler]
Invalidating session: 1B2AA0FD58F4509952385C13D2A84D56 2014-07-11
11:52:31,059 DEBUG [:]
[org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler]
Using default Url: /login.jsp 2014-07-11 11:52:31,060 DEBUG [:]
[org.springframework.security.web.DefaultRedirectStrategy] Redirecting
to '/login.jsp'
Relogin Transaction ( that fails )
2014-07-11 11:52:53,679 DEBUG [:]
[org.springframework.security.web.util.AntPathRequestMatcher] Request
'/j_spring_security_check' matched by universal pattern '/**'
2014-07-11 11:52:53,680 DEBUG [:]
[org.springframework.security.web.FilterChainProxy]
/j_spring_security_check at position 1 of 8 in additional filter
chain; firing Filter: 'SecurityContextPersistenceFilter' 2014-07-11
11:52:53,680 DEBUG [:]
[org.springframework.security.web.context.HttpSessionSecurityContextRepository]
HttpSession returned null object for SPRING_SECURITY_CONTEXT
2014-07-11 11:52:53,680 DEBUG [:]
[org.springframework.security.web.context.HttpSessionSecurityContextRepository]
No SecurityContext was available from the HttpSession:
org.apache.catalina.session.StandardSessionFacade#12224026. A new one
will be created. 2014-07-11 11:52:53,680 DEBUG [:]
[org.springframework.security.web.FilterChainProxy]
/j_spring_security_check at position 2 of 8 in additional filter
chain; firing Filter: 'LogoutFilter' 2014-07-11 11:52:53,680 DEBUG [:]
[org.springframework.security.web.FilterChainProxy]
/j_spring_security_check at position 3 of 8 in additional filter
chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2014-07-11 11:52:53,680 DEBUG [:]
[org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter]
Request is to process authentication 2014-07-11 11:52:53,680 DEBUG [:]
[org.springframework.security.authentication.ProviderManager]
Authentication attempt using
org.springframework.security.authentication.dao.DaoAuthenticationProvider
2014-07-11 11:52:53,680 DEBUG [:]
[org.springframework.security.authentication.dao.DaoAuthenticationProvider]
Authentication failed: password does not match stored value 2014-07-11
11:52:53,681 DEBUG [:]
[org.springframework.security.authentication.ProviderManager]
Authentication attempt using
org.springframework.security.ldap.authentication.LdapAuthenticationProvider
And here is my spring config...
<!-- web security -->
<bean id="filterChainProxy" name="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<bean class="org.springframework.security.web.DefaultSecurityFilterChain">
<constructor-arg>
<bean class="org.springframework.security.web.util.AntPathRequestMatcher">
<constructor-arg value="/**" />
</bean>
</constructor-arg>
<constructor-arg>
<list>
<ref bean="httpSessionContextIntegrationFilter"/>
<ref bean="logoutFilter"/>
<ref bean="authenticationProcessingFilter"/>
<ref bean="securityContextHolderAwareRequestFilter"/>
<ref bean="rememberMeProcessingFilter"/>
<ref bean="anonymousProcessingFilter"/>
<ref bean="exceptionTranslationFilter"/>
<ref bean="filterInvocationInterceptor"/>
</list>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
<bean id="httpSessionContextIntegrationFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="/login.jsp"/> <!-- URL redirected to after logout -->
<constructor-arg>
<list>
<ref bean="rememberMeServices"/>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>
<bean id="authenticationProcessingFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<!-- authenticationManager is obtained from dataimport-authorization-context.xml -->
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler">
<bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<constructor-arg value="/login.jsp?login_error=1"/>
</bean>
</property>
<property name="authenticationSuccessHandler">
<bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/welcome"/>
</bean>
</property>
<property name="filterProcessesUrl" value="/j_spring_security_check"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
<bean id="securityContextHolderAwareRequestFilter" class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter"/>
<bean id="rememberMeProcessingFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<!-- authenticationManager is obtained from dataimport-authorization-context.xml -->
<property name="authenticationManager" ref="authenticationManager"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
<bean id="anonymousProcessingFilter" class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
<property name="key" value="changeThis"/>
<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
</bean>
<bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
<property name="forceHttps" value="false"/>
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessdenied.jsp"/>
</bean>
</property>
</bean>
<bean id="filterInvocationInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<!-- authenticationManager is obtained from dataimport-authorization-context.xml -->
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager">
<bean class="org.springframework.security.access.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleVoter"/>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
</list>
</property>
</bean>
</property>
<!-- property name="objectDefinitionSource">
<value><![CDATA[
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/hello=ROLE_IMPORTER_GROUP
/tasks=ROLE_IMPORTER_GROUP
/import=ROLE_IMPORTER_GROUP
/bvt=ROLE_IMPORTER_GROUP
/process=ROLE_IMPORTER_GROUP
/workflow=ROLE_IMPORTER_GROUP
/listauditlog=ROLE_EDITOR_GROUP
/editpropertytransaction=ROLE_EDITOR_GROUP
/deleteproperty=ROLE_EDITOR_GROUP
/deletepropertysearchresult=ROLE_EDITOR_GROUP
/deletepropertyenqueueresult=ROLE_EDITOR_GROUP
]]></value>
</property -->
<property name="securityMetadataSource">
<security:filter-security-metadata-source>
<security:intercept-url pattern="/login.jsp**" access="ROLE_ANONYMOUS"/>
<security:intercept-url pattern="/favicon.ico" access="ROLE_ANONYMOUS"/>
<security:intercept-url pattern="/listauditlog" access="ROLE_IMPORTER_GROUP"/>
</security:filter-security-metadata-source>
</property>
</bean>
<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="changeThis"/>
</bean>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<!-- This bean is optional; it isn't used by any other bean as it only listens and logs -->
<bean id="authLoggerListener" class="org.springframework.security.authentication.event.LoggerListener">
<property name="logInteractiveAuthenticationSuccessEvents" value="true"/>
</bean>
<!-- This bean is optional; it isn't used by any other bean as it only listens and logs -->
<bean id="authzLoggerListener" class="org.springframework.security.access.event.LoggerListener">
</bean>
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager" >
<!-- bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager" -->
<property name="providers">
<list>
<!-- uncomment the following for specifying user/password/roles from an XML file.
The usernames/password/role XML file will be dataimport-security-context.xml
-->
<ref bean="daoAuthenticationProvider"/>
<!-- uncomment the following for enabling authorization using LDAP.
LDAP settings are read from dataimport-ldap.properties
-->
<ref bean="ldapAuthProvider"/>
<bean class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
<property name="key" value="changeThis"/>
</bean>
<bean class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
<property name="key" value="changeThis"/>
</bean>
</list>
</property>
</bean>
Any help/pointers appreciated.
Thanks in advance.
EDIT 1:
I stepped through the code. I put a breakpoint in DaoAuthenticationProvider::additionalAuthenticationChecks. This is what is happening.
The first time, login succeeds, and a UserDetails service has the password, that matches the one supplied by the user.
The second time, with the same credentials, it seems as if the UserDetails service has the correct username, but password is null. So, the incoming non-null password does not match the "null" password in the UserDetailsService. Due to this, authentication fails.
I found the problem. I was using the spring InMemoryDaoImpl class to store the usernames and passwords. However, as per the spring depcreated api list, this class has been deprecated in favor of the InMemoryUserDetailsManager. When I switched my beans to this, it worked as expected.
One nitpick, it seems that the spring documentation has not kept up to date with this. The documentation for spring-security 3.2.4.RELEASE seems to be out of date on this issue.
http://docs.spring.io/spring-security/site/docs/3.2.4.RELEASE/reference/htmlsingle
I have a simple web app example. Some unsecured pages and some secured pages.
Login is via Spring Security and CAS. Application is mostly working OK - users get directed to CAS login page when requesting a secured resource, login works, and user gets redirected back to the secure page they requested. User will have various authentication properties - available to be checked with Spring Security taglib : http://www.springframework.org/security/tags - eg:
Authentication class :
class org.springframework.security.web.
authentication.preauth.PreAuthenticatedAuthenticationToken
Authentication authorities : [ROLE_ADMIN]
Authentication name : admin-user
Authentication authenticated : true
Authentication principal :
org.springframework.security.core.userdetails.User#bf0d4bda:
Username: admin-user;
Password: [PROTECTED];
Enabled: true;
AccountNonExpired: true;
credentialsNonExpired: true;
AccountNonLocked: true;
Granted Authorities: ROLE_ADMIN
However, once that logged-in user visits a non-secured page, then they appear to be seen as having none of these credentials. Instead, they are see as
Authentication class : class
org.springframework.security.authentication.AnonymousAuthenticationToken
Authentication authorities : [ROLE_ANONYMOUS]
Authentication name : anonymousUser
Authentication authenticated : true
Authentication principal : anonymousUser
So, this makes it impossible to use the security taglib to show or hide page items on the unsecured pages based on whether or not the user is logged in - eg, can't show a logout button if the user is already logged in:
<sec:authorize access="isAuthenticated()" >
<a href='<c:url value="/j_spring_security_logout" />'>logout</a>
</sec:authorize>
or show login button if not logged in:
<sec:authorize access="not isAuthenticated()" >
<a href='<c:url value="/secure/home" />'>login</a>
</sec:authorize>
Logged in user always shows as not authenticated in these pages even when logged in. So always sees the login button, and never the logout. When going back to a secured page, there is no problem. This is not fatal to my app - but the same principle makes it impossible for me to customise the non-secured pages with content only appropriate to logged in users (menus, messages, etc).
Is this normal behaviour with Spring Security + CAS? Or is there a way to make it work as you would expect? Perhaps I have misconfigured the filter chains?
One more thing - I have exeprimented with looking at SPRING_SECURITY_CONTEXT in the session. At first I thought this would work - this gets popluated with Granted Authorities = ROLE_ADMIN on the unsecured pages. So, I thought I could use that to detect if a user was logged in and had the correct authority. However, SPRING_SECURITY_CONTEXT does not get popluated on the very first secured page that gets loaded on the return from CAS - only on subsequent page loads.
You have to tell to the Spring session manager that there is actually a session for the user and that Spring has to maintain it.
<http use-expressions="true"
auto-config="true"
lowercase-comparisons="true"
disable-url-rewriting="true"
access-denied-page="/accessDenied.jsf"
entry-point-ref="casAuthenticationFilterEntryPoint">
[..]
<custom-filter ref="concurrencyFilter" position="CONCURRENT_SESSION_FILTER" />
<custom-filter ref="casAuthenticationFilter" position="CAS_FILTER" />
<logout invalidate-session="true"
logout-success-url="${cas.url}/logout?service=${local.url}/workspace/" />
</http>
That's a part of the security filter chain, that we implemented, the important is the concurrencyFilter that is defined below and the casAuthenticationFilter
<beans:bean id="casAuthenticationFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
<beans:property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
<beans:property name="sessionAuthenticationStrategy" ref="sas" />
</beans:bean>
<beans:bean id="sas"
class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="migrateSessionAttributes" value="true" />
<beans:property name="exceptionIfMaximumExceeded" value="true" />
<beans:property name="alwaysCreateSession" value="true" />
<beans:property name="maximumSessions" value="20" />
</beans:bean>
<beans:bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />
<beans:bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<beans:property name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="expiredUrl" value="/sessionExpired.jsf" />
</beans:bean>
I hope that helps, and is complete. I've to leave now, but if you need more just ask
I'm trying to authenticate and then query our corporate LDAP using Spring LDAP and Spring security. I managed to make authentication work but when I attempt to run search I always get the following exception
In order to perform this operation a successful bind must be completed on the connection
After much research I have a theory that after I authenticate and before I can query I need to bind to connection. I just don't know what and how?
Just to mention - I can successfully browse and search our LDAP using JXplorer so my parameters are correct.
Here's section of my securityContext.xml
<security:http auto-config='true'>
<security:intercept-url pattern="/reports/goodbye.html"
access="ROLE_LOGOUT" />
<security:intercept-url pattern="/reports/**" access="ROLE_USER" />
<security:http-basic />
<security:logout logout-url="/reports/logout"
logout-success-url="/reports/goodbye.html" />
</security:http>
<security:ldap-server url="ldap://s140.foo.com:1389/dc=td,dc=foo,dc=com" />
<security:authentication-manager>
<security:authentication-provider ref="ldapAuthProvider">
</security:authentication-provider>
</security:authentication-manager>
<!-- Security beans -->
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://s140.foo.com:1389/dc=td,dc=foo,dc=com" />
</bean>
<bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
<bean class="foo.bar.reporting.server.security.ldap.LdapAuthenticatorImpl">
<property name="contextFactory" ref="contextSource" />
<property name="principalPrefix" value="TD\" />
<property name="employee" ref="employee"></property>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="foo.bar.reporting.server.security.ldap.LdapAuthoritiesPopulator" />
</constructor-arg>
</bean>
<!-- DAOs -->
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSource" />
Here's code snippet from LdapAuthenticatorImpl that performs authentication. No problem here:
#Override
public DirContextOperations authenticate(final Authentication authentication) {
// Grab the username and password out of the authentication object.
final String name = authentication.getName();
final String principal = this.principalPrefix + name;
String password = "";
if (authentication.getCredentials() != null) {
password = authentication.getCredentials().toString();
}
if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
final InitialLdapContext ldapContext = (InitialLdapContext)
this.contextFactory.getContext(principal, password);
// We need to pass the context back out, so that the auth provider
// can add it to the Authentication object.
final DirContextOperations authAdapter = new DirContextAdapter();
authAdapter.addAttributeValue("ldapContext", ldapContext);
this.employee.setqId(name);
return authAdapter;
} else {
throw new BadCredentialsException("Blank username and/or password!");
}
}
And here's another code snippet from EmployeeDao with my futile attempt to query:
public List<Employee> queryEmployeesByName(String query)
throws BARServerException {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new WhitespaceWildcardsFilter("cn", query));
try {
// the following line throws bind exception
List result = ldapTemplate.search(BASE, filter.encode(),
new AttributesMapper() {
#Override
public Employee mapFromAttributes(Attributes attrs)
throws NamingException {
Employee emp = new Employee((String) attrs.get("cn").get(),
(String) attrs.get("cn").get(),
(String) attrs.get("cn").get());
return emp;
}
});
return result;
} catch (Exception e) {
throw new BarServerException("Failed to query LDAP", e);
}
}
And lastly - the exception I'm getting
org.springframework.ldap.UncategorizedLdapException:
Uncategorized exception occured during LDAP processing; nested exception is
javax.naming.NamingException: [LDAP: error code 1 - 00000000: LdapErr:
DSID-0C090627, comment: In order to perform this operation a successful bind
must be completed on the connection., data 0, vece]; remaining name
'DC=TD,DC=FOO,DC=COM'
It looks like your LDAP is configured to not allow a search without binding to it (no anonymous bind). Also you have implemented PasswordComparisonAuthenticator and not BindAuthenticator to authenticate to LDAP.
You could try modifying your queryEmployeesByName() method to bind and then search, looking at some examples in the doc.
I'm going to accept #Raghuram answer mainly because it got me thinking in the right direction.
Why my code was failing? Turned out - the way I wired it I was trying to perform anonymous search which is prohibited by the system - hence the error.
How to rewire example above to work? First thing (and ugly thing at that) you need to provide user name and password of user that will be used to access the system. Very counterintuitive even when you login and authenticated, even if you are using BindAuthenticator system will not attempt to reuse your credentials. Bummer. So you need to stick 2 parameters into contextSource definition like so:
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://foo.com:389/dc=td,dc=foo,dc=com" />
<!-- TODO - need to hide this or encrypt a password -->
<property name="userDn" value="CN=admin,OU=Application,DC=TD,DC=FOO,DC=COM" />
<property name="password" value="blah" />
</bean>
Doing that allowed me to replace custom implementation of authenticator with generic BindAuthenticator and then my Java search started working
I got the same error, couldn't find a solution.
Finally I changed the application pool identity to network service and everything worked like a charm.
(I have windows authentication and anonymous enabled on my site)
All I want, is a simple remember-me. I read http://static.springsource.org/spring-security/site/docs/3.0.x/reference/remember-me.html
What I have done so far:
Created my own UserDetailsService to work with Hibernate / JPA. My impl. does not consider any remember-me stuff
Considered configuration through appContext <security:remember-me key="89dqj219dn910lsAc12" user-service-ref="jpaUserDetailsService" token-validity-seconds="864000"/>
Checked, that the cookie SPRING_SECURITY_REMEMBER_ME_COOKIE is really set
Logged in to the secured site and it works
When I restart my browser, I keep getting an error:
org.springframework.security.access.AccessDeniedException: Access is denied
Authentication object as a String: org.springframework.security.authentication.RememberMeAuthenticationToken#9ab72a70: Principal: de.myapp.businessobjects.AppUser#61f68b18: Username: myad; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; PersonalInformation: 65537; ; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
And here is my secContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
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.0.xsd">
<security:global-method-security pre-post-annotations="enabled">
</security:global-method-security>
<security:http use-expressions="true" access-denied-page="/accessDenied">
<security:form-login
login-page="/login"
login-processing-url="/loginProcess"
default-target-url="/intro"
authentication-failure-url="/login?login_error=1"
/>
<security:logout
logout-url="/logout"
logout-success-url="/logoutSuccess"/>
<security:intercept-url pattern="/**" access="permitAll"/>
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/styles/**" access="permitAll"/>
<security:intercept-url pattern="/scripts/**" access="permitAll"/>
<security:remember-me key="89dqj219dn910lsAc12" user-service-ref="jpaUserDetailsService"
token-validity-seconds="864000"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="jpaUserDetailsService">
<security:password-encoder hash="sha">
</security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
<bean id="rememberMeFilter" class=
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="jpaUserDetailsService"/>
<property name="key" value="89dqj219dn910lsAc12"/>
</bean>
<bean id="rememberMeAuthenticationProvider" class=
"org.springframework.security.authentication.RememberMeAuthenticationProvider">
<property name="key" value="89dqj219dn910lsAc12"/>
</bean>
</beans>
and finally some debug trace
03:45:14.598 [7225609#qtp-10131947-7] DEBUG o.s.w.b.a.s.HandlerMethodInvoker - Invoking request handler method: public java.lang.String de.myapp.controller.bstController.showbstpage(java.lang.String,javax.servlet.http.HttpServletResponse)
03:45:14.598 [7225609#qtp-10131947-7] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.lang.String de.myapp.controller.bstController.showbstpage(java.lang.String,javax.servlet.http.HttpServletResponse); target is of class [de.myapp.controller.bstController]; Attributes: [[authorize: 'isFullyAuthenticated() and #username == principal.username', filter: 'null', filterTarget: 'null']]
03:45:14.598 [7225609#qtp-10131947-7] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.RememberMeAuthenticationToken#9ab72a70: Principal: de.myapp.businessobjects.AppUser#61f68b18: Username: myad; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; PersonalInformation: 65537; ; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
03:45:14.599 [7225609#qtp-10131947-7] DEBUG o.s.c.c.s.GenericConversionService - Converting value false of [TypeDescriptor java.lang.Boolean] to [TypeDescriptor java.lang.Boolean]
03:45:14.599 [7225609#qtp-10131947-7] TRACE o.s.c.c.s.GenericConversionService - Matched cached converter NO_OP
03:45:14.599 [7225609#qtp-10131947-7] DEBUG o.s.c.c.s.GenericConversionService - Converted to false
03:45:14.599 [7225609#qtp-10131947-7] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter#a866a9, returned: -1
03:45:14.599 [7225609#qtp-10131947-7] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.access.vote.RoleVoter#1ebf305, returned: 0
03:45:14.599 [7225609#qtp-10131947-7] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.access.vote.AuthenticatedVoter#19ffd6f, returned: 0
I really don't know where to continue debugging. What have I missed? Do I have to create my own implementation of remember-me?
I would really appreciate a working sample application that demonstrates the default implementation of springs remember-me...
-------- EDIT -----------
I have just compiled and run the remember-me reference app by springsecurity itself: the spring-security\samples\tutorial account app AND the contact app. Actually, I have exactly the same problem?!?. I have tried firefox, opera and ie ... I am shattered ...
It looks like remember-me authentication was working fine in your application, since you obtain a valid authentication token from the remember-me cookie.
However, the log output indicates that there is a method access control annotation on a controller method bstController.showbstpage which requires "full" authentication, from the expression isFullyAuthenticated() and #username == principal.username. Remember-me doesn't qualify as full authentication, hence the expression rejects the current authentication.
As an aside the intercept-url elements are wrongly ordered since /** is at the top and will be applied to all requests, making the others redundant.
Also, it wouldn't be possible to get the same issue with the sample applications, since they don't require full authentication for any operation, so you must have had some other issue there.
When you login, is the "password" field on your UserDetails object being set to non-null / non-empty value? In my application, the actual authentication is delegated off to another system and I don't store the user-submitted password on my UserDetails object. I couldn't get the RememberMe cookie to work until I set the password property to a value. In my case, I simply defaulted the property to the word "password" just so it wouldn't be null/empty string.
I don't know if that's anything like your scenario, but this drove me crazy until I figured it out.
Greetings!
When I try authenticating against my existing db I'm getting authenticated but I get the 403 page. If I just tried a wrong password I get 'wrong credentials' message as expected.
I tried authenticating per sample app included with SpringSecurity and that worked fine.
security-context.xml:
<?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-2.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
<global-method-security secured-annotations="enabled"></global-method-security>
<http auto-config="true" >
<intercept-url pattern="/admin/**" access="ROLE_TEST" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<form-login
login-page="/login/index.jsp"
default-target-url="/admin/test.jsp"
authentication-failure-url="/login/index.jsp?login_error=1" />
</http>
<authentication-provider user-service-ref="jdbcUserService">
<password-encoder ref="passwordEncoder">
<salt-source system-wide="n103df"/>
</password-encoder>
</authentication-provider>
<beans:bean id="jdbcUserService" class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl">
<beans:property name="rolePrefix" value="" />
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="enableAuthorities" value="true"/>
<beans:property name="enableGroups" value="false"/>
<beans:property name="authoritiesByUsernameQuery" value="SELECT username,authority FROM authorities WHERE username = ?" />
<beans:property name="usersByUsernameQuery" value="SELECT username,password,enabled as enabled FROM users WHERE username = ?" />
<beans:property name="groupAuthoritiesByUsernameQuery" value="" />
</beans:bean>
<beans:bean id="passwordEncoder" class="org.springframework.security.providers.encoding.Md5PasswordEncoder"/>
Will appreciate any help :-)
Thanks in advance!
If you are getting a 403 code, it means that the user does not have the required roles. So, athentication is not the problem, is authorization.
The only way to know what is going on is to put logging level to debug, there should be more info. Post it here.
Does your roles have the 'ROLE_' prefix?
... figured it out. Despite having ROLE_TEST specified in the config file and the same in the 'authority' column of the db, Spring-Sec was expecting ROLE_SUPERVISOR:
[DEBUG,AbstractSecurityInterceptor,http-8084-7] Secure object: FilterInvocation: URL: /admin/test.jsp; ConfigAttributes: [ROLE_TEST]
[DEBUG,AbstractSecurityInterceptor,http-8084-7] Previously Authenticated: org.springframework.security.providers.UsernamePasswordAuthenticationToken#af840ed7: Principal: org.springframework.security.userdetails.User#3ec100: Username: testUser; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_SUPERVISOR; Password: [PROTECTED]; Authenticated: true; Details: org.springframework.security.ui.WebAuthenticationDetails#1c07a: RemoteIpAddress: 127.0.0.1; SessionId: 350B260FAFDDBF04D5CB4AAAB7B8A441; Granted Authorities: ROLE_SUPERVISOR
[DEBUG,ExceptionTranslationFilter,http-8084-7] Access is denied (user is not anonymous); delegating to AccessDeniedHandler
org.springframework.security.AccessDeniedException: Access is denied
at org.springframework.security.vote.AffirmativeBased.decide(AffirmativeBased.java:68)
... now I'm curious, how come?
So after changing ROLE_TEST to ROLE_SUPERVISOR in the config file all worked as expected.