mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-29 16:22:12 +00:00
225 lines
8.4 KiB
Plaintext
225 lines
8.4 KiB
Plaintext
[[servlet-authentication-form]]
|
|
= Form Login
|
|
:figures: servlet/authentication/unpwd
|
|
|
|
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.
|
|
// FIXME: describe authenticationentrypoint, authenticationfailurehandler, authenticationsuccesshandler
|
|
|
|
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.
|
|
|
|
.Redirecting to the Log In Page
|
|
image::{figures}/loginurlauthenticationentrypoint.png[]
|
|
|
|
The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram.
|
|
|
|
image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized.
|
|
|
|
image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`.
|
|
|
|
image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__ and sends a redirect to the log in page with the configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`].
|
|
In most cases the `AuthenticationEntryPoint` is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`].
|
|
|
|
image:{icondir}/number_4.png[] The browser will then request the log in page that it was redirected to.
|
|
|
|
image:{icondir}/number_5.png[] Something within the application, must <<servlet-authentication-form-custom,render the log in page>>.
|
|
|
|
[[servlet-authentication-usernamepasswordauthenticationfilter]]
|
|
When the username and password are submitted, the `UsernamePasswordAuthenticationFilter` authenticates the username and password.
|
|
The `UsernamePasswordAuthenticationFilter` extends xref:servlet/authentication/architecture/index.adoc#servlet-authentication-abstractprocessingfilter[AbstractAuthenticationProcessingFilter], so this diagram should look pretty similar.
|
|
|
|
.Authenticating Username and Password
|
|
image::{figures}/usernamepasswordauthenticationfilter.png[]
|
|
|
|
The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram.
|
|
|
|
|
|
image:{icondir}/number_1.png[] When the user submits their username and password, the `UsernamePasswordAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] by extracting the username and password from the `HttpServletRequest`.
|
|
|
|
image:{icondir}/number_2.png[] Next, the `UsernamePasswordAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated.
|
|
The details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-storage[user information is stored].
|
|
|
|
image:{icondir}/number_3.png[] If authentication fails, then __Failure__
|
|
|
|
* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out.
|
|
* `RememberMeServices.loginFail` is invoked.
|
|
If remember me is not configured, this is a no-op.
|
|
// FIXME: link to rememberme
|
|
* `AuthenticationFailureHandler` is invoked.
|
|
// FIXME: link to AuthenticationFailureHandler
|
|
|
|
image:{icondir}/number_4.png[] If authentication is successful, then __Success__.
|
|
|
|
* `SessionAuthenticationStrategy` is notified of a new log in.
|
|
// FIXME: Add link to SessionAuthenticationStrategy
|
|
* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].
|
|
// FIXME: link securitycontextpersistencefilter
|
|
* `RememberMeServices.loginSuccess` is invoked.
|
|
If remember me is not configured, this is a no-op.
|
|
// FIXME: link to rememberme
|
|
* `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`.
|
|
* The `AuthenticationSuccessHandler` is invoked. Typically this is a `SimpleUrlAuthenticationSuccessHandler` which will redirect to a request saved by xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] when we redirect to the log in page.
|
|
|
|
[[servlet-authentication-form-min]]
|
|
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:
|
|
|
|
.Form Log In
|
|
====
|
|
.Java
|
|
[source,java,role="primary"]
|
|
----
|
|
protected void configure(HttpSecurity http) {
|
|
http
|
|
// ...
|
|
.formLogin(withDefaults());
|
|
}
|
|
----
|
|
|
|
.XML
|
|
[source,xml,role="secondary"]
|
|
----
|
|
<http>
|
|
<!-- ... -->
|
|
<form-login />
|
|
</http>
|
|
----
|
|
|
|
.Kotlin
|
|
[source,kotlin,role="secondary"]
|
|
----
|
|
fun configure(http: HttpSecurity) {
|
|
http {
|
|
// ...
|
|
formLogin { }
|
|
}
|
|
}
|
|
----
|
|
====
|
|
|
|
In this configuration Spring Security will render a default log in page.
|
|
Most production applications will require a custom log in form.
|
|
|
|
[[servlet-authentication-form-custom]]
|
|
The configuration below demonstrates how to provide a custom log in form.
|
|
|
|
.Custom Log In Form Configuration
|
|
====
|
|
.Java
|
|
[source,java,role="primary"]
|
|
----
|
|
protected void configure(HttpSecurity http) throws Exception {
|
|
http
|
|
// ...
|
|
.formLogin(form -> form
|
|
.loginPage("/login")
|
|
.permitAll()
|
|
);
|
|
}
|
|
----
|
|
|
|
.XML
|
|
[source,xml,role="secondary"]
|
|
----
|
|
<http>
|
|
<!-- ... -->
|
|
<intercept-url pattern="/login" access="permitAll" />
|
|
<form-login login-page="/login" />
|
|
</http>
|
|
----
|
|
|
|
.Kotlin
|
|
[source,kotlin,role="secondary"]
|
|
----
|
|
fun configure(http: HttpSecurity) {
|
|
http {
|
|
// ...
|
|
formLogin {
|
|
loginPage = "/login"
|
|
permitAll()
|
|
}
|
|
}
|
|
}
|
|
----
|
|
====
|
|
|
|
[[servlet-authentication-form-custom-html]]
|
|
When the login page is specified in the Spring Security configuration, you are responsible for rendering the page.
|
|
// FIXME: default login page rendered by Spring Security
|
|
Below is a https://www.thymeleaf.org/[Thymeleaf] template that produces an HTML login form that complies with a login page of `/login`:
|
|
|
|
.Log In Form
|
|
====
|
|
.src/main/resources/templates/login.html
|
|
[source,xml]
|
|
----
|
|
<!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 xref:servlet/exploits/csrf.adoc#servlet-csrf[CSRF Token] which is xref:servlet/exploits/csrf.adoc#servlet-csrf-include-form-auto[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.
|
|
|
|
[[servlet-authentication-form-custom-controller]]
|
|
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:
|
|
|
|
.LoginController
|
|
====
|
|
.Java
|
|
[source,java,role="primary"]
|
|
----
|
|
@Controller
|
|
class LoginController {
|
|
@GetMapping("/login")
|
|
String login() {
|
|
return "login";
|
|
}
|
|
}
|
|
----
|
|
|
|
.Kotlin
|
|
[source,kotlin,role="secondary"]
|
|
----
|
|
@Controller
|
|
class LoginController {
|
|
@GetMapping("/login")
|
|
fun login(): String {
|
|
return "login"
|
|
}
|
|
}
|
|
----
|
|
====
|