No Spring Security Remember me cookie created when logging in programmatically

7.1k views Asked by At

Right after registration (sign up) I'm logging in my user programmatically via Spring Security:

public register(HttpServletRequest request, String user, String password) {
    ...
    request.login(user, password);
}

This works fine, but it doesn't create the remember-me cookie (although with interactive login the cookie is created fine).

Now I've read in this and this answer, that you have to wire in the implementation of RememberMeServices (I use PersistentTokenBasedRememberMeServices) and then call onLoginSuccess. I haven't been successful to autowire PersistentTokenBasedRememberMeServices.

How to make this work? Is this the right way? Why Spring Security doesn't offer a more convenient way?


P.S.: This is an excerpt from my configuration:

@Configuration
@EnableWebSecurity
public class WebSecConf extends WebSecurityConfigurerAdapter {

    ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .rememberMe()
                .tokenRepository(new MyPersistentTokenRepository())
                .rememberMeCookieName("rememberme")
                .tokenValiditySeconds(60 * 60 * 24) 
                .alwaysRemember(true)
                .useSecureCookie(true)
                .and()
            ....
       ...
    }
}
2

There are 2 answers

8
abaghel On BEST ANSWER

You didn't mention the Spring version. Below configuration will work with Spring 4 but you can modify it for other version. In your WebSecConf class autowire PersistentTokenRepository and UserDetailsService interfaces. Add Bean to get PersistentTokenBasedRememberMeServices instance.

@Configuration
@EnableWebSecurity
public class WebSecConf extends WebSecurityConfigurerAdapter {

@Autowired
PersistentTokenRepository persistenceTokenRepository;
@Autowired
UserDetailsService userDetailsService;
    ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .rememberMe()
                .tokenRepository(persistenceTokenRepository)
                .rememberMeCookieName("rememberme")
                .tokenValiditySeconds(60 * 60 * 24) 
                .alwaysRemember(true)
                .useSecureCookie(true)
                .and()
            ....
       ...
    }

@Bean
public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() {
    PersistentTokenBasedRememberMeServices persistenceTokenBasedservice = new PersistentTokenBasedRememberMeServices("rememberme", userDetailsService, persistenceTokenRepository);
    persistenceTokenBasedservice.setAlwaysRemember(true);
    return persistenceTokenBasedservice;
  }
}

Now in your Controller or class where you are doing programmatic login, autowire PersistentTokenBasedRememberMeServices and add below code inside the method to invoke loginSuccess method.

@Autowired
PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices;

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth != null){
        persistentTokenBasedRememberMeServices.loginSuccess(request, response, auth);
    }
0
Mark On

I've stumbled on this issue and struggled a bit to get everything working correctly, for future reference this is how to set things up.

Define a RememberMeService bean configured to your needs.

Use TokenBasedRememberMeServices if you want a simple hash based token system or PersistentTokenBasedRememberMeServices if you'd rather persist the tokens to database. Both solutions are described in further details here : https://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/remember-me.html

Please note that the constructor first argument is not the cookie name but the key used to validate remember-me tokens.

@Configuration
public class SecurityBeans {
    @Autowire
    PersistentTokenRepository persistenceTokenRepository;
    @Autowired
    UserDetailsService userDetailsService;


    @Bean
    public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() {
        PersistentTokenBasedRememberMeServices persistenceTokenBasedservice = new TokenBasedRememberMeServices("remember-me-key", userDetailsService, persistenceTokenRepository);
        persistenceTokenBasedservice.setCookieName("rememberme");
        persistenceTokenBasedservice.setTokenValiditySeconds(60 * 60 * 24);
        persistenceTokenBasedservice.setAlwaysRemember(true);
        persistenceTokenBasedservice.setUseSecureCookie(true);
        return persistenceTokenBasedservice;
    }
}

You should inject the RememberMeService directly when configuring HttpSecurity. You also have to configure the exact same key as defined in your RememberMeService because the configurer also sets up the RememberMeAuthenticationProvider which checks that the remember-me token key generated by RememberMeService is correct.

@Configuration
@EnableWebSecurity
public class WebSecConf extends WebSecurityConfigurerAdapter {

    @Autowired
    RememberMeServices rememberMeServices;
    ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .rememberMe()
                .rememberMeServices(rememberMeServices)
                .key("remember-me-key")
                .and()
            ....
       ...
    }
}

And finally you should invoke RememberMeService's loginSuccess in your method doing the programmatic login as described in abaghel's answer.