跳到主要内容
版本:7.0.2

表单登录

DeepSeek V3 中英对照 Form Form Login

Spring Security 支持通过HTML表单提交用户名和密码进行身份验证。本节将详细介绍基于表单的身份验证在Spring Security中的工作原理。

本节将探讨基于表单的登录在Spring Security中的工作原理。首先,我们来看用户是如何被重定向到登录表单的:

loginurlauthenticationentrypoint

图 1. 重定向至登录页面

上图基于我们的SecurityFilterChain示意图构建。

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

2 Spring Security 的 AuthorizationFilter 通过抛出 AccessDeniedException 异常,表明未认证的请求被拒绝

3 由于用户未经认证,ExceptionTranslationFilter 会启动身份验证流程,并通过配置的 AuthenticationEntryPoint 发送重定向到登录页面。在大多数情况下,AuthenticationEntryPointLoginUrlAuthenticationEntryPoint 的一个实例。

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

5 应用程序中的某些内容必须渲染登录页面

当用户名和密码被提交时,UsernamePasswordAuthenticationFilter 会从 HttpServletRequest 实例中提取用户名和密码,创建一个 UsernamePasswordAuthenticationToken,这是一种 Authentication 类型。UsernamePasswordAuthenticationFilter 继承自 AbstractAuthenticationProcessingFilter,因此下面的图表看起来应该非常相似:

用户名密码认证过滤器

图 2. 用户名与密码认证

该图基于我们的SecurityFilterChain示意图构建。

1 当用户提交用户名和密码时,UsernamePasswordAuthenticationFilter 会从 HttpServletRequest 实例中提取用户名和密码,创建一个 UsernamePasswordAuthenticationToken,这是一种 Authentication 类型。

2 接下来,UsernamePasswordAuthenticationToken 会被传入 AuthenticationManager 实例进行认证。AuthenticationManager 的具体实现细节取决于用户信息的存储方式

3 如果身份验证失败,则返回 Failure

  1. SecurityContextHolder 被清空。

  2. 调用 RememberMeServices.loginFail。如果未配置记住我功能,此操作无效。请参阅 Javadoc 中的 RememberMeServices 接口。

  3. 调用 AuthenticationFailureHandler。请参阅 Javadoc 中的 AuthenticationFailureHandler 类。

4 如果认证成功,则显示 成功

  1. SessionAuthenticationStrategy 会收到新登录的通知。详见 Javadoc 中的 SessionAuthenticationStrategy 接口。

  2. Authentication 被设置到 SecurityContextHolder 中。详见 Javadoc 中的 SecurityContextPersistenceFilter 类。

  3. 调用 RememberMeServices.loginSuccess。如果未配置记住我功能,此操作无效。详见 Javadoc 中的 RememberMeServices 接口。

  4. ApplicationEventPublisher 发布一个 InteractiveAuthenticationSuccessEvent 事件。

  5. 调用 AuthenticationSuccessHandler。通常,这是一个 SimpleUrlAuthenticationSuccessHandler,它会重定向到由 ExceptionTranslationFilter 保存的请求(该请求是在我们重定向到登录页面时保存的)。

默认情况下,Spring Security 启用了表单登录功能。然而,一旦提供了任何基于 Servlet 的配置,就必须显式地提供基于表单的登录。以下示例展示了一个最小化的显式 Java 配置:

public SecurityFilterChain filterChain(HttpSecurity http) {
http
.formLogin(withDefaults());
// ...
}

在上述配置中,Spring Security 会渲染一个默认的登录页面。大多数生产环境的应用都需要自定义登录表单。

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

public SecurityFilterChain filterChain(HttpSecurity http) {
http
.formLogin((form) -> form
.loginPage("/login")
.permitAll()
);
// ...
}

当在Spring Security配置中指定登录页面时,您需要负责渲染该页面。以下Thymeleaf模板会生成一个符合/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表单,有几个关键点:

  • 表单应执行一个指向 /loginpost 请求。

  • 表单需要包含一个 CSRF 令牌,该令牌由 Thymeleaf 自动包含

  • 表单应在名为 username 的参数中指定用户名。

  • 表单应在名为 password 的参数中指定密码。

  • 如果找到名为 error 的 HTTP 参数,则表示用户未能提供有效的用户名或密码。

  • 如果找到名为 logout 的 HTTP 参数,则表示用户已成功注销。

许多用户的需求仅限于自定义登录页面。但如有需要,您可以通过额外配置来自定义前面展示的所有内容。

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

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