Form Login

Spring Security 为通过 HTML 表单提供用户名和密码的方式提供支持。此部分详细介绍基于表单的验证在 Spring Security 中如何运作。

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.

此部分探讨在 Spring Security 中表单驱动的登录如何运作。首先,我们了解用户如何重定向到登录表单:

This section examines how form-based login works within Spring Security. First, we see how the user is redirected to the login form:

loginurlauthenticationentrypoint
Figure 1. Redirecting to the Login Page

前图基于我们的 SecurityFilterChain 图表构建。

The preceding figure builds off our SecurityFilterChain diagram.

number 1首先,用户向未获得授权的资源 (/private) 发出未经身份验证的请求。

number 1 First, a user makes an unauthenticated request to the resource (/private) for which it is not authorized.

number 2 Spring Security 的 AuthorizationFilter 指出未经身份验证的请求 Denied 抛出了 AccessDeniedException

number 2 Spring Security’s AuthorizationFilter indicates that the unauthenticated request is Denied by throwing an AccessDeniedException.

number 3 由于用户未经身份验证,所以 ExceptionTranslationFilter 启动 Start Authentication 并在配置的 AuthenticationEntryPoint 中向登录页面发送重定向。在大多数情况下,AuthenticationEntryPoint 是 {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[LoginUrlAuthenticationEntryPoint] 的实例。

number 3 Since the user is not authenticated, ExceptionTranslationFilter initiates Start Authentication and sends a redirect to the login page with the configured AuthenticationEntryPoint. In most cases, the AuthenticationEntryPoint is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[LoginUrlAuthenticationEntryPoint].

number 4浏览器请求其被重定向到的登录页面。

number 4 The browser requests the login page to which it was redirected.

number 5应用程序中的某个内容必须render the login page

number 5 Something within the application, must servlet-authentication-form-custom.

当提交用户名和密码时,UsernamePasswordAuthenticationFilter 会验证用户名和密码。UsernamePasswordAuthenticationFilter 扩展 AbstractAuthenticationProcessingFilter,因此下图看起来应该非常相似:

When the username and password are submitted, the UsernamePasswordAuthenticationFilter authenticates the username and password. The UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter, so the following diagram should look pretty similar:

usernamepasswordauthenticationfilter
Figure 2. Authenticating Username and Password

此图建立在我们的 SecurityFilterChain 图基础上。

The figure builds off our SecurityFilterChain diagram.

number 1 当用户提交其用户名和密码时,UsernamePasswordAuthenticationFilter 会通过从 HttpServletRequest 实例中提取用户名和密码,创建一个 UsernamePasswordAuthenticationToken,它是一种 Authentication

number 1 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 instance.

number 2 接下来,将 UsernamePasswordAuthenticationToken 传递到 AuthenticationManager 实例中以进行身份验证。AuthenticationManager 的外观详细信息取决于 user information is stored 的方式。

number 2 Next, the UsernamePasswordAuthenticationToken is passed into the AuthenticationManager instance to be authenticated. The details of what AuthenticationManager looks like depend on how the user information is stored.

number 3如果身份验证失败,则_Failure_。

number 3 If authentication fails, then Failure.

  1. The SecurityContextHolder is cleared out.

  2. 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.

  3. AuthenticationFailureHandler is invoked. See the {security-api-url}org/springframework/security/web/authentication/AuthenticationFailureHandler.html[AuthenticationFailureHandler] class in the Javadoc

number 4 如果身份验证成功,则 Success

number 4 If authentication is successful, then Success.

  1. SessionAuthenticationStrategy is notified of a new login. See the {security-api-url}org/springframework/security/web/authentication/session/SessionAuthenticationStrategy.html[SessionAuthenticationStrategy] interface in the Javadoc.

  2. The Authentication is set on the SecurityContextHolder. See the {security-api-url}org/springframework/security/web/context/SecurityContextPersistenceFilter.html[SecurityContextPersistenceFilter] class in the Javadoc.

  3. RememberMeServices.loginSuccess 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.

  4. ApplicationEventPublisher publishes an InteractiveAuthenticationSuccessEvent.

  5. The AuthenticationSuccessHandler is invoked. Typically, this is a SimpleUrlAuthenticationSuccessHandler, which redirects to a request saved by ExceptionTranslationFilter when we redirect to the login page.

默认情况下会启用 Spring Security 表单登录。但是,只要提供了基于 servlet 的任何配置,就必须明确提供基于表单的登录。以下示例显示了一个最小的明确 Java 配置:

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

  • XML

  • Kotlin

public SecurityFilterChain filterChain(HttpSecurity http) {
	http
		.formLogin(withDefaults());
	// ...
}
<http>
	<!-- ... -->
	<form-login />
</http>
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
	http {
		formLogin { }
	}
	// ...
}

在上述配置中,Spring Security 呈现了一个默认登录页面。大多数生产应用程序都需要一个自定义登录表单。

In the preceding configuration, Spring Security renders a default login page. Most production applications require a custom login form.

以下配置演示如何提供自定义登录表单。

The following configuration demonstrates how to provide a custom login form.

Custom Login Form Configuration
  • 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()
		}
	}
	// ...
}

当登录页面在 Spring Security 配置中指定时,您将负责渲染页面。下面的 Thymeleaf模板生成一个 HTML 登录表单,该表单符合`/login`的登录页面。

When the login page is specified in the Spring Security configuration, you are responsible for rendering the page. The following Thymeleaf template produces an HTML login form that complies with a login page of /login.:

Login Form - src/main/resources/templates/login.html
<!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>

有关默认 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 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 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.

如果你使用 Spring MVC,则需要一个控制器将 GET /login 映射到我们创建的登录模板。以下示例显示了一个最小的`LoginController`:

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

  • Kotlin

@Controller
class LoginController {
	@GetMapping("/login")
	String login() {
		return "login";
	}
}
@Controller
class LoginController {
    @GetMapping("/login")
    fun login(): String {
        return "login"
    }
}