| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 | [[servlet-authentication-form]]= Form Login:figures: servlet/authentication/unpwdSpring 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, authenticationsuccesshandlerThis section examines how form-based login works within Spring Security.First, we see how the user is redirected to the login form:.Redirecting to the Login Pageimage::{figures}/loginurlauthenticationentrypoint.png[]The preceding 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 login page with the configured xref:servlet/authentication/architecture.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 requests the login page to which it was redirected.image:{icondir}/number_5.png[] Something within the application, must <<servlet-authentication-form-custom,render the login 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.adoc#servlet-authentication-abstractprocessingfilter[AbstractAuthenticationProcessingFilter], so the following diagram should look pretty similar:.Authenticating Username and Passwordimage::{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.adoc#servlet-authentication-authentication[`Authentication`], by extracting the username and password from the `HttpServletRequest` instance.image:{icondir}/number_2.png[]  Next, the `UsernamePasswordAuthenticationToken` is passed into the `AuthenticationManager` instance to be authenticated.The details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/passwords/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.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out.. `RememberMeServices.loginFail` is invoked.If remember me is not configured, this is a no-op.See the {security-api-url}org/springframework/security/web/authentication/RememberMeServices.html[`RememberMeServices`] interface in the Javadoc.. `AuthenticationFailureHandler` is invoked.See the {security-api-url}springframework/security/web/authentication/AuthenticationFailureHandler.html[`AuthenticationFailureHandler`] class in the Javadocimage:{icondir}/number_4.png[] If authentication is successful, then __Success__.. `SessionAuthenticationStrategy` is notified of a new login.See the {security-api-url}springframework/security/web/authentication/session/SessionAuthenticationStrategy.html[`SessionAuthenticationStrategy`] interface in the Javadoc.. The ref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].See the {security-api-url}springframework/security/web/context/SecurityContextPersistenceFilter.html[`SecurityContextPersistenceFilter`] class in the Javadoc.. `RememberMeServices.loginSuccess` is invoked.If remember me is not configured, this is a no-op.See the {security-api-url}springframework/security/web/authentication/RememberMeServices.html[`RememberMeServices`] interface in the Javadoc.. `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`.. The `AuthenticationSuccessHandler` is invoked. Typically, this is a `SimpleUrlAuthenticationSuccessHandler`, which redirects to a request saved by xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] when we redirect to the login page.[[servlet-authentication-form-min]]By default, Spring Security form login is enabled.However, as soon as any servlet-based configuration is provided, form based login must be explicitly provided.The following example shows a minimal, explicit Java configuration:.Form Login====.Java[source,java,role="primary"]----public SecurityFilterChain filterChain(HttpSecurity http) {	http		.formLogin(withDefaults());	// ...}----.XML[source,xml,role="secondary"]----<http>	<!-- ... -->	<form-login /></http>----.Kotlin[source,kotlin,role="secondary"]----open fun filterChain(http: HttpSecurity): SecurityFilterChain {	http {		formLogin { }	}	// ...}----====In the preceding configuration, Spring Security renders a default login page.Most production applications require a custom login form.[[servlet-authentication-form-custom]]The following configuration demonstrates how to provide a custom login form..Custom Login Form Configuration====.Java[source,java,role="primary"]----public SecurityFilterChain filterChain(HttpSecurity http) {	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"]----open fun filterChain(http: HttpSecurity): SecurityFilterChain {	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 SecurityThe following https://www.thymeleaf.org/[Thymeleaf] template produces an HTML login form that complies with a login page of `/login`.:.Login 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 needs 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 named `error` is found, it indicates the user failed to provide a valid username or password.* If the HTTP parameter named `logout` is found, it indicates the user has logged out successfully.Many users do not need much more than to customize the login page.However, if needed, you can customize everything shown earlier with additional configuration.[[servlet-authentication-form-custom-controller]]If you use Spring MVC, you need a controller that maps `GET /login` to the login template we created.The following example shows a minimal `LoginController`:.LoginController====.Java[source,java,role="primary"]----@Controllerclass LoginController {	@GetMapping("/login")	String login() {		return "login";	}}----.Kotlin[source,kotlin,role="secondary"]----@Controllerclass LoginController {    @GetMapping("/login")    fun login(): String {        return "login"    }}----====
 |