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.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

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


six × 9 =



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.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close