About Yuan Ji

Yuan is a passionate Java programmer and open source evangelist. He is eager to learn new technologies and loves clean and beautiful application design. He lives in Edmonton, Canada as an independent consultant and contractor.

Add RememberMe Authentication With Spring Security

I mentioned in my post Add Social Login to Jiwhiz Blog that the RememberMe function was not working with Spring Social Security. Well, it is because the application is not authenticating the user by username and password now, and is totally depending on social websites (like Google, Facebook and Twitter) to do the job. The default Spring Security configuration cannot handle this situation. Spring Security might be the most complicated software among all Spring Portfolio projects. There are about 10 filters needing to be set up correctly to get a very simple Web application to work with security. To simplify application development, Spring Security provides namespace configuration since version 2.0 to automatically set up all the necessary components together, so developers don’t need to figure out the details. It works very well for most of the Web applications, unless your application is different from a traditional one.

After I changed my website login process from username password authentication to Spring Social Security without a password, the old configuration for remember-me didn’t work anymore. The Spring Security Reference document has very few explanations about Remember-Me Authentication, so I bought the book Spring Security 3.1 written by Rob Winch, the project lead of Spring Security. The book has an entire chapter talking about Remember-Me Services, and it helped me a lot to understand how remember-me works in Spring Security. After reading the book, I feel it is much easier to read the Spring Security source code, and reading the source code is always enjoyable.

Since I’m not storing passwords for users’ accounts, the default TokenBasedRememberMeServices cannot work with my application, and I don’t want to create my own RememberMeServices – too much work. Fortunately, there is another Persistent Token Approach, that is to store tokens into the database and compare tokens inside cookies. All I need is to customize PersistentTokenBasedRememberMeServices in my application with PersistentTokenRepository to store the tokens. Spring Security provides a JDBC implementation of PersistentTokenRepository, and I found it is trivial to write my own implementation for MongoDB after reading the source code.

The first step is to store data of PersistentRememberMeToken to MongoDB. I need to add a domain entity class for it:

@Document(collection = 'RememberMeToken')
public class RememberMeToken extends BaseEntity{

    private String username;

    @Indexed
    private String series;

    private String tokenValue;

    private Date date;

... // getter/setter omitted

    public RememberMeToken(){

    }

    public RememberMeToken(PersistentRememberMeToken token){
        this.series = token.getSeries();
        this.username = token.getUsername();
        this.tokenValue = token.getTokenValue();
        this.date = token.getDate();
    }

}

Next, use Spring Data to add a repository for the entity:

public interface RememberMeTokenRepository extends MongoRepository<RememberMeToken, String>{
    RememberMeToken findBySeries(String series);
    List<RememberMeToken> findByUsername(String username);
}

Then the only relatively heavy coding is to implement PersistentTokenRepository for MongoDB:

public class MongoPersistentTokenRepositoryImpl implements PersistentTokenRepository {

    private final RememberMeTokenRepository rememberMeTokenRepository;

    public MongoPersistentTokenRepositoryImpl(RememberMeTokenRepository rememberMeTokenRepository){
        this.rememberMeTokenRepository = rememberMeTokenRepository;
    }

    @Override
    public void createNewToken(PersistentRememberMeToken token) {
        RememberMeToken newToken = new RememberMeToken(token);
        this.rememberMeTokenRepository.save(newToken);
    }

    @Override
    public void updateToken(String series, String tokenValue, Date lastUsed) {
        RememberMeToken token = this.rememberMeTokenRepository.findBySeries(series);
        if (token != null){
            token.setTokenValue(tokenValue);
            token.setDate(lastUsed);
            this.rememberMeTokenRepository.save(token);
        }

    }

    @Override
    public PersistentRememberMeToken getTokenForSeries(String seriesId) {
        RememberMeToken token = this.rememberMeTokenRepository.findBySeries(seriesId);
        return new PersistentRememberMeToken(token.getUsername(), token.getSeries(), token.getTokenValue(), token.getDate());
    }

    @Override
    public void removeUserTokens(String username) {
        List<RememberMeToken> tokens = this.rememberMeTokenRepository.findByUsername(username);
        this.rememberMeTokenRepository.delete(tokens);
    }
}

The rest of the work is all configuration. I need to wire them together in Java config class:

