Reputation: 12605
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
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
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
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