[JAVA] Create login / logout function with Spring Security according to Spring official guide [For beginners]

*** Addition: The description in home.html and the screenshot image were incorrect, so I corrected it. *** ***

Premise / Environment

I wanted to create a login / logout function using Spring Security, but I was addicted to various things and got stuck for about 2 days. Finally, I decided to make it according to the official Spring guide. →https://spring.io/guides/gs/securing-web/

Since the whole sentence is in English, I managed to understand it while writing while looking up the meaning, so I will share it for beginners.

environment:

Create a project with Spring Boot

New → Spring Starter Project Select and create with the following items.

This is absolutely! That's not to say, but I think that the movement will change depending on the environment, so I think that it is better for people who want to copy and paste for the time being.

Introducing Spring Security

Now, let's modify the created project.

First, add the description "I'll use Spring Security!" To pom.xml. (Spring officially does it after creating the Hello World application, but this time I will do it first) What is pom.xml? If so, please try google. Roughly speaking, pom.xml is like a Maven config file. (Excuse me if I am wrong)

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 http://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.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>login-Training-SpringBoot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>login-Training-SpringBoot</name>
    <description>Demo project for Spring Boot</description>

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

    <dependencies>
        //Add here!
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <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>
        </dependency>
    </dependencies>

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

</project>

Between <dependencies> </ dependencies>

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

To add. With this, the setting "I will use Spring Security!" Is completed.

First, create an unsecured application

First of all, we will create an application that Hello World without security.

Creating home.html

Because it uses Thymeleaf template

src/main/resources/templates/home.html

Create an HTML file under the templates folder as shown below.

home.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>
		<meta charset="UTF-8">
		<title>Hello,SpringSecurity!</title>
	</head>
	<body>
		<h1>WELCOME!</h1>
		<br>
        <p>Login<a th:href="@{/hello}">Here</a></p>
     <p><a th:href="@{/}">New user registration</a></p>
	</body>
</html>
  1. Description in html tag: Thymeleaf declaration, Security declaration, XHTML namespace
  2. Link to "/ hello"

First about namespaces. A namespace is a "namespace" created so that a computer can identify a combination of multiple vocabularies on XML where the markup language can be freely designed. (literally) It's too conceptual and hard to understand, This article is easy to understand about namespaces. A brief description of the XML namespace

The "xmlns" used this time is called "XML namespace", and it seems that the computer can be identified by combining the vocabulary type and URI.

Also,

In many cases, the http: scheme is used for the URI that indicates the namespace, so I get the impression that something can be retrieved, but this is primarily the role of "ID" as the name implies. Please understand that it will be fulfilled

Well, I'm sure you think it's a tag identification ID here.

