跳到主要内容

表单登录

QWen Max 中英对照 Form Form Login

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

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

loginurlauthenticationentrypoint

图 1. 重定向到登录页面

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

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

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

3 由于用户未通过身份验证,ExceptionTranslationFilter 将启动 Start Authentication 并重定向到带有配置的 AuthenticationEntryPoint 的登录页面。在大多数情况下,AuthenticationEntryPointLoginUrlAuthenticationEntryPoint 的实例。

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

5 应用程序中的某个部分必须渲染登录页面

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

usernamepasswordauthenticationfilter

图 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 如果身份验证成功,则为 Success

  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());
// ...
}
java

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

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

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

当在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>
xml

关于默认的 HTML 表单,有几点需要注意:

  • 表单应该向 /login 执行一个 post 操作。

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

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

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

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

  • 如果发现名为 logout 的 HTTP 参数,表示用户已成功登出。

许多用户只需要自定义登录页面。然而,如果需要的话,您可以通过额外的配置来自定义前面展示的所有内容。

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

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