__CONFIG_colors_palette__{"active_palette":0,"config":{"colors":{"40f3f":{"name":"Main Accent","parent":-1}},"gradients":[]},"palettes":[{"name":"Default","value":{"colors":{"40f3f":{"val":"var(--tcb-color-0)"}},"gradients":[]},"original":{"colors":{"40f3f":{"val":"rgb(127, 215, 132)","hsl":{"h":123,"s":0.52,"l":0.67,"a":1}}},"gradients":[]}}]}__CONFIG_colors_palette__

__CONFIG_colors_palette__{"active_palette":0,"config":{"colors":{"40f3f":{"name":"Main Accent","parent":-1}},"gradients":[]},"palettes":[{"name":"Default","value":{"colors":{"40f3f":{"val":"var(--tcb-color-0)"}},"gradients":[]},"original":{"colors":{"40f3f":{"val":"rgb(127, 215, 132)","hsl":{"h":123,"s":0.52,"l":0.67,"a":1}}},"gradients":[]}}]}__CONFIG_colors_palette__
Table of Contents

In this post, we will look at the spring security login example. Login is part of the Spring Security authentication process. We already covered the Spring Security Authentication Providers which is core to the spring security login process.

 

Introduction

For most of the web application, the common mode of authentication works where user provides the username and password and system validate the credentials to make sure they are valid. Spring security supports the following mode for the login process.

  1. Form login (custom fill the username and password)
  2. Basic authentication.
  3. Digest

When we provide the credentials, spring has multiple ways to read the details for the authentication, which includes.

  1. In Memory Storage (Not useful in the real-world applications)
  2. JDBC Authentication.
  3. Custom User Details Service (We will use this approach)
  4. LDAP (Mostly for Intranet applications)

Before we move with the code, it’s very important that we have the clarity of the login workflow.

 

1. Spring Security Login

Let’s look at the login workflow:

spring security login

Let’s take a quick look at the workflow:

  1. User trying to access secured resource (We allow access to log in user).
  2. The Spring security filter chain will throw exception, showing that it does not allow the access to the unauthenticated user.
  3. The ExceptionTranslationFilter starts Start Authentication and send the customer to the login page.
  4. Browser request for the login page.
  5. It shows the login screen to the customer.

Now we understand the workflow, let’s build our application to allow customer login.

 

2. Default Security Login Page

By default, Spring security will show the default login page. If you don’t do any customization or configuration, you might see a similar login page on your application.

custom success handler

Most application will like to have their own customize login page. Spring security provides a flexible option to configure custom login page for our application. The next section will cover the customization and configuration of the login page.

 

3. Security Security Custom Login Page

To enable the custom login page, override the configure(HttpSecurity http) method by extending the WebSecurityConfigurerAdapter class. Let’s look at the configuration:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .formLogin(form - > form
            .loginPage("/login")
            .defaultSuccessUrl("/home")
            .failureUrl("/login?error=true")
        );
}

Setting up few things in above code:

  1. We are configuring the login page URL as /login, this tells spring security to call this URL before displaying the login page.
  2. We need to create a controller mapped to the “/login” get request. Our controller is now responsible to return the login page HTML.
  3. On the successful login, we are redirecting the user to the /home URL. This will display the home page to the customer.
  4. For failed login attempt, we keep user on the same URL but add a param as “error=true“.

In the later part of this series, we will also configure the success and failure handlers. Handlers are a great way to post processing.

 

3.1. Login Controller

With the above configuration, we also need the login controller to maps GET /login to our controller. Our controller will return the custom login page. Here is the login page controller for your reference:

package com.javadevjournal.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginPageController {

    @GetMapping("/login")
    public String login(){
        return "account/login";
    }
}

There is nothing special about the spring security login controller.

 

3.2. Login Page

We need to create a login page as per our design. Our controller will return this page. I am using Thymeleaf to build the page, but you can use any other templating engine of your choice.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">

<head th:replace="core/header :: head">
    <meta charset="utf-8">
