This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Security 6.1.12! |
Form Login
Spring Security provides support for username and password being provided through an html form. This section provides details on how form based authentication works within Spring Security.
Let’s take a look at how form based log in works within Spring Security. First, we see how the user is redirected to the log in form.
The figure builds off our SecurityFilterChain
diagram.
First, a user makes an unauthenticated request to the resource /private
for which it is not authorized.
Spring Security’s FilterSecurityInterceptor
indicates that the unauthenticated request is Denied by throwing an AccessDeniedException
.
Since the user is not authenticated, ExceptionTranslationFilter
initiates Start Authentication and sends a redirect to the log in page with the configured AuthenticationEntryPoint
.
In most cases the AuthenticationEntryPoint
is an instance of LoginUrlAuthenticationEntryPoint
.
The browser will then request the log in page that it was redirected to.
Something within the application, must render the log in page.
When the username and password are submitted, the UsernamePasswordAuthenticationFilter
authenticates the username and password.
The UsernamePasswordAuthenticationFilter
extends AbstractAuthenticationProcessingFilter, so this diagram should look pretty similar.
The figure builds off our SecurityFilterChain
diagram.
When the user submits their username and password, the UsernamePasswordAuthenticationFilter
creates a UsernamePasswordAuthenticationToken
which is a type of Authentication
by extracting the username and password from the HttpServletRequest
.
Next, the UsernamePasswordAuthenticationToken
is passed into the AuthenticationManager
to be authenticated.
The details of what AuthenticationManager
looks like depend on how the user information is stored.
If authentication fails, then Failure
-
The SecurityContextHolder is cleared out.
-
RememberMeServices.loginFail
is invoked. If remember me is not configured, this is a no-op. -
AuthenticationFailureHandler
is invoked.
If authentication is successful, then Success.
-
SessionAuthenticationStrategy
is notified of a new log in. -
The Authentication is set on the SecurityContextHolder.
-
RememberMeServices.loginSuccess
is invoked. If remember me is not configured, this is a no-op. -
ApplicationEventPublisher
publishes anInteractiveAuthenticationSuccessEvent
. -
The
AuthenticationSuccessHandler
is invoked. Typically this is aSimpleUrlAuthenticationSuccessHandler
which will redirect to a request saved byExceptionTranslationFilter
when we redirect to the log in page.
Spring Security form log in is enabled by default. However, as soon as any servlet based configuration is provided, form based log in must be explicitly provided. A minimal, explicit Java configuration can be found below:
-
Java
-
XML
-
Kotlin
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.formLogin(withDefaults());
// ...
}
<http>
<!-- ... -->
<form-login />
</http>
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
formLogin { }
}
// ...
}
In this configuration Spring Security will render a default log in page. Most production applications will require a custom log in form.
The configuration below demonstrates how to provide a custom log in form.
-
Java
-
XML
-
Kotlin
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.formLogin(form -> form
.loginPage("/login")
.permitAll()
);
// ...
}
<http>
<!-- ... -->
<intercept-url pattern="/login" access="permitAll" />
<form-login login-page="/login" />
</http>
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
formLogin {
loginPage = "/login"
permitAll()
}
}
// ...
}
When the login page is specified in the Spring Security configuration, you are responsible for rendering the page.
Below is a Thymeleaf template that produces an HTML login form that complies with a login page of /login
:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<title>Please Log In</title>
</head>
<body>
<h1>Please Log In</h1>
<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>
<input type="text" name="username" placeholder="Username"/>
</div>
<div>
<input type="password" name="password" placeholder="Password"/>
</div>
<input type="submit" value="Log in" />
</form>
</body>
</html>
There are a few key points about the default HTML form:
-
The form should perform a
post
to/login
-
The form will need to include a CSRF Token which is automatically included by Thymeleaf.
-
The form should specify the username in a parameter named
username
-
The form should specify the password in a parameter named
password
-
If the HTTP parameter error is found, it indicates the user failed to provide a valid username / password
-
If the HTTP parameter logout is found, it indicates the user has logged out successfully
Many users will not need much more than to customize the log in page. However, if needed, everything above can be customized with additional configuration.
If you are using Spring MVC, you will need a controller that maps GET /login
to the login template we created.
A minimal sample LoginController
can be seen below:
-
Java
-
Kotlin
@Controller
class LoginController {
@GetMapping("/login")
String login() {
return "login";
}
}
@Controller
class LoginController {
@GetMapping("/login")
fun login(): String {
return "login"
}
}