@Configuration
public class SocialAndSecurityConfig {
    @Inject
    private Environment environment;

    @Inject
    private AccountService accountService;

    @Inject
    private AuthenticationManager authenticationManager;

    @Inject
    private RememberMeTokenRepository rememberMeTokenRepository;

...

    @Bean
    public RememberMeServices rememberMeServices(){
        PersistentTokenBasedRememberMeServices rememberMeServices = new PersistentTokenBasedRememberMeServices(
                        environment.getProperty('application.key'), accountService, persistentTokenRepository());
        rememberMeServices.setAlwaysRemember(true);
        return rememberMeServices;
    }

    @Bean 
    public RememberMeAuthenticationProvider rememberMeAuthenticationProvider(){
        RememberMeAuthenticationProvider rememberMeAuthenticationProvider = 
                        new RememberMeAuthenticationProvider(environment.getProperty('application.key'));
        return rememberMeAuthenticationProvider; 
    }

    @Bean 
    public PersistentTokenRepository persistentTokenRepository() {
        return new MongoPersistentTokenRepositoryImpl(rememberMeTokenRepository);
    }
}

The last step is to add the remember-me service to security xml config file, which is the last piece of xml config and we cannot eliminate it now. (Update: a new project Spring Security Java Config will replace xml configuration with Java config in Spring Security.)

<?xml version='1.0' encoding='UTF-8'?>
<beans:beans xmlns='http://www.springframework.org/schema/security'
    xmlns:beans='http://www.springframework.org/schema/beans'
    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xsi:schemaLocation='
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd'>

    <http use-expressions='true' entry-point-ref='socialAuthenticationEntryPoint'>
        <custom-filter position='PRE_AUTH_FILTER' ref='socialAuthenticationFilter' />
        <logout logout-url='/signout' delete-cookies='JSESSIONID' />
        <remember-me services-ref='rememberMeServices' />
        <!-- Configure these elements to secure URIs in your application -->
        <intercept-url pattern='http://a3ab771892fd198a96736e50.javacodegeeks.netdna-cdn.com/favicon.ico' access='permitAll' />
        <intercept-url pattern='/robots.txt' access='permitAll' />
        <intercept-url pattern='/resources/**' access='permitAll' />
        <intercept-url pattern='/signin' access='permitAll'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/signin/*' access='permitAll'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/presentation/**' access='hasRole('ROLE_USER')'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/myAccount/**' access='hasRole('ROLE_USER')'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/myPost/**' access='hasRole('ROLE_AUTHOR')'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/admin/**' access='hasRole('ROLE_ADMIN')'
            requires-channel='#{environment['application.secureChannel']}' />
        <intercept-url pattern='/**' access='permitAll' />

    </http>

    <authentication-manager alias='authenticationManager'>
        <authentication-provider ref='socialAuthenticationProvider' />
        <authentication-provider ref='rememberMeAuthenticationProvider' />
    </authentication-manager>

</beans:beans>

That’s all for adding Remember-Me Authentication to my blog application. Now you can login through Google/Facebook/Twitter to my website, and the website will consistently remember you for the next two weeks.
 

Reference: Add RememberMe Authentication With Spring Security from our JCG partner Yuan Ji at the Jiwhiz blog.

Related Whitepaper:

Web Application Security; How to Minimize Prevalent Risk of Attacks

Vulnerabilities in web applications are now the largest vector of enterprise security attacks.

Stories about exploits that compromise sensitive data frequently mention culprits such as cross-site scripting, SQL injection, and buffer overflow. Vulnerabilities like these fall often outside the traditional expertise of network security managers.

Get it Now!  

2 Responses to "Add RememberMe Authentication With Spring Security"

  1. Poonam says:

    Hi Yuan,
    Thank you for this article. Can you please provide an example project for facebook login with remember me services? In my project, I have integrated Facebook Javascript SDK and I get the facebook id and the facebook oauth token which I then pass it to a controller. In this setup, I am not sure how does the authentication manger fits in. Is this the right setup? I wanted to know how you have implemented “socialAuthenticationProvider”, “socialAuthenticationEntryPoint” and “socialAuthenticationFilter”.
    It would be great if you could provide an example project with the same.

    Thanks!

  2. John Simth says:

    Not working

Leave a Reply


nine − = 5



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books