“Do not roll your own security”. This is the most common phrase you read when googling on how to implement security for your application. And for good reason. Security is hard and easy to get wrong.

That’s the reason for which most frameworks offer you a solution to the problem. Most of them require time to implement and configure right and need an entire infrastructure for user management. You need to have an Active Directory or a Database. Alternatively, you can use oAuth to authenticate the users via in external provider (Google, Facebook, etc).

Sometimes all you need is a simple user management scheme to store a few users, nothing fancy.

Use-case description

This was the case with a project we have. The application we are talking about is a tool used only by reasonably technical guys and only used internally. Until this point in time there was no need for user authentication in the application but…requirements change. We needed a way to authenticate a relatively small number of users and we didn’t want to setup a database for that. We also didn’t want to do something clumsy like saving the users in a file on disk and for sure we wanted as little improvisation as possible. We needed to strike a compromise.

Since we were already using Spring Boot for the application it seemed reasonable to use Spring Security. Spring Security offers a few options:

  • Ldap
  • oAuth 2.0
  • OpenID
  • Jdbc
  • Etc

See here ( https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#userdetailsservice-implementations )

Since we already had some jdbc wrappers and utilities classes related to do, this seemed the best direction of all to go with.

So we just need a database. But we don’t want to go full length by provisioning an entire database just for a simple table or two where we want to store the users and groups.

 

Introducing H2 database

Citing from Wikipedia : “H2 is a relational database management system written in Java. It can be embedded in Java applications or run in the client-server mode.” (https://en.wikipedia.org/wiki/H2_(DBMS))

H2 database comes by default with Spring Boot configuration and it has an in-memory use. Namely, for quick prototyping you can use this database to test things.

In-memory is not enough for us, since we need also percistency across restarts.

Luckily there is also a file-based option for using H2 database. That is, we can point to a single file on disk and we will have there a SQL-compliant database. No need for a database server or complicated setup.

Implementation

First of all, let’s see what we need in our pom file for all of this to work.

For H2 database, we need the following:

<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
</dependency>

For Thymeleaf:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

For JDBC:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

Now, let’s go to the actual implementation.

First, we need to configure the location of the database and the username/password to connect with. This is done into the application.properties file.

# Datasource
spring.datasource.url=jdbc:h2:file:~/deploy-manager/h2db/h2_database
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

Spring.datasource.url -> is the location to the database file. The file need not exist in advance. It will be created for you.

The rest of the entries should be self-explanatory. Those are the default settings nonetheless.

Configure H2 servlet

We need to configure the servlet for the H2 database. This is useful also for the purpose of accessing and viewing the database via a console in the browser. Create a class which extends the ServletContextIntializer and annotate it with @Configuration.

See below:

@Configuration
public class WebConfiguration implements ServletContextInitializer {

    private static final Logger log = LoggerFactory.getLogger(WebConfiguration.class);

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        initH2Console(servletContext);
    }

    private void initH2Console(ServletContext servletContext) {
        log.info("Starting H2 console");
        ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet());
        h2ConsoleServlet.addMapping("/h2/*");
        h2ConsoleServlet.setLoadOnStartup(1);
    }
}

 

Tip:

If you want an alternative way of viewing database, you can download a utility from here: http://www.h2database.com/html/download.html. This is a java based database viewer. Start it with java -jar. It will automatically open a server on 8082 port and you can use that to view the database in the browser.

Creating necessary database object

We’re done configuring the database. Now we need to create the supporting database objects.

Because Spring Security provides us with an JDBC API, it expects a particular set of objects in the database to be present so that it can store and read usernames/groups from it.

 

@Service
public class H2Dao {
    private static final Logger log = LoggerFactory.getLogger(H2Dao.class);
    
    JdbcTemplate jdbcTemplate;
    
    @Autowired
    public H2Dao( JdbcTemplate jdbcTemplate ){
        jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS " 
                + "USERS(username VARCHAR(255) PRIMARY KEY, password VARCHAR(255), enabled BOOLEAN);");
        jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS " 
                + "authorities(username VARCHAR(255), authority  VARCHAR(255), foreign key (username) references users(username));");
    }
}

 

We initialize the tables in the constructor with the help of “create table if not exists” statements. We put this in the constructor and annotate the class with @Service so that it’s picked up and instantiated by the Spring Boot runtime. When this happens, the constructor is invoked.

To complete this step, we only need to add a field of type H2Dao with the annotation of @Autowired so that it’s instantiated lazily. You can do this in whatever Spring controller class you would like:

