[Java] Spring Boot Tutorial Using Spring Security Authentication

4 minute read

I created a web page with a secure form by referring to the official Spring Boot tutorial (Protecting Web Applications).

environment

Maven 3.6.3 (OK for Maven 3.2 or later)
OS: macOS Mojave version 10.14.6
Text editor: VSCode
Java: 11.0.2

Create

Open Spring Initializr and create a foundation with the following contents.
This time, we will add “Spring Web” and “Thymeleaf” as dependencies.
スクリーンショット 2020-07-15 11.17.56.png

Unzip the resulting zip file and open it in an editor.

Let’s check pom.xml.
The contents of the automatically created pom file are as follows.

pom.xml


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>securing-web</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>securing-web</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

First, let’s create a web application that is not protected by anything.
The structure is as simple as displaying the home screen and “Hello World”.

Create from the home screen. Create a home.html file under src / main / resources / templates.
The contents are as follows.

home.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Example</title>
    </head>
    <body>
        <h1>Welcome!</h1>

        <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
    </body>
</html>

Next, create a hello screen. Create a hello.html file under src / main / resources / templates.
The contents are as follows.

hello.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Hello World!</title>
    </head>
    <body>
        <h1>Hello world!</h1>
    </body>
</html>

Except for the html tag, it is a normal hrml. This will be edited again when implementing security.

Next, create an MvcConfig.java file under src / main / java / com / example / securingweb.
The contents are as follows.

MvcConfig.java


package com.example.securingweb;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MvcConfig implements WebMvcConfigurer {

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

}

The WebMvcConfigurer class inherited by the MvcConfig class overrides the addViewControllers method, so if you create such a Config class, you can implement the MVC configuration without creating a controller.

@Configuration is to allow you to configure various Spring settings. Also, in the Configuration class, add @Configuration to the class.

Now let’s run the app. Nothing is secure at this point.

Run

Execute the following command.

./mvnw spring-boot:run

Once started, try opening http: // localhost: 8080 / hello in your browser.
You should get to the hello screen without logging in.

Set Spring Security

Now, let’s prevent users who are not logged in from displaying the hello screen.

With the current specifications, when the user clicks the link on the home screen, it immediately goes to the hello screen. Let’s use Spring Security to change this so that it only appears after login.

Add the following dependencies to the pom file.

pom.xml


<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <scope>test</scope>
</dependency>

Next, create a WebSecurityConfig.java file under src / main / java / com / example / securingweb with the following contents.

WebSecurityConfig.java


package com.example.securingweb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
                                // "/", "/home"Is accessible to all users
				.antMatchers("/", "/home").permitAll()
                                //Authentication is required for other access
				.anyRequest().authenticated()
				.and()
                        //Login and logout are accessible to all users
			.formLogin()
				.loginPage("/login")
				.permitAll()
				.and()
			.logout()
				.permitAll();
	}

	@Bean
	@Override
	public UserDetailsService userDetailsService() {
		UserDetails user =
                         //Specify default username and password
			 User.withDefaultPasswordEncoder()
				.username("user")
				.password("password")
				.roles("USER")
				.build();

		return new InMemoryUserDetailsManager(user);
	}
}

@EnableWebSecurity
It is an annotation that can use the security function.

permitAll()
A method that allows access to all users. On the contrary, it does not allow anonymous access.

authenticated()
This is a method for requesting authentication.

UserDetailsService overrides the UserDetailsService class to specify user settings.

Now only the logged-in user can go to the hello screen.

Next, create a login page. Create a login.html file under src / main / resources / templates.

login.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://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>

$ {param.error} and $ {param.logout} get the parameters of error and logout.
If error is true, an error message is displayed, and if logout is true, a logout message is displayed.

Finally, display your current username so you can sign out. To do this, greet the current user in hello.html and have them show the signout form.

Now, edit hello.html to the following contents.

hello.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Hello World!</title>
    </head>
    <body>
        <h1 th:inline="text">Hello 
//Get the user name and display it on the screen
[[${#httpServletRequest.remoteUser}]]!</h1>
        <form th:action="@{/logout}" method="post">
            <input type="submit" value="Sign Out"/>
        </form>
    </body>
</html>

The Sign Out form will POST to / logout. When you log out, you will be redirected to / login? Logout.

Finally, I’ll add a little to SecuringWebApplication.java for running the app.
Edit SecuringWebApplication.java under src / main / java / com / example / securingweb to the following contents.

SecuringWebApplication.java


package com.example.securingweb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SecuringWebApplication {

	public static void main(String[] args) throws Throwable {
		SpringApplication.run(SecuringWebApplication.class, args);
	}

}

Run again

Try starting the app with the following command.

./mvnw spring-boot:run

Once started, open http: // localhost: 8080.
Click the link to go to the first page.
スクリーンショット 2020-07-16 11.01.08.png

Click here to move to the login screen.
Type ʻuser in User Name and password` in Password.
スクリーンショット 2020-07-16 11.02.54.png

I was able to log in.

スクリーンショット 2020-07-16 11.03.48.png

If you enter different ones for User Name and Password, they will be played properly.
スクリーンショット 2020-07-16 11.03.21.png

Click the Sign Out button to revoke your authentication and return to the login page with a logout message.
スクリーンショット 2020-07-16 11.04.07.png