Spring security UsernamePasswordAuthenticationFilter with remember-me - spring

I want to add the remember-me into my login page, by reading here , it needs a UserDetailsService. But my UserDetailsService is not getting called, can anyone point where i am wrong? Thanks.
The spring-security.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"
xmlns:p="http://www.springframework.org/schema/p"
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-3.1.xsd">
<!-- configure Spring-Security
auto-config is false.
use-expressions is true: see http://static.springsource.org/spring-security/site/docs/3.1.x/reference/el-access.html
access-denied-page: which page is redirected when login is denied
entry-point-ref: This attribute allows this behaviour to be overridden by defining a customized
AuthenticationEntryPoint bean which will start the authentication process
-->
<security:http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint" >
<!-- define how to handle the url /auth/login, primitAll is used since we defined use-expressions=true -->
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/search" access="hasRole('ROLE_USER')"/>
<!-- The logout element adds support for logging out by navigating to a particular URL.
The default logout URL is /j_spring_security_logout,
but you can set it to something else using the logout-url attribute -->
<security:logout
invalidate-session="true"
logout-success-url="/login" />
<security:custom-filter ref="blacklistFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
</security:http>
<!-- Custom filter to deny unwanted users even though registered -->
<bean id="blacklistFilter" class="com.myapp.filter.BlacklistFilter" />
<!-- Custom filter for username and password. we need to create another 4 beans -->
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:rememberMeServices-ref="rememberMeServices"
p:authenticationManager-ref="customAuthenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" />
<!-- Bean 1: Custom authentication manager. -->
<bean id="customAuthenticationManager" class="com.myapp.manager.CustomAuthenticationManager" />
<!-- bean 2: set the default failure url here -->
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/login?error=true" />
<!-- bean 3: set the default target url here -->
<bean id="customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler"
p:defaultTargetUrl="/search" />
<!-- bean 4: remember me -->
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="myapp"/>
</bean>
<bean id="userDetailsService" class="com.myapp.service.UserDetailsServiceImpl" />
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login"/>
<security:authentication-manager/></beans>
Thanks, Ralph
I added the filter, but UserDetailsServiceImpl is still not called, there is a stop point.
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
logger.info("User details service is called");
return null;
}
and now the configuration is :
<?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"
xmlns:p="http://www.springframework.org/schema/p"
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-3.1.xsd">
<!-- configure Spring-Security
auto-config is false.
use-expressions is true: see http://static.springsource.org/spring-security/site/docs/3.1.x/reference/el-access.html
access-denied-page: which page is redirected when login is denied
entry-point-ref: This attribute allows this behaviour to be overridden by defining a customized
AuthenticationEntryPoint bean which will start the authentication process
-->
<security:http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint" >
<!-- define how to handle the url /auth/login, primitAll is used since we defined use-expressions=true -->
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/search" access="hasRole('ROLE_USER')"/>
<!-- The logout element adds support for logging out by navigating to a particular URL.
The default logout URL is /j_spring_security_logout,
but you can set it to something else using the logout-url attribute -->
<security:logout
invalidate-session="true"
logout-success-url="/login" />
<security:custom-filter ref="blacklistFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
<security:custom-filter ref="rememberMeFilter" position="REMEMBER_ME_FILTER"/>
</security:http>
<!-- Custom filter to deny unwanted users even though registered -->
<bean id="blacklistFilter" class="com.myapp.filter.BlacklistFilter" />
<!-- Custom filter for username and password. we need to create another 4 beans -->
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:rememberMeServices-ref="rememberMeServices"
p:authenticationManager-ref="customAuthenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" />
<!-- Bean 1: Custom authentication manager. -->
<bean id="customAuthenticationManager" class="com.myapp.manager.CustomAuthenticationManager" />
<!-- bean 2: set the default failure url here -->
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/login?error=true" />
<!-- bean 3: set the default target url here -->
<bean id="customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler"
p:defaultTargetUrl="/search" />
<!-- bean 4: remember me -->
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="myapp"/>
</bean>
<bean id="userDetailsService" class="com.myapp.service.UserDetailsServiceImpl" />
<bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="customAuthenticationManager" />
</bean>
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login"/>
<security:authentication-manager alias="theAuthenticationManager"/></beans>

It looks like you forgot to add the RememberMeAuthenticationFilter. -- Have a look at the example in the doc you mentioned, you will see what I mean.
Try to reduce your configuration first, to the very default configuration like in http://www.i-develop.be/blog/2010/02/04/spring-security-remember-me/

