One of the advanced Spring Security features is remember-me option which allows user to use single sign to system after user's his credentials has been validated and he has been authenticated by system.

The most secure approach to do that is to use Persistant Token approach. You can read more about it here

 

We can describe this approach works the following way:

 

  • When user signs in to web application additional cookie issues besides on used for section.
  • The login cookie contains the client username, a series identifier, and a token.
  • When a non-logged-in user visits the site and presents a login cookie, the username, series, and token are looked up in the database.
  • If such comnination exists in database, the user is considered authenticated. The used token is removed from the database. A new token is generated, stored in database with the username and the same series identifier, and a new login issued to the client.
  • If the username and series are present but the tokenis incorrect, it's considered as hacking attempts and some action (account block, warning email) should to be done.

 

According to Spring Documentation all those tokens stored in following schema: 

create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null).

 

and the object stored there is org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken.

We will store this object using Spring integration Services  for MongoDb. since we don't tables in MongoDb we will store our token in rememberMeTokens collection. 

 

Let's create some kind of PersistentRememberMeToken CRUD:

 

@Repository(value="persistanceTokenDao")
public class PersistanceTokenDaoImpl implements PersistanceTokenDao {

    @Autowired
    MongoTemplate mongoTemplate;

        @Override
    public void insertToken(PersistentRememberMeToken token){
        //"insert into persistent_logins (username, series, token, last_used) values(?,?,?,?)";
        this.mongoTemplate.insert(token, "rememberMeTokens");
    }
    
    @Override
    public void updateToken(String series, String tokenValue, Date lastUsed){   
    // "update persistent_logins set token = ?, last_used = ? where series = ?";
        Update update= new Update();
        update.set("tokenValue", tokenValue);
        update.set("date",lastUsed);
        Query query = new Query();
        query.addCriteria(Criteria.where("series").is(series));
        this.mongoTemplate.updateFirst(query, update, "rememberMeTokens");
    }
    
    @Override
    public void deleteToken(String username){
    //"delete from persistent_logins where username = ?";
       this.mongoTemplate.remove( new Query(Criteria.where("username").is(username)),"rememberMeTokens");
    }
    
    @Override
    public PersistentRememberMeToken getTokenForSeries(String seriesId){
    //"select username,series,token,last_used from persistent_logins where series = ?";
        PersistentRememberMeToken token = this.mongoTemplate.findOne(new Query(Criteria.where("series").is(seriesId)), PersistentRememberMeToken.class, "rememberMeTokens");
    return token;
    }
}

 

Spring Security class which is responsible on CRUD operations is org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl

this class implements PersistentTokenRepository and this class makes no more then CRUD operation on token. So what we will do is just create our own class which implements this interface:

 

@Component(value="mongoDBTokenRepository")
public class MongoDBTokenRepository implements PersistentTokenRepository {

    @Autowired
    PersistanceTokenDao persistanceTokenDao;
    
    @Override
    public void createNewToken(PersistentRememberMeToken token) {
        persistanceTokenDao.insertToken(token);
    }

    @Override
    public PersistentRememberMeToken getTokenForSeries(String seriesId) {
        return persistanceTokenDao.getTokenForSeries(seriesId);
    }

    @Override
    public void removeUserTokens(String username) {
        persistanceTokenDao.deleteToken(username);
    }

    @Override
    public void updateToken(String series, String tokenValue, Date lastUsed) {
        persistanceTokenDao.updateToken(series, tokenValue, lastUsed);
    }
} 

 

Well. On this stage all our customizations are done and we will continue according to Spring Security documentation.

 
<security:http>
    <security:remember-me services-ref="rememberMeServices" key="myRememberMeKey" />
</security:http>

<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
      <property name="tokenRepository" ref="mongoDBTokenRepository" />
      <property name="userDetailsService" ref="userDetailsService" />
      <property name="key" value="myRememberMeKey" />
      <property name="alwaysRemember" value="true" />
</bean>
 
 
Notice, that we are injection our mongoDBTokenRepository bean into remeberMeServices bean using setter injection named tokenRepository.
now if we login (for example with my credentials danny/somepassword) and will query out token collection in mongoDB we would see out token in database:
 
mongos> db.rememberMeTokens.find();
{ "_id" : ObjectId("4f2f0c2d44ae01e3e77b953f"), "_class" : "org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken", "username" : "danny", "series" : "j7tbXwSONKqYhBGRRM9blw==", "tokenValue" : "D0L2ntuUTzpndPR6Mj+15w==", "date" : ISODate("2012-02-09T23:09:33.145Z") }
 
Using this approach we can keep our tokens in any database no matter if they are relational or not. We just need to implement PersistantTokenRepository interface and inject custom DAO service.