Spring security token persistence storage not working
Asked Answered
I

3

8

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)
);
Imelda answered 6/2, 2016 at 14:12 Comment(0)
M
1

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

}
Marry answered 19/5, 2016 at 13:40 Comment(2)
your answer is irrelevant. I'm having the same problem and the issue isn't just with inserting the token data into DB, the remember-me cookie isn't being created in client-side (browser) as well. So I think this is something more than writing token data into DB.Johnsson
Actually it is relevant. Error in DB persistance will cause coodie to fail, see loginSuccess in my answerHovel
S
0

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.

Sitter answered 10/7, 2020 at 1:12 Comment(0)
H
0

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);
    }
}
Hovel answered 13/1, 2023 at 8:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.