表单登录
Spring Security 支持通过HTML表单提交用户名和密码进行身份验证。本节将详细介绍基于表单的身份验证在Spring Security中的工作原理。
本节将探讨基于表单的登录在Spring Security中的工作原理。首先,我们来看用户是如何被重定向到登录表单的:

图 1. 重定向至登录页面
上图基于我们的SecurityFilterChain示意图构建。
1 首先,用户向未经授权的资源(/private)发出未经身份验证的请求。
2 Spring Security 的 AuthorizationFilter 通过抛出 AccessDeniedException 异常,表明未认证的请求被拒绝。
3 由于用户未经认证,ExceptionTranslationFilter 会启动身份验证流程,并通过配置的 AuthenticationEntryPoint 发送重定向到登录页面。在大多数情况下,AuthenticationEntryPoint 是 LoginUrlAuthenticationEntryPoint 的一个实例。
4 浏览器请求被重定向到的登录页面。
5 应用程序中的某些内容必须渲染登录页面。
当用户名和密码被提交时,UsernamePasswordAuthenticationFilter 会从 HttpServletRequest 实例中提取用户名和密码,创建一个 UsernamePasswordAuthenticationToken,这是一种 Authentication 类型。UsernamePasswordAuthenticationFilter 继承自 AbstractAuthenticationProcessingFilter,因此下面的图表看起来应该非常相似:

图 2. 用户名与密码认证
该图基于我们的SecurityFilterChain示意图构建。
1 当用户提交用户名和密码时,UsernamePasswordAuthenticationFilter 会从 HttpServletRequest 实例中提取用户名和密码,创建一个 UsernamePasswordAuthenticationToken,这是一种 Authentication 类型。
2 接下来,UsernamePasswordAuthenticationToken 会被传入 AuthenticationManager 实例进行认证。AuthenticationManager 的具体实现细节取决于用户信息的存储方式。
3 如果身份验证失败,则返回 Failure。
-
调用
RememberMeServices.loginFail。如果未配置记住我功能,此操作无效。请参阅 Javadoc 中的 RememberMeServices 接口。 -
调用
AuthenticationFailureHandler。请参阅 Javadoc 中的 AuthenticationFailureHandler 类。
4 如果认证成功,则显示 成功。
-
SessionAuthenticationStrategy会收到新登录的通知。详见 Javadoc 中的 SessionAuthenticationStrategy 接口。 -
Authentication 被设置到 SecurityContextHolder 中。详见 Javadoc 中的 SecurityContextPersistenceFilter 类。
-
调用
RememberMeServices.loginSuccess。如果未配置记住我功能,此操作无效。详见 Javadoc 中的 RememberMeServices 接口。 -
ApplicationEventPublisher发布一个InteractiveAuthenticationSuccessEvent事件。 -
调用
AuthenticationSuccessHandler。通常,这是一个SimpleUrlAuthenticationSuccessHandler,它会重定向到由 ExceptionTranslationFilter 保存的请求(该请求是在我们重定向到登录页面时保存的)。
默认情况下,Spring Security 启用了表单登录功能。然而,一旦提供了任何基于 Servlet 的配置,就必须显式地提供基于表单的登录。以下示例展示了一个最小化的显式 Java 配置:
- 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 会渲染一个默认的登录页面。大多数生产环境的应用都需要自定义登录表单。
以下配置展示了如何提供自定义登录表单。
- 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模板会生成一个符合/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表单,有几个关键点:
许多用户的需求仅限于自定义登录页面。但如有需要,您可以通过额外配置来自定义前面展示的所有内容。
如果你使用 Spring MVC,需要一个控制器将 GET /login 映射到我们创建的登录模板。以下示例展示了一个最简化的 LoginController:
- Java
- Kotlin
@Controller
class LoginController {
@GetMapping("/login")
String login() {
return "login";
}
}
@Controller
class LoginController {
@GetMapping("/login")
fun login(): String {
return "login"
}
}