Related

too many redirections have happened while trying to open the login page

I use spring and spring security for my application
Here is my spring security xml file
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<security:http auto-config="true">
<security:intercept-url pattern="/**" access="hasRole('ROLE_MEMBRE')" />
<security:intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<!-- <security:form-login login-page="/login.jsp" -->
<!-- default-target-url="/WEB-INF/pages/index.jsp" -->
<!-- authentication-failure-url="/login.jsp" username-parameter="username" -->
<!-- password-parameter="password" /> -->
<security:logout logout-success-url="/login?logout" />
<!-- enable csrf protection -->
<security:csrf />
<security:custom-filter after="FORM_LOGIN_FILTER"
ref="authenticationFilter" />
<security:anonymous enabled="true" />
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider
ref="authenticationProvider" />
</security:authentication-manager>
<bean id="authenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService" />
<property name="passwordEncoder" ref="encoder" />
</bean>
<bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<bean id="userDetailsService" class="spring.security.UserDetailsServiceImpl" />
<bean id="passwordChecker" class="spring.security.impl.PasswordCheckerImpl" />
<bean id="authenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="filterProcessesUrl" value="/login" />
<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
<property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
</bean>
<bean id="authenticationSuccessHandler" class="spring.security.AuthenticationSuccessHandlerImpl">
<property name="defaultTargetUrl" value="/WEB-INF/pages/index.jsp" />
<property name="userManagementService" ref="userManagementService" />
</bean>
<bean id="authenticationFailureHandler" class="spring.security.AuthenticationFailureHandlerImpl">
<property name="defaultFailureUrl" value="/login.jsp" />
<property name="userManagementService" ref="userManagementService" />
</bean>
<bean id="userManagementService" class="spring.security.UserDetailsServiceImpl">
</bean>
</beans>
When I try to open my login page I have the following error message on safari : too many redirection occured when trying to open the page. This can happen when you open a page that is redirected towards another page that redirect again towards the original page
on firefox, the message error is : the page is not redirected correctly. Firefox detected that the server redirect the demand for this adress in a way that will not be successfull
I don't see what could be the cause. Thanks in advance for your answers
I solved my problem by changing the security:http by
<security:http auto-config="true">
<security:intercept-url pattern="/login.jsp" access="isAnonymous()" />
<security:intercept-url pattern="/" access="isAnonymous()" />
<security:logout logout-success-url="/login?logout" />
<!-- enable csrf protection -->
<security:csrf />
<security:custom-filter after="FORM_LOGIN_FILTER"
ref="authenticationFilter" />
<security:anonymous enabled="true" />

Unable to autowire a field in UserDetailsService

