Spring Security makes it really simple to authenticate users against a database. This tutorial builds on the previous tutorial of configuring Spring Security to secure web applications.

Database Schema

Spring Security is happy to do all of the work of querying a database and validating user information provided your database conforms to the correct database schema (note, you are free to customize). Here is the sql script that is used to configure an example datasource for this project that is based of the one provided in the Spring documetation.

/* See https://docs.spring.io/spring-security/site/docs/current/reference/html/appendix-schema.html */ DROP TABLE IF EXISTS persistent_logins; DROP TABLE IF EXISTS group_members; DROP TABLE IF EXISTS group_authorities; DROP TABLE IF EXISTS groups; DROP TABLE IF EXISTS authorities; DROP TABLE IF EXISTS users; create table users( username varchar_ignorecase(50) not null primary key, password varchar_ignorecase(50) not null, enabled boolean not null ); create table authorities ( username varchar_ignorecase(50) not null, authority varchar_ignorecase(50) not null, constraint fk_authorities_users foreign key(username) references users(username) ); create unique index ix_auth_username on authorities (username,authority); create table groups ( id bigint generated by default as identity(start with 0) primary key, group_name varchar_ignorecase(50) not null ); create table group_authorities ( group_id bigint not null, authority varchar(50) not null, constraint fk_group_authorities_group foreign key(group_id) references groups(id) ); create table group_members ( id bigint generated by default as identity(start with 0) primary key, username varchar(50) not null, group_id bigint not null, constraint fk_group_members_group foreign key(group_id) references groups(id) ); create table persistent_logins ( username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null ); insert into users values('bob_belcher', 'burger_bob', true); insert into authorities values ('bob_belcher', 'user');

This script drops all tables if they exist and then recreates the database tables. It also populates the database with a user: bob_belcher. Creating and destroying the DB in this fashion is useful for both development purposes and unit testing. Naturally, a production machine would preserve the data each time.

Spring Configuration

Configuring Spring Security to work with our database is a complete breeze at this point. We start by creating two bean definitions for both a data source and a jdbcTemplate.

@Configuration class DataConfig { @Bean(name = arrayOf("dataSource")) fun dataSource() : DataSource { //This will create a new embedded database and run the schema.sql script return EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("schema.sql") .build() } @Bean fun jdbcTemplate(@Qualifier("dataSource") dataSource: DataSource) : JdbcOperations { return JdbcTemplate(dataSource) } }

Since I am using Spring Boot, I did qualify our dataSource bean so that the container knew which bean I wanted to use for our datasource.

Now that we have our data source configured, we just need to tell Spring Security about it. It’s not very difficult.

@Configuration //Make this as a configuration class @EnableWebSecurity //Turn on Web Security class SecurityWebInitializer( //Inject our datasource into this class for the AuthenticationManagerBuilder @Autowired @Qualifier("dataSource") val dataSource: DataSource) : WebSecurityConfigurerAdapter(){ override fun configure(http: HttpSecurity) { http .formLogin() .and() .logout() .logoutSuccessUrl("/") .and() .rememberMe() .tokenRepository(JdbcTokenRepositoryImpl()) .tokenValiditySeconds(2419200) .key("BurgerBob") .and() .httpBasic() .and() .authorizeRequests() .antMatchers("/").authenticated() .anyRequest().permitAll() } override fun configure(auth: AuthenticationManagerBuilder) { //As long as our database schema conforms to the default queries //we can use jdbcAuthentication and pass in our data source //Spring will do the rest of the work for us auth.jdbcAuthentication().dataSource(dataSource) } }

In this case, all that is needed is to call auth.jdbcAuthentication().dataSource and pass in our dataSource object. Spring Security takes it from there.

Conclusion

Here is a video of this in action.



You can grab the entire code from my Github page here.