</head>
<body class="hold-transition login-page">
    <div class="login-box">
        <div class="login-logo">
            <div class="card">
                <div class="card-body login-card-body">
                    <p class="login-box-msg">Sign in to start your session</p>
                    <p th:if="${loginError}" class="error">Wrong user or password</p>
                    <form th:action="@{/login}" method="post">
                        <div th:if="${param.error}">
                            <div class="alert alert-danger">
                                Invalid username or password.
                            </div>
                        </div>
                        <div class="input-group mb-3">
                            <input type="email" class="form-control" name="username" placeholder="Email">
                            <div class="input-group-append">
                                <div class="input-group-text">
                                    <span class="fas fa-envelope"></span>
                                </div>
                            </div>
                        </div>
                        <div class="input-group mb-3">
                            <input type="password" name="password" class="form-control" placeholder="Password">
                            <div class="input-group-append">
                                <div class="input-group-text">
                                    <span class="fas fa-lock"></span>
                                </div>
                            </div>
                        </div>

                        <div class="row">
                            <div class="col-4">
                                <button type="submit" class="btn btn-primary btn-block">Sign In</button>
                            </div>
                            <!-- /.col -->
                        </div>
                    </form>
</body>
</html>

It’s a simple HTML page, but let’s highlight a few important points in the HTML.

  1. Our form will perform a post request to /login.
  2. The /login handled automatically by Spring security. We need not create any controller method for the post request.
  3. From field parameters should be username andpassword(make sure they have exactly same name as the /login mapping expecting these parameters).
  4. For the failed login attempt, it sends back an HTTP parameter. We are using the parameter to display the error message to the customer (check param.error in the HTML).

Spring security will automatically include CSRF token as a hidden field. This is a security feature. We will discuss it in the later section of this course.

 

4. Spring Security Login Configurations

We have completed the custom login page for the spring security, basic configurations are also in place. For the Spring Boot application, spring security will be active by adding the spring security stater in the classpath. It will start prompting for username and password.

To enable the Spring Security’s web security support and provide the Spring MVC integration, we will add the @EnableWebSecurity to our security configuration class.We also need some additional steps for the security configuration.

  1. We want to allow certain pages to be accessible without forcing the user to login.
  2. Certain part of the application should be secure and we will force the customer to login.
  3. We like the CSS and other static content outside of the security (until you want to secure them)

Here is our security configuration class:

@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/login", "/register")
            .permitAll()
            .antMatchers("/account/**").access("hasRole('ROLE_ADMIN')")
            .and()
            .formLogin(form - > form
                .loginPage("/login")
                .defaultSuccessUrl("/home")
                .failureUrl("/login?error=true")
            );
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
            .antMatchers("/resources/**", "/static/**");
    }
}

