Lesson 3: Remember Me with Persistence
1. Main Goal
The focus of this lesson is to switch from the
cookie based remember-me implementation to the persistence backed
implemented that the framework provides.
2. Lesson Notes
The relevant module you need to import when you're starting with this lesson is: m3-lesson3-start
If you want to skip and see the complete implementation, feel free to jump ahead and import: m3-lesson3
A quick note is that this lesson is using MySQL, so you'll need to naturally have a MySQL Server running locally.
The credentials used in the code of this lesson are: [email protected]/pass (data.sql).
2.1. The Theory
The persistence backed implementation of remember-me is more secure than its cookie based counterpart with a mechanism that no longer relies on the username and passsword.
This means that the token generation mechanism doesn't use either the username or password to generate the cookie value.
And as a direct consequence, if a cookie gets compromised, it's enough the delete the persisted tokens and the user no longer needs to change their password to invalidate issued tokens. Also, the valid username is no longer exposed.
Next - the new remember me mechanism no longer validates the signature being present in the cookie - but the fact that the token is present in the DB.
2.2. The Implementation
Let's start by switching our persistence over to using MySQL just so that we can easily check the DB and see these tokens.
We're first going to define the MySQL dependency in Maven:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
We are going to make use of Boot and the application.properties file to configure our persistence to use MySQL:
spring.datasource.url=jdbc:mysql://localhost/lss33?createDatabaseIfNotExist=true spring.datasource.username=restUser spring.datasource.password=restmy5ql spring.datasource.driver-class-name=com.mysql.jdbc.Driver
Now, over in our security configure, we are going to wire in the data source and start using it when defining a persistence repository for our tokens :
@Autowired private DataSource dataSource; @Bean public PersistentTokenRepository persistentTokenRepository() { JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); jdbcTokenRepository.setDataSource(dataSource); return jdbcTokenRepository; }
Finally, we're going to make the switch from the cookie based implementation to the persistence backed one:
.tokenRepository(persistentTokenRepository())
2.3. The DB Structure
Before we run everything, we need to make sure the DB structure necessary for storing our tokens is created as well:
create table if not exists persistent_logins ( username varchar_ignorecase(100) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null );
We can make use of the data.sql file that Spring Boot uses by default for setting up data on startup here.
To run this file on startup for a MySql database, we also have to set the initialization-mode property to always:
spring.datasource.initialization-mode=always
But, keep in mind that there are a lot of options to set up an
initial DB structure when a project starts up - besides Spring Boot.
2.4. The Series
Finally, one quick note - I touched on the concept of the series in the video.
This series field identifies the login of the user and doesn't change for duration of the entire persistent session.
It also contains a random token, regenerated each time a user logs in via the persisted remember-me functions.
The goal is to make brute force attacks impossible in practice.
2.5. Differences in Boot 2
Note that the video lesson is focused on Boot 1. Let's go over the relevant changes in the current 2.x version of Boot.
First of all, some keys of the common application properties have changed:
- server.tomcat.access_log_enabled has been renamed into server.tomcat.accesslog.enabled
- server.sessionTimeout has been renamed into server.servlet.session.timeout
Then, a newer version of the MySql data source driver has been released. Therefore, we should now initialise the spring.datasource.driver-class-name to the new version: com.mysql.cj.jdbc.Driver.
Then, as we've already mentioned in previous lessons, in Spring Boot 2, it's required to specify the password encryption in order to configure the user detail service:
@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); }
It allows us as well to avoid storing the passwords in plain text format. It means that in our data.sql script that we use to populate the database once the application starts, we can now provide the encoded version of the password:
insert into user (id, email, password, ...) values (1, '[email protected]', NNN, ...);
where NNN is the encoded version of the user's password. It is important that we calculate the encoded version using the same encoder with which we configure the user detail service, i.e. the BCryptPasswordEncoder.
3. Resources
- Remember-Me - Persistent Token Approach (in the Official Reference)