I know there are some questions about this topic but mine is a little different. I'm trying to include openID authentication into my project developed with Spring, Spring-security and Spring-MVC.
To achieve the openID auth, some clases are necessary: AccessDeniedHandler and UserDetailsService are configured in applicationContext-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:security="http://www.springframework.org/schema/security"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- turn on global security -->
<security:global-method-security secured-annotations="enabled"/>
<bean id="openIdAuthFailureHandler" class="es.institution.dept.security.MyAccessDeniedHandler"/>
<bean id="userDetailsService" class="es.institution.dept.service.impl.UserDetailsServiceImpl"/>
<security:http auto-config="true">
<security:intercept-url pattern="/welcome*" access="ROLE_USER, ROLE_ADMIN" />
<security:intercept-url pattern="/user/*" access="ROLE_USER, ROLE_ADMIN" />
<security:intercept-url pattern="/rest/*" access="ROLE_USER, ROLE_ADMIN" />
<security:intercept-url pattern="/admin/*" access="ROLE_ADMIN" />
<security:logout logout-success-url="/" />
<security:openid-login login-page="/openidLogin" default-target-url="/welcome" authentication-failure-url="/loginfailed" user-service-ref="userDetailsService"/>
<security:access-denied-handler ref="openIdAuthFailureHandler"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:password-encoder hash="md5"/>
<security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="
SELECT username, password, active as enabled
FROM users WHERE username=?"
authorities-by-username-query="
select ur.username, ur.rolename as authority from users_roles ur
where ur.username=?" />
</security:authentication-provider>
</security:authentication-manager>
</beans>
UserDetailsService is called by Spring when it needs to know user data (username, password, roles...) For this reason, I need to call one of my services (UserService) in UserDetailsService:
public class UserDetailsServiceImpl implements UserDetailsService{
#Autowired
UserService userService;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
es.institution.dept.model.User user = userService.getUserByUsername("mannuk");
if(user == null)
throw new UsernameNotFoundException("User does not exist");
return new User(user.getUsername(), user.getPassword(), user.isActive(), false, false, false, getGrantedAuthorities(username));
}
public List<GrantedAuthority> getGrantedAuthorities(String username) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (Role role : userService.getAllRoles(username)) {
authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
}
return authorities;
}
}
I tried two options:
1)Define #Service annotation in UserDetailsService which throws an Exception during the start up. It says that UserDetails bean does not exist (it is necessary in applicationSecurity-context.xml)
2)Declare a bean definition in applicationContext-security.xml. The startup is OK (no errors) but the UserService is not autowired.
This is my applicationContext.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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
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">
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/adminDB"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="/WEB-INF/mybatis-config.xml" />
</bean>
<bean id="usersMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="rolesMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.RoleMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="groupMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.GroupMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="policyMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.institution.dept.dao.PolicyMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<!-- Json converter bean -->
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"></bean>
</beans>
This is my app-servlet.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:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd">
<!-- Enabling Spring beans auto-discovery -->
<context:component-scan base-package="es.institution.dept" />
<!-- Enabling Spring MVC configuration through annotations -->
<mvc:annotation-driven />
<!-- Enabling Spring Async tasks through annotations -->
<task:annotation-driven />
<mvc:view-controller path="/" view-name="login" />
<!-- Load resources -->
<mvc:resources mapping="/resources/**" location="/resources/"/>
<!-- Bean definitions i18n -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
<!-- Intercepts the change of the locale: example.html?ln=en -->
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="ln" />
</bean>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" >
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
<!-- Register the messages.properties -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="WEB-INF/classes/locale/messages" />
</bean>
<!-- Defining which view resolver to use -->
<bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
Note that UserService is working fine in other places like controllers. It seems to be a problem with the UserDetailsService itself.
If you need more info do not hesitate to write to me. I hope to solve this issue. The solution will be voted and checked.
Beans declared in root application context (applicationContext.xml and applicationContext-security.xml) cannot access beans declared in servler-specific context (app-servlet.xml)
Components of Spring Security (including UserDetailsService) must be declared in root application context
So, you need to declare UserService in applicationContext.xml instead of picking it up by <context:component-scan> in app-servlet.xml.

How to add LDAP cache in Spring LDAP?

