Spring Security - Form Login & Logout

Created: 2020-06-28 | 8 min


I am still in the process of learning more about Spring Security. This time I created a simple example that involves Form Login and Logout customizations.

#Scope

#Out of scope

#Pre-requisites

#Create project

Go to https://start.spring.io/ and add the following dependencies:

Generate, download and open the project in your IDE. I'll use Intellij IDEA.

#Create the components

In the project that we just downloaded, let's edit the SpringSecurity02Application.java file, which is our SpringBootApplication:

Then, under the resources folder of our application, in this case spring-security-02/src/main/resources/templates, let's create the four templates we want to render in our flow:

The project structure should look like:

The above is all what we will create (in terms of files and classes). Now let's move to the details.

This is how our code should look like for the whole example (repo available on Github):

#Review the components

From the previous picture, let's take a look at each component and what each piece means.

Block #1. The Form Login Setup

Block #2. The Logout Setup

Block #3. The controller

The controller has four request handlers, all of them provide server-rendered views that will serve to our example to have the login authentication flow. Most of these views will work with the template engine Thymeleaf to access data from the application's context.

spring-security-02/src/main/resources/templates/login.html

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
    <title>Login Page</title>
</head>
<body>
<h1>Login Page</h1>
<form th:action="@{/custom-login}" method="post">
    <div>
        Username: <input type="text" name="myUser"/>
    </div>
    <div>
        Password: <input type="password" name="myPass"/>
    </div>
    <div>
        <input type="submit" value="Log in" />
    </div>
</form>
</body>
</html>

In this template:

  1. Action /custom-login will send the content (POST) from the client (the browser) to the server (our application)
  2. Username and password <input>'s names should match with the usernameParameter(...) and passwordParameter(...) we specified to the formLogin() setup
spring-security-02/src/main/resources/templates/login-error.html

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<html>
<head>
    <title>Login Error Page</title>
</head>
<body>
<h1>Login Error Page</h1>
<p>Reason: <span th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}"></span></p>
<p>Go to <a href="/custom-login">Login Page</a></p>
</body>
</html>

In this template:

  1. Print the error message for any failed login attempt. The error message is coming from an exception thrown by Spring Security and added into the session attribute: SPRING_SECURITY_LAST_EXCEPTION
  2. A link that redirects to the login page

Note: Usually, we don't need a separate page just to inform that provided credentials are wrong. But I wanted to create this one for the sake of exploring this feature...

spring-security-02/src/main/resources/templates/protected-resource.html

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
    <title>Protected Resource</title>
</head>
<body>
<h1>Protected Resource</h1>
<p>Hey, I am a protected resource. Only authenticated users can see me.</p>
<form th:action="@{/custom-logout}" method="post">
    <div>
        <input type="submit" value="Logout" />
    </div>
</form>
</body>
</html>

In this template:

  1. Points to the action /custom-logout. It should match with what we set in logoutUrl(...) during the logout() setup.
  2. Notice the method for this form is POST. According to the documentation:

If CSRF protection is enabled (default), then the request must also be a POST. This means that by default POST "/logout" is required to trigger a log out. If CSRF protection is disabled, then any HTTP method is allowed.

In our example, we didn't disabled the CSRF protection, so we want to do POST. More about CSRF: https://en.wikipedia.org/wiki/Cross-site_request_forgery

<html>
<head>
    <title>Public Resource</title>
</head>
<body>
<h1>Public Resource</h1>
<p>Hey, I am a public resource. Anyone can see me.</p>
<p>Login to see protected resources. <a href="/custom-login">Login Page</a></p>
</body>
</html>
  1. This view can be accessed without being authenticated. Remember that we only require authentication for the protected-resource, which we defined in our SecurityConfiguration class:
.authorizeRequests()
        .antMatchers(HttpMethod.GET, "/protected-resource").authenticated();

#Run and test

  1. Run the application with the following instruction in terminal (need to cd to the project directory):
$ ./gradlew bootRun
  1. Grab the auto-generated password from logs and wait until Spring Boot is fully initialized:
Using generated security password: 0c6fc7f8-34b1-4c04-a0f6-dbaa8eb8c21f
  1. Open http://localhost:8080 in a browser

This is the public resource (no need to authenticate so far)

  1. Click on the "Login Page" link and the Login Form should be rendered. Enter user as the username (defaulted by Spring Security) and the auto-generated password. Then click on "Log in" button

  1. The protected resource should be displayed

  1. If during login form credentials are invalid, then login-error view should be displayed

  1. If user is not authenticated and tries to access protected-resource through the browser via http://localhost:8080/protected-resource , then the login page should be displayed. We didn't do anything but just telling Spring Security that resource needs authentication, then Spring Security is responsible of redirect the request to the Login Page

#Final Thoughts

From this example, I learned the following:

https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurer.html#loginPage-java.lang.String-

In the above link, I found a Example login.jsp, which gave me a clear idea of what I have to have in login.html. I think Spring Security team makes a great job documenting their code.

#Refs