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>
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") }