I want to cache LDAP user data locally to allow faster queries. Do the Spring LDAP offers such a functionality? How can I do this?
I am using Spring Security 3.1 and Spring LDAP 1.3.1 for authentication and authorization. It would be nice to have a cache for LDAP using built-in mechanism if exists..
Spring LDAP configuration:
applicationContext-ldap.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:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd
">
<!-- Ldap -->
<jee:jndi-lookup id="ldapUrl" jndi-name="appName/ldapUrl" expected-type="java.lang.String" />
<jee:jndi-lookup id="ldapUser" jndi-name="appName/ldapUser" expected-type="java.lang.String" />
<jee:jndi-lookup id="ldapPassword" jndi-name="appName/ldapPassword" expected-type="java.lang.String" />
<!-- for authentication and search purpose -->
<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" ref="ldapUrl" />
<property name="userDn" ref="ldapUser" />
<property name="password" ref="ldapPassword" />
<property name="pooled" value="true" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<property name="contextSource" ref="ldapContextSource" />
</bean>
<!-- for pagination search purpose -->
<bean id="dirContext" factory-bean="ldapContextSource" factory-method="getReadOnlyContext" scope="session"/>
<bean id="singleLdapContextSource" class="org.springframework.ldap.core.support.SingleContextSource" scope="session">
<constructor-arg ref="dirContext"/>
</bean>
<bean id="singleLdapTemplate" class="org.springframework.ldap.core.LdapTemplate" scope="session">
<property name="contextSource" ref="singleLdapContextSource" />
</bean>
</beans>
Spring Security configuration:
spring-security.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.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- This is where we configure Spring-Security -->
<security:http
auto-config="true"
use-expressions="true"
access-denied-page="/auth/denied"
>
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/app/admin" access="permitAll"/>
<security:intercept-url pattern="/app/common" access="hasRole('User')"/>
<security:intercept-url pattern="/viol/home" access="permitAll"/>
<security:intercept-url pattern="/app/users" access="permitAll"/>
<security:intercept-url pattern="/admin/edit/*" access="hasRole('Administrator')"/>
<security:form-login
login-page="/auth/login"
authentication-failure-url="/auth/loginFailure"
default-target-url="/auth/authorize"/>
<security:logout
invalidate-session="true"
logout-success-url="/auth/login"
logout-url="/logout"/>
</security:http>
<security:authentication-manager>
<security:ldap-authentication-provider
server-ref="ldapContextSource"
user-search-filter="(sAMAccountName={0})"
user-search-base="dc=myDomain,dc=com"
/>
</security:authentication-manager>
</beans>
Thank you very much for your help!
If you configure EhCacheBasedUserCache and use ldap-user-service then you can use cache as:
<authentication-manager>
<authentication-provider>
<ldap-user-service
user-search-filter="(sAMAccountName={0})" user-search-base="dc=myDomain,dc=com" cache-ref="userCache" />
</authentication-provider>
</authentication-manager>
I don't think Spring offers client side LDAP caching out of the box, as caching LDAP query results on the client would pose a security risk. The cache will certainly hold stale data at some point, which is not a huge problem if it's e.g. the email/home address of the user, but much worse when it comes to e.g. role assignments and other authentication/authorization related data. You will be much better off by scaling up the server side, so that it's able to handle the load.
That's being said, introducing caching is pretty easy since Spring 3.1, because it provides excellent support for it. In your case it would be enough to use a custom LdapContextSource like the following:
public class CachingLdapContextSource extends AbstractContextSource {
#Override
protected DirContext getDirContextInstance(Hashtable environment)
throws NamingException
{
InitialLdapContext context = new InitialLdapContext(environment, null);
return new CachingDirContextWrapper(context);
}
}
The wrapper class simply delegates all DirContext methods to the underlying implementation and decorates methods to be cached with #Cacheable.
class CachingDirContextWrapper implements DirContext {
private final DirContext delegate;
CachingDirContextWrapper(DirContext delegate) {
this.delegate = delegate;
}
#Override
#Cacheable(value = "search")
public NamingEnumeration<SearchResult> search(...)
{
return delegate.search(name, matchingAttributes, attributesToReturn);
}
...
}
Refer to the official documentation, and this tutorial on details about how to configure a cache storage to be used by Spring.
But once again, you'd better not do this, I think.

Spring Security custom authentication and remember me

