表单登录(Form Login)
本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
Spring Security提供了对通过HTML表单提供用户名和密码的支持。本节将详细介绍基于表单的认证在Spring Security中如何工作。
本节研究了基于表单的登录在Spring Security中是如何工作的。首先,我们看到用户是如何被重定向到登录表单的。
上图建立在 SecurityFilterChain
图上。
首先,一个用户向其未被授权的资源(/private
)发出一个未经认证的请求。
pring Security 的 AuthorizationFilter
通过抛出一个 AccessDeniedException
来表明未经认证的请求被拒绝了。
由于用户没有被认证,ExceptionTranslationFilter
启动了 Start Authentication,并发送一个重定向到配置的 AuthenticationEntryPoint
的登录页面。在大多数情况下, AuthenticationEntryPoint
是 LoginUrlAuthenticationEntryPoint
的一个实例。
浏览器请求进入其被重定向的登录页面。
应用程序中的某些东西,必须渲染登录页面。
当用户名和密码被提交后,UsernamePasswordAuthenticationFilter
会对用户名和密码进行认证。UsernamePasswordAuthenticationFilter
扩展了 AbstractAuthenticationProcessingFilter,所以下面的图看起来应该很相似。
上图建立在 SecurityFilterChain
图上。
当用户提交他们的用户名和密码时,UsernamePasswordAuthenticationFilter
通过从 HttpServletRequest
实例中提取用户名和密码,创建一个 UsernamePasswordAuthenticationToken
,这是一种 Authentication
类型。
接下来,UsernamePasswordAuthenticationToken
被传入 AuthenticationManager
实例,以进行认证。AuthenticationManager
的细节取决于 用户信息的存储方式。
如果认证失败,则为 Failure.
-
RememberMeServices.loginFail
被调用。如果没有配置remember me,这就是一个无用功。参见Javadoc中的RememberMeServices
接口。 -
AuthenticationFailureHandler
被调用。参见Javadoc中的AuthenticationFailureHandler
类。
如果认证成功,则 Success。
-
SessionAuthenticationStrategy
被通知有新的登录。参见Javadoc中的SessionAuthenticationStrategy
接口。 -
Authentication 被设置在 SecurityContextHolder 上。参见 Javadoc 中的
SecurityContextPersistenceFilter
类。 -
RememberMeServices.loginSuccess
被调用。如果没有配置remember me,这就是一个无用功。参见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
-
public SecurityFilterChain filterChain(HttpSecurity http) { http .formLogin(form -> form .loginPage("/login") .permitAll() ); // ... }
- XML
-
<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表单,有几个关键点。
-
表单应该以
post
方法请求/login
。 -
该表单需要包含 CSRF Token,Thymeleaf 会 自动包含。
-
该表单应在一个名为
username
的参数中指定用户名。 -
表单应该在一个名为
password
的参数中指定密码。 -
如果发现名为
error
的HTTP参数,表明用户未能提供一个有效的用户名或密码。 -
如果发现名为
logout
的HTTP参数,表明用户已经成功注销。
许多用户除了定制登录页面外,并不需要更多的东西。然而,如果需要的话,你可以通过额外的配置来定制前面显示的一切。
如果你使用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"
}
}