You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
8.4 KiB
224 lines
8.4 KiB
[[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" |
|
} |
|
} |
|
---- |
|
====
|
|
|