Ahmed Hegazy
Ahmed Hegazy

Reputation: 12605

Spring security token persistence storage not working

The problem is that the login and all things are working great except the remember me logic. The cookie is not set and there is no rows are inserted in the database.

This is the security configuration class.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * Spring security configurations.
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // Authorize all requests
                .authorizeRequests()
                    // Allow only admins to access the administration pages
                    .antMatchers("/admin/**").access("hasRole('ADMIN')")
                    // Allow any one to access the register and the main pages only alongside
                    // the resources files that contains css and javascript files
                    .antMatchers("/resources/**", "/register", "/").permitAll()
                    // Authenticate any other request
                    .anyRequest().authenticated()
                    .and()
                // Set up the login form.
                .formLogin()
                    //.successHandler(successHandler())
                    .loginPage("/login")
                    .usernameParameter("email").passwordParameter("password")
                    .permitAll()
                    .and()
                // Enable remember me cookie and persistence storage
                .rememberMe()
                    // Database token repository
                    .tokenRepository(persistentTokenRepository())
                    // Valid for 20 days
                    .tokenValiditySeconds(20 * 24 * 60 * 60)
                    .rememberMeParameter("remember-me")
                    .and()
                // Log out handler
                .logout()
                    .permitAll()
                    .and()
                // Enable Cross-Site Request Forgery
                .csrf();
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
        db.setDataSource(dataSource);
        return db;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        // Provide database authentication and swl queries to fetch the user's data..
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("select email, password, enabled from users where email=?")
                .authoritiesByUsernameQuery("select us.email, ur.role from users us, " +
                        " roles ur where us.role_id=ur.id and us.email=?");
    }
}

and this is the database table for token persistence

CREATE TABLE persistent_logins (
    username VARCHAR(254) NOT NULL,
    series VARCHAR(64) NOT NULL,
    token VARCHAR(64) NOT NULL,
    last_used TIMESTAMP NOT NULL,
    PRIMARY KEY (series)
);

Upvotes: 8

Views: 2325

Answers (3)

tom
tom

Reputation: 2300

Right, so actually, cookie is written in the login success hanlder, so DB persitance error can cause a cookie tofail to be written. My issue was data type on date in persistant table in ms sql:

CREATE TABLE [dbo].[persistent_logins](
--[id] [bigint] IDENTITY(1,1) NOT NULL,
[username] [varchar](64) NOT NULL,
[series] [varchar](64) NOT NULL,
[token] [varchar](64) NOT NULL,
[last_used] [datetime] NOT NULL default CURRENT_TIMESTAMP);

From there SecSecurityConfig is:

 .rememberMe()
  // Valid for 20 days
  .tokenValiditySeconds(20 * 24 * 60 * 60)
   .rememberMeParameter("remember-me")
   .key("yourPrivateKeyOfChoice")
   .tokenRepository(persistentTokenRepository())             
   .rememberMeServices(rememberMeServices())
...
@Bean
public RememberMeServices rememberMeServices() {
    CustomRememberMeServices rememberMeServices = new 
    CustomRememberMeServices("againThePrivateKey", 
            userDetailsService, persistentTokenRepository());
    return rememberMeServices;
  }
@Autowired
@Qualifier("dataSourceEwoForSap")
DriverManagerDataSource dataSource;

@Bean
public PersistentTokenRepository persistentTokenRepository() {
    JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
    db.setCreateTableOnStartup(false);
    db.setDataSource(dataSource);
    return db;

}

...then custom remember me has

public CustomRememberMeServices(String key, UserDetailsService userDetailsService, PersistentTokenRepository tokenRepository) {
    super(key, userDetailsService, tokenRepository);
    this.tokenRepository = tokenRepository;
    this.key = key;
}


      
@Override
protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
    String username = ((User) successfulAuthentication.getPrincipal()).getEmail();
    logger.debug("Creating new persistent login for user " + username);
    PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(username, generateSeriesData(), generateTokenData(), new Date());
    try {
        tokenRepository.createNewToken(persistentToken);
        addCookie(persistentToken, request, response);
    } catch (Exception e) {
        logger.error("Failed to save persistent token ", e);
    }
}

Upvotes: 0

invzbl3
invzbl3

Reputation: 6440

I've reproduced the same issue. Using debug I've checked loginSuccess() method for AbstractRememberMeServices class.

Internal logic was like:

public final void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
    if (!this.rememberMeRequested(request, this.parameter)) {
        this.logger.debug("Remember-me login not requested.");
    } else {
        this.onLoginSuccess(request, response, successfulAuthentication);
    }
}

it turned out that I hadn’t been tagged Remember Me tag while the user was logging in, so I couldn't call onLoginSuccess() method and fell into the block if instead of else.

After I marked the flag, I was able to persist the token and cookies.

Notice: logic can be taken from the answer mentioned by @FuSsA.

Upvotes: 0

FuSsA
FuSsA

Reputation: 4297

Spring Security comes with 2 implementation of PersistentTokenRepository : JdbcTokenRepositoryImpl and InMemoryTokenRepositoryImpl. I'm using Hibernate in my application, i create a custom implementation using Hibernate instead of using JDBC.

@Repository("tokenRepositoryDao")
@Transactional
public class HibernateTokenRepositoryImpl extends AbstractDao<String, PersistentLogin>
        implements PersistentTokenRepository {

    static final Logger logger = LoggerFactory.getLogger(HibernateTokenRepositoryImpl.class);

    @Override
    public void createNewToken(PersistentRememberMeToken token) {
        logger.info("Creating Token for user : {}", token.getUsername());
        PersistentLogin persistentLogin = new PersistentLogin();
        persistentLogin.setUsername(token.getUsername());
        persistentLogin.setSeries(token.getSeries());
        persistentLogin.setToken(token.getTokenValue());
        persistentLogin.setLast_used(token.getDate());
        persist(persistentLogin);

    }

    @Override
    public PersistentRememberMeToken getTokenForSeries(String seriesId) {
        logger.info("Fetch Token if any for seriesId : {}", seriesId);
        try {
            Criteria crit = createEntityCriteria();
            crit.add(Restrictions.eq("series", seriesId));
            PersistentLogin persistentLogin = (PersistentLogin) crit.uniqueResult();

            return new PersistentRememberMeToken(persistentLogin.getUsername(), persistentLogin.getSeries(),
                    persistentLogin.getToken(), persistentLogin.getLast_used());
        } catch (Exception e) {
            logger.info("Token not found...");
            return null;
        }
    }

    @Override
    public void removeUserTokens(String username) {
        logger.info("Removing Token if any for user : {}", username);
        Criteria crit = createEntityCriteria();
        crit.add(Restrictions.eq("username", username));
        PersistentLogin persistentLogin = (PersistentLogin) crit.uniqueResult();
        if (persistentLogin != null) {
            logger.info("rememberMe was selected");
            delete(persistentLogin);
        }

    }

    @Override
    public void updateToken(String seriesId, String tokenValue, Date lastUsed) {
        logger.info("Updating Token for seriesId : {}", seriesId);
        PersistentLogin persistentLogin = getByKey(seriesId);
        persistentLogin.setToken(tokenValue);
        persistentLogin.setLast_used(lastUsed);
        update(persistentLogin);
    }

}

Upvotes: 1

Related Questions