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.
- Form login (custom fill the username and password)
- Basic authentication.
- Digest
When we provide the credentials, spring has multiple ways to read the details for the authentication, which includes.
- In Memory Storage (Not useful in the real-world applications)
- JDBC Authentication.
- Custom User Details Service (We will use this approach)
- 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:
Let’s take a quick look at the workflow:
- User trying to access secured resource (We allow access to log in user).
- The Spring security filter chain will throw exception, showing that it does not allow the access to the unauthenticated user.
- The
ExceptionTranslationFilter
starts Start<em>Authentication</em>
and send the customer to the login page. - Browser request for the login page.
- 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.
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:
- We are configuring the login page URL as
/login
, this tells spring security to call this URL before displaying the login page. - We need to create a controller mapped to the “
/login
” get request. Our controller is now responsible to return the login page HTML. - On the successful login, we are redirecting the user to the
/home
URL. This will display the home page to the customer. - 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.
- Our form will perform a post request to
/login
. - The
/login
handled automatically by Spring security. We need not create any controller method for the post request. - From field parameters should be
username
andpassword
(make sure they have exactly same name as the/login
mapping expecting these parameters). - 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.
- We want to allow certain pages to be accessible without forcing the user to login.
- Certain part of the application should be secure and we will force the customer to login.
- 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:
- Login and register page to be accessible without any login. Permitting it to all users using the
antMatchers
pattern. - 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. - WebSecurityConfigurerAdapter provides a set of methods to enable specific web security configuration.
@EnableWebSecurity
enable spring security supports with support for the Spring MVC integration.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.
- Try to find the customer in the database based on our data model (Remember Spring security needs to load customer information before authentication).
- Provide information about the user authorities.
- 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.
- Directly access secure URL (e.g. any URL with pattern
/account/**
), Spring security will force the login by redirecting us to /login page. - Acess the non-secure URL. We should be able to access those with no issue.
- 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.
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")
).
If you provide an invalid credential, we will get the login screen with the 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 configurationantMatchers("/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
Let’s talk about the above workflow:
- Customer fills out the credentials on the login page.
- On form submission, the
UsernamePasswordAuthenticationFilter
creates aUsernamePasswordAuthenticationToken
by extracting the username and password from the request parameters. - The
AuthenticationManager
is responsible to validate the user based on the supplied credentials (Look in to theUserUserDetailService
to understand how it works). - If authenticated, Spring security performs several additional operations.
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.- Spring security store the user authentication details in the
SecurityContextHolder
. It will update the SecurityContextHolder with authentication details. - If
RememberMeServices
service is active, it will activate theloginSuccess
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). - It will publish an
InteractiveAuthenticationSuccessEven
. - 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.)
- For the fail attempt, Spring security will also perform a few important steps to make sure it clears out all sensitive and secure information.
- I will clear the
SecurityContextHolder
out. - Call the
loginFail
method of theRememberMeServices
service to remove cookies and other related information. - The
AuthenticationFailureHandler
triggers to perform any additional clean-up action.
- I will clear the
Here is another overview of the form based login process.
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.
- We may like to set user default currency and other details in session.
- Like to restore the customer shopping cart.
- 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.
SavedRequestAwareAuthenticationSuccessHandler
.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:
- Create success handler by implementing the
AuthenticationSuccessHandler
interface. - 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.
- How Spring security login process works?
- Configure and use custom login page in spring security.
- How to configure the success and failure process for our application?
- In the last section, we covered the spring security login workflow.
The source code for this article is available on the GitHub.
Thanks for sharing. It helped me a lot.