Here are some important points to consider:

  1. Login and register page to be accessible without any login. Permitting it to all users using the antMatchers pattern.
  2. We are allowing only logged is customer to access URLs matching with pattern /account/**. Looking for a certain role before allowing the user to access the URL.
  3. WebSecurityConfigurerAdapter provides a set of methods to enable specific web security configuration.
  4. @EnableWebSecurity enable spring security supports with support for the Spring MVC integration.
  5. configure(HttpSecurity http) method is used to configure distinct security points for our application (e.g. secure and non-secure urls, success handlers etc.).

 

4.1. Landing Page for Successful Authentication

After the successful authentication, we want to redirect customer to the home screen. Spring security provide flexible way to do this.

http.authorizeRequests()
    ....
    .formLogin(form - > form
        .defaultSuccessUrl("/home")
    );

 

4.2. Landing Page Failure

If we like, we can redirect the user to different URL in case of authentication failure. This can be easily done using the security configuration.

http.authorizeRequests()
    ....
    .formLogin(form - > form
        .failureUrl("/login?error=true")
    );

 

5. Configuring the Security Authentication Provider

The last part of the application is to configure the authentication provider. We will inject the custom UserDetailService in the authentication provider.

@Bean
public DaoAuthenticationProvider authProvider() {
    DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
    authProvider.setUserDetailsService(userDetailsService);
    authProvider.setPasswordEncoder(passwordEncoder);
    return authProvider;
}

@Override
protected void configure(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(authProvider());
}

For more details, read spring security authentication provider (I assume that you are following the complete series). Before we start the login process, make sure you have followed the spring security account registration process.

 

6. Spring Security Custom UserDetailService

If you look closely, we are injecting custom UserDetailService in the DAOAuthenticationProvider. This UserDetailsService class will help us with following tasks.

  1. Try to find the customer in the database based on our data model (Remember Spring security needs to load customer information before authentication).
  2. Provide information about the user authorities.
  3. Authorities helps spring security to decide if a customer can access a resource or not.
@Service
public class CustomUserDetailService implements UserDetailsService {

    @Autowired
    UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        final UserEntity customer = userRepository.findByEmail(email);
        if (customer == null) {
            throw new UsernameNotFoundException(email);
        }
        UserDetails user = User.withUsername(customer.getEmail())
            .password(customer.getPassword())
            .authorities("USER").build();
        return user;
    }
}

We are setting the valid user authority as “USER“. This will help the security allow access to URL’s with pattern /account/**.

We will learn more about the roles, authorities and how to use them during this course.

 

7. Test Login Process

It’s time to test the login process. Build and run the application. Once the application start, we will run the following test cases.

  1. Directly access secure URL (e.g. any URL with pattern /account/**), Spring security will force the login by redirecting us to /login page.
  2. Acess the non-secure URL. We should be able to access those with no issue.
  3. try login with a valid and invalid credentials.

Let’s look at each test case:

7.1. Login With Valid Credential

Once application up, open the http://localhost:8080/login URL in your browser. We will have the custom login page from spring security.

spring security login page

Provide the valid credentials (which you used while registration), click on the “Sign In” button. Spring security will validate the credentials and will redirect us based on our configuration (.defaultSuccessUrl("/home")). 

Spring Security Tutorial Welcome Page

If you provide an invalid credential, we will get the login screen with the error message.

Spring security login error message

Also, try to access the following http://localhost:8080/account/starter, security configuration will redirect you to the login page. Remember, we have the following in our security configuration antMatchers("/account/**").access("hasRole('ROLE_USER')")

 

8. Spring Security Login Workflow

We will enhance our login process in this series (Will add remember me service etc.) but I like to revisit the login workflow

spring security login workflow

Let’s talk about the above workflow:

  1. Customer fills out the credentials on the login page.
  2. On form submission, the UsernamePasswordAuthenticationFilter creates a UsernamePasswordAuthenticationToken by extracting the username and password from the request parameters.
  3. The AuthenticationManager is responsible to validate the user based on the supplied credentials (Look in to the UserUserDetailService to understand how it works).
  4. If authenticated, Spring security performs several additional operations.
    1. SessionAuthenticationStrategy is notified for new login. This handles the HTTP session and makes sure a valid session exists and handles any against session-fixation attacks.
    2. Spring security store the user authentication details in the SecurityContextHolder. It will update the SecurityContextHolder with authentication details.
    3. If RememberMeServices service is active, it will activate the loginSuccess method. This service is useful if you want to remember user for sometime (remember, on many sites, we have the option “Keep me Logged in”. We can create a similar feature using this service).
    4. It will publish an InteractiveAuthenticationSuccessEven.
    5. The AuthenticationSuccessHandler is invoked. This success handler will try to redirect the user to the location when we redirect to the login page (e.g. If you were moving to my account and got the login page, on successful login, it will redirect you to the account page.)
  5. For the fail attempt, Spring security will also perform a few important steps to make sure it clears out all sensitive and secure information.
    1. I will clear the SecurityContextHolder out.
    2. Call the loginFail method of the RememberMeServices service to remove cookies and other related information.
    3. The AuthenticationFailureHandler triggers to perform any additional clean-up action.

 

9. Spring Security Success Handler

The Spring Security Success Handlers are a powerful mechanism and strategy to handle a successful user authentication. With .defaultSuccessUrl("/home"), we can redirect the user to a pre-defined location, however, for enterprise application, we may like to execute certain operations before redirecting user. Let’s think about an eCommerce application, we may like to do following operations after authentication and before user landing on a certain page.

  1. We may like to set user default currency and other details in session.
  2. Like to restore the customer shopping cart.
  3. Like to redirect the user to certain workflow based on the profile.

The security success handlers are a great way to handle all these business workflows. Spring security provides few success handlers used automatically during the login process.

  1. SavedRequestAwareAuthenticationSuccessHandler.
  2. SimpleUrlAuthenticationSuccessHandler

On successful authentication, Spring security automatically invoke AuthenticationSuccessHandler and it will make sure that customer is redirected to the requested page when we redirect the customer to the login page. To create custom security handler, we have the following 2 options:

  1. Create success handler by implementing the AuthenticationSuccessHandler interface.
  2. Extend the sucess handler available with Spring security like SimpleUrlAuthenitcaionSuccessHandler.

 

Summary

In this article, we discuss the spring security login, we discuss the distinct feature of the login process. To summarize we discuss following points in this post.

  1. How Spring security login process works?
  2. Configure and use custom login page in spring security.
  3. How to configure the success and failure process for our application?
  4. In the last section, we covered the spring security login workflow.

The source code for this article is available on the GitHub.

0 0 vote
Article Rating

Related Posts

Manish Sharma


Manish's primary interests are Java, Spring Boot and Spring. His focus is more toward the automations and testing.Manish love travelling and when not working, he might be exploring some new destination.

Subscribe
Notify of

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
0
Would love your thoughts, please comment.x
()
x