Here we set the default namespace with xmlns =" " and Setting th: with xmlns: th =" ", The sec: is set with xmlns: sec =" "`.

Next is the link to "/ hello" Here, the link is posted in Thymeleaf notation. The @ {...} part of the th: href attribute is the link destination. In this case, it means that it is linked to "localhost: 8080 / hello". (Although the link for new user registration is also described, it is not implemented this time because it is only displayed and not implemented)

How to write Thymeleaf Reference: Completely master Thymeleaf with the minimum required sample

[hello.html] Now, create hello.html that describes the link earlier.

hello.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <meta charset="UTF-8">
        <title>Hello,SpringSecurity!</title>
    </head>
    <body>
        <h3>Welcome!</h3>
    </body>
</html>

Welcome here once! Just display.

Creating the MvcConfig class

Next, create a class to set MVC. src/main/java/hello/MvcConfig.java Place in.

MvcConfig.java


package hello;

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");
    }
}
  1. Add @Configuration annotation
  2. implements WebMvcConfigurer

About implements: Implement the interface! How to use implements in Java [For beginners]

Roughly speaking interface (specification)-> implemented class (insert the actual definition into the specification)-> use that method It is an image like that.

Security your application

Create security setting class (WebSecurityConfig.java)

Create a configuration class that inherits from the WebSecurityConfigurerAdapter class. src/main/java/hello/WebSecurityConfig.java Arranged like this.

WebSecurityConfig.java


package hello;

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()
                // "/"When/home is accessible to all users
                .antMatchers("/","/home").permitAll()
                //Authentication is required for access to other than the above
                .anyRequest().authenticated()
                .and()
            //Login and logout URL specifications and permissions for all users
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        UserDetails user =
                User.withDefaultPasswordEncoder()
                    .username("user")
                    .password("pass")
                    .roles("USER")
                    .build();
        return new  InMemoryUserDetailsManager(user);
    }
}
  1. Inherit WebSecurityConfigurerAdapter
  2. Write @EnableWebSecurity annotation
  3. Define URL path and permissions
  4. userDetailsService () uses @Bean annotation to set user name and password

First of all, inheritance should be inherited when creating a class.

So, @EnableWebSecurity, but it seems that you can use the WebSecurity function by writing this. Well, it's a security feature on the Web! That is the description. I'll leave the exact definition to Google Sensei. ..

As for the URL path and access rights, as described in the comment. Allow access to all users with permitAll () Requires authentication with authenticated () I feel like.

Finally, the userDetailsService () part, First, @Bean is the same as JavaBeans, You can generate a user's bean (concrete contents) by using @Bean annotation. It also overrides the UserDetailsService class, where you specify the username and password. Here, username is username, password is pass, and role is USER.

Create login.html

login.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <meta charset="UTF-8">
        <title>LoginPage</title>
    </head>
    <body>
        <div th:if="${param.error}">
The user name or password is incorrect
        </div>
        <div th:if="${param.logout}">
Logped out
        </div>
        <form th:action="@{/login}" method="post">
            <div><label>username:<input type="text" name="username" /></label></div>
            <div><label>password:<input type="password" name="password" /></label></div>
            <div><input type="submit" value="Login" /></div>
        </form>
    </body>
</html>

src/main/resources/templates/login.html Created in.

$ {param} is an unfamiliar object called an "implicit object". To explain it roughly, it gets the parameters after the. (Dot). (Search for "$ {param} EL expression") So you can get the parameter of error with $ {param.error}. In this case, if error is true, an error message is displayed, and if logout is true, a message to that effect is displayed.

Also, according to the Spring official, it seems that if login fails, it redirects to " / login? Error ".

If the user fails to authenticate, the page is redirected to "/login?error" and our page displays the appropriate error message.

By the way, it seems that it is necessary to add / at the end of the input tag when writing XML. Why an error occurs if you do not add a slash to the input tag in Spring Boot 2

Finally, show your username and logout link in hello.html

hello.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <meta charset="UTF-8">
        <title>Hello,SpringSecurity!</title>
    </head>
    <body>
        <h2>Hello!</h2>
        <h1 th:inline="text">Hello[[${#httpServletRequest.remoteUser}]]!</h1>
        <form th:action="@{/logout}" method="post">
            <input type="submit" value="Log out" />
        </form>
        <p></p>
    </body>
</html>

The display part of the body has been rewritten.

  1. Display user name with [[$ {# httpServletRequest.remoteUser}]]
  2. Create a logout button

Get the user name in the part of [[$ {# httpServletRequest.remoteUser}]] and display it on the screen.

We display the username by using Spring Security’s integration with HttpServletRequest#getRemoteUser(). (From the official guide)

Here, we will get the username using the getRemoteUser () method of HttpServletRequest! Is written. Using this idea, Get remoteUser data with httpServletRequest It is an image like that. The double brackets [[]] seem to be for data conversion as far as I can see. I think it's because I take two steps: get it → format it for output. Perhaps.

Create a class for executing the application

I'm finally here! All you have to do now is set up your project to run.

Officially, a new execution class Application.java is created in the hello package, but while thinking that it is already in the com.example.demo package by default ... When I read it, it said something like "What should I do if it's in the hello package?", So I created it just in case. (It may not make sense because I haven't translated it properly)

@ComponentScan tells Spring to look for other components, configurations, and services in the hello package, allowing it to find the controllers.

src/main/java/hello/Application.java I will make it.

Application.java


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <meta charset="UTF-8">
        <title>Hello,SpringSecurity!</title>
    </head>
    <body>
        <h2>Hello!</h2>
        <h1 th:inline="text">Hello[[${#httpServletRequest.remoteUser}]]!</h1>
        <form th:action="@{/logout}" method="post">
            <input type="submit" value="Log out" />
        </form>
        <p></p>
    </body>
</html>

I think it's okay to delete the default execution class. I'm scared so I commented out the full text w

Run!

Finally completed! Let's run it now.

You can start it by right-clicking the project → Run → Spring Boot application. After that, try accessing by typing http: // localhost: 8080 in the URL input field of your browser.

home.html SS-home画面.PNG

First, the home screen was output. You can see the link to the login page. I haven't used it yet, but new user registration is also displayed.

Press Log Out.

login.html SS-login画面.PNG

You have transitioned to the login screen. Since the user information is "Logged out", it is displayed as "Logged out".

Next, let's log in by entering the user name (user) and password (pass) that you specified.

hello.html SS-hello画面.PNG

I was able to log in! Since the user name is "user", it is a little difficult to understand, but it is in the form of "Hello + user +!".

Now, let's enter the wrong user name or password.

login.html SS-loginerror画面.PNG

The error is displayed properly.

Hmm, it was so long. If this can be done after understanding the mechanism, the application will be effective.

Thank you for your hard work!

Recommended Posts

Create login / logout function with Spring Security according to Spring official guide [For beginners]
Login function with Spring Security
Try to implement login function with Spring Boot
[Introduction to Spring Boot] Authentication function with Spring Security
[Spring Boot] How to create a project (for beginners)
Tutorial to create a blog with Rails for beginners Part 1
Tutorial to create a blog with Rails for beginners Part 2
Tutorial to create a blog with Rails for beginners Part 0
A new employee tried to create an authentication / authorization function from scratch with Spring Security
With Spring boot, password is hashed and member registration & Spring security is used to implement login function.
Implemented authentication function with Spring Security ③
Implemented authentication function with Spring Security ①
Try to implement login function with Spring-Boot
Login function implementation by Spring Security (securityConfig)
[For beginners] How to implement the delete function
Create Spring Cloud Config Server with security with Spring Boot 2.0
I tried to create a shopping site administrator function / screen with Java and Spring
Settings for connecting to MySQL with Spring Boot + Spring JDBC
How to implement login request processing (Rails / for beginners)
Create a simple demo site with Spring Security with Spring Boot 2.1
Beginners create Spring Tools Suite environment with VS Code
Try to work with Keycloak using Spring Security SAML (Spring 5)
Create API key authentication for Web API in Spring Security
Login with HttpServletRequest # login in Spring Security of Servlet 3.x environment
[Rails] How to display error messages for comment function (for beginners)