I used this tutorial to implement a custom authentication manager. Login and logout works fine.
Now I want to use spring security remember-me authentication. As far as I know, remember-me requires a userDetailService. So I implemented a custom userDetailService.
On the login page I added a checkbox with name _spring_security_remember_me. But, remember-me doesn't work. The remember-me cookie is not set after succesful login. I think this is a configuration problem or do I need to implement a custom remember me to work with custom authentication ?
<input type="checkbox" name="_spring_security_remember_me">stay signed in
My Spring-Security.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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:jee="http://www.springframework.org/schema/jee"
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
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
<security:http auto-config="false" use-expressions="true" access-denied-page="/login?error=true"
entry-point-ref="authenticationEntryPoint" >
<!-- Zugriff auf /login für alle erlauben -->
<security:intercept-url pattern="/login" access="permitAll"/>
<!-- resources -->
<security:intercept-url pattern="/resources/**" access="permitAll"/>
<!--<security:intercept-url pattern="/**" access="permitAll"/> -->
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
<!-- Zugriff auf /admin/** einschränken -->
<security:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"/>
<security:logout
invalidate-session="true"
logout-success-url="/login?logout=true"
logout-url="/j_spring_security_logout"/>
<security:custom-filter ref="blacklistFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
<!-- Session Timeout Seite setzen und
Session Fixation Attack Protection einschalten -->
<security:session-management invalid-session-url="/login?timeout=true"
session-fixation-protection="migrateSession">
<!-- Maxmale Anzahl von Session per User (Doppelanmeldung) -->
<security:concurrency-control max-sessions="3"
error-if-maximum-exceeded="false" />
</security:session-management>
<security:remember-me key="myAppKey" token-validity-seconds="864000" user-service-ref="customUserDetailService"/>
</security:http>
<!-- custom user service -->
<bean id="customUserDetailService" class="com.stefan.app.security.CustomUserDetailsService">
<property name="userBean" ref="userBean" />
</bean>
<!-- Custom filter to deny unwanted users even though registered -->
<bean id="blacklistFilter" class="com.stefan.app.security.filter.BlacklistFilter" />
<!-- Custom filter for username and password. The real customization is done in the customAthenticationManager -->
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:authenticationManager-ref="customAuthenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" />
<!-- Custom authentication manager. In order to authenticate, username and password must not be the same -->
<bean id="customAuthenticationManager" class="com.stefan.app.security.CustomAuthenticationManager">
<property name="userBean" ref="userBean" />
</bean>
<jee:local-slsb id="userBean" jndi-name="java:global/com.stefan.auctionnsiper-ear/app.ejb/UserBean!com.stefan.app.user.UserBeanLocal"
business-interface="com.stefan.app.user.UserBeanLocal"/>
<!-- We just actually need to set the default failure url here -->
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/login?error=true" />
<!-- We just actually need to set the default target url here -->
<bean id="customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler"
p:defaultTargetUrl="/products" />
<!-- The AuthenticationEntryPoint is responsible for redirecting the user to a particular page, like a login page,
whenever the server sends back a response requiring authentication -->
<!-- See Spring-Security Reference 5.4.1 for more info -->
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login"/>
<!-- The tag below has no use but Spring Security needs it to autowire the parent property of
org.springframework.security.authentication.ProviderManager. Otherwise we get an error
A probable bug. This is still under investigation-->
<security:authentication-manager/>
</beans>
Check:
https://stackoverflow.com/questions/8815277/spring-security-tokenbasedremembermeservices-cookiename-ignored
http://forum.springsource.org/showthread.php?130600-Spring-security-remember-me-cookie-configuration-example
Try this:
<security:http>
...
<security:remember-me services-ref="rememberMeServices" />
</security:http>
<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="customUserDetailService"/>
<property name="tokenValiditySeconds" value="864000"/>
<property name="cookieName" value="SPRING_RM"/>
<property name="key" value="myAppKey"/>
</bean>
Besides what JoGo said, don't forget to set the "rememberMeServices" property on your "authenticationFilter", that is:
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:authenticationManager-ref="customAuthenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler"
p:rememberMeServices-ref="rememberMeServices" />
Additionally you'll probably need to remove the remember me cookie when the user logs out, so he's not automatically logged in:
<security:logout
invalidate-session="true"
logout-success-url="/login?logout=true"
logout-url="/j_spring_security_logout"
delete-cookies="SPRING_RM"
/>
I hope this helps.

#Service instantiated twice

Trying to make some experiments with Spring MVC and Spring Security:
#Controller
#RequestMapping("/auth")
public class AuthController {
#Autowired
// #Qualifier("userDetailsService") - tried adding this
private MyUserDetailsService userDetailsService;
...
}
// #Scope("singleton") - tried adding this
#Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
...
}
Complete context.xml that I have:
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<!-- ORIGINAL springmvc-servlet.xml -->
<mvc:annotation-driven />
<mvc:resources mapping="/static/**" location="/static/" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<context:annotation-config />
<context:component-scan base-package="com.xxxxxxxxx" />
<!-- end ORIGINAL springmvc-servlet.xml -->
<!-- FROM springmvc-security.xml -->
<security:global-method-security secured-annotations="enabled">
</security:global-method-security>
<security:http auto-config="true" access-denied-page="/auth/denied">
<security:intercept-url pattern="/admin/*" access="ROLE_ADMIN"/>
<security:intercept-url pattern="/user/*" access="ROLE_USER"/>
<security:intercept-url pattern="/auth/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/auth/register" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:form-login login-page="/auth/login" authentication-failure-url="/auth/login?login_error=true" default-target-url="/user"/>
<security:logout logout-url="/auth/logout" logout-success-url="/" invalidate-session="true"/>
<security:openid-login authentication-failure-url="/login?login_error=t" user-service-ref="openIdUserDetailsService" />
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsService" />
</security:authentication-manager>
<!-- end FROM springmvc-security.xml -->
</beans>
For some reason, there are 2 instances of MyUserDetailsService created. The first one is used by Spring Security and the second one is injected to AuthController. What's the right approach in case I want to have a single instance of MyUserDetailsService?
You haven't shown enough configuration to be certain, but I'd bet money that you're confused about how Spring ApplicationContexts should be managed in a Spring MVC app. My answer to another question about the same problem is almost certainly what you need to read:
Declaring Spring Bean in Parent Context vs Child Context
You've most likely declared your service bean (either explicitly or with a component-scan) in both the root and child contexts of your app. Being a service bean, it should live only in the root context. You may also benefit from reading this answer:
Spring XML file configuration hierarchy help/explanation
this config works for me :
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
and
<bean id="userService" class="com.mydomain.service.UserDetailsServiceImpl" />
A tutorial here.

Resources