@Autowired
private H2Dao dao;

Note:

For a complete overview of tables needed you can look here:

https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#appendix-schema

 

Configuring Security

So, we are now done with the database side. Now we need to configure the actual security side.

To do this, create a class which extends from WebSecurityConfigurerAdapter, like below:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(Constants.BCRYPT_STRENGTH);
    }
    
    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        
      auth.jdbcAuthentication().dataSource(jdbcTemplate.getDataSource())
        .usersByUsernameQuery(
            "select username,password, enabled from users where username=?")
        .authoritiesByUsernameQuery(
            "select username, authority from authorities where username=?")
        .passwordEncoder(passwordEncoder());      
    }   
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()                
                .mvcMatchers("/*").authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
        
        http.authorizeRequests().mvcMatchers("/rest/*").authenticated().and().httpBasic();
    }   
    
    
    @Bean
    @Override
    public JdbcUserDetailsManager userDetailsService() {
        JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
        manager.setJdbcTemplate(jdbcTemplate);
        return manager;
    }    
    
}

configAuthentication() method -> This tells the Spring Runtime what queries to use when searching for username and for authorities of a user. These come with a default usually, but sometimes you maybe have a slightly different layout for the database and need to provide a custom query.

You might wonder why do we need an passwordEncoder. Well, we don’t want to store the passwords in plain text, so we need to hash them before storing them. Since they are hashed, the password that the user enters needs to be first hashed with the same algorithm and only then compared to what is stored. We pass the passwordEncoder into the authentication manager so that it knows what to use.

 

Here, we chose BCryptPasswordEncoder and you can pass the strength as a parameter, between 4 and 20.

Read more about password encoding here: https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#core-services-password-encoding

Important: Keep in mind that when you add a new user programmatically, you have to use the passwordEncoder yourself in order to hash the password before storing it into the database.

User userData = new User(username, passwordEncoder.encode(password), true, true, true, true, grantedAuthorityList);

Tip:

If you want to add some test or default users manually, you need to insert into the database the hashed passwords. In order to hash them with bcrypt you can use this site:

https://www.browserling.com/tools/bcrypt

Remember to also choose the appropriate Strength value.

Configure() method -> Configures the pages that are under protection. In this case, all the pages. Because it is a web application, we need a form page to authentication the user so we configure with the help of “formLogin()” where we are specify the name of the login page.

Our application also provides rest services to consume. We don’t want a form authentication for this, we need basic. It’s very simple to specify basic authentication for a subset of paths as you see:

userDetailsService() method -> This is the core of our configuration. This is where we tell Spring Runtime what to use as a user management. We create a JdbcUserDetailsManager() and set the JdbcTemplate on it.

JdbcTemplate is injected (you can see it as a field annotated with @Autowired). Remember from the beginning that we configured the H2 database into application.properties? Well, this is how Spring knows where to connect to.

    @Bean
    @Override
    public JdbcUserDetailsManager userDetailsService() {
        JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
        manager.setJdbcTemplate(jdbcTemplate);
        return manager;
    }

Login page

Earlier, we’ve setup the login page. This is the place where users are redirects when they try to access a secured page; in our case, any page.

We need to create this page and to register it so that Spring Runtime knows about it.

Under “resources/templates” create a page login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Example </title>
    </head>
    <body>
        <div th:if="${param.error}">
            Invalid username and password.
        </div>
        <div th:if="${param.logout}">
            You have been logged out.
        </div>
        <form th:action="@{/login}" method="post">
            <div><label> User Name : <input type="text" name="username"/> </label></div>
            <div><label> Password: <input type="password" name="password"/> </label></div>
            <div><input type="submit" value="Sign In"/></div>
        </form>
    </body>
</html>

This is a very basic page, enough to collect your username and password and authenticate you, but does the job. We use Thymeleaf for this simple task.

Now let’s register the page.

@Configuration
public class McvConfig extends WebMvcConfigurerAdapter{

    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
    }
    
}

This is self-descriptive. We point to the /login page (we don’t necessarily need to add the .html extension) and give it the generic view name of “login”.

Next time when you access a secured page, you will be redirected to login page, like in the picture

Put your username and password and you’ll be redirected to the original accessed page.

 

There you go, you now have a locally stored database of users and a login page to access secured pages.

Of course, there is more work to do, you need to add TLS encryption, add a user management page and so on. I hope this provides a good start