servlet-authentication-passwords-form.md 7.8 KB
Newer Older
dallascao's avatar
dallascao 已提交
1 2 3 4 5 6
# 表单登录

Spring 安全性为正在通过 HTML 表单提供的用户名和密码提供支持。本节详细介绍了 Spring 安全性中基于表单的身份验证的工作方式。

让我们来看看基于表单的登录在 Spring 安全性中是如何工作的。首先,我们来看看用户是如何被重定向到 Log In 表单的。

茶陵後's avatar
茶陵後 已提交
7
![LoginurlauthenticationEntryPoint ](https://docs.spring.io/spring-security/reference/_images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png)
dallascao's avatar
dallascao 已提交
8 9 10 11 12

图 1。重定向到登录页面

该图构建于我们的[`SecurityFilterChain`](../../architecture.html# Servlet-SecurityFilterchain)图。

茶陵後's avatar
茶陵後 已提交
13
![number 1](https://docs.spring.io/spring-security/reference/_images/icons/number_1.png)首先,用户向资源`/private`发出未经授权的请求。
dallascao's avatar
dallascao 已提交
14

茶陵後's avatar
茶陵後 已提交
15
![number 2](https://docs.spring.io/spring-security/reference/_images/icons/number_2.png) Spring security 的[`FilterSecurityInterceptor`](.../授权/authorization/authorization/authorization-requests.html# Servlet-authorization-filtersecurityinterceptor)通过抛出`AccessDeniedException`表示未经验证的请求是*拒绝*
dallascao's avatar
dallascao 已提交
16

茶陵後's avatar
茶陵後 已提交
17
![number 3](https://docs.spring.io/spring-security/reference/_images/icons/number_3.png)由于未对用户进行身份验证,[`ExceptionTranslationFilter`](..../architecture.html# Servlet-ExceptionTranslationFilter)启动*启动身份验证*,并用配置的[`AuthenticationEntryPoint`](../architecture.html# Servlet-authentication-authentrationEntryPoint)向登录页面发送重定向。在大多数情况下,`AuthenticationEntryPoint`是[`LoginUrlAuthenticationEntryPoint`](https://DOCS. Spring.io/ Spring-security/site/DOCS/5.6.2/api/org/springframework/security/web/authentication/loginurlauthenticationentrypoint.html)的一个实例。
dallascao's avatar
dallascao 已提交
18

茶陵後's avatar
茶陵後 已提交
19
![number 4](https://docs.spring.io/spring-security/reference/_images/icons/number_4.png)然后浏览器将请求重定向到的登录页面。
dallascao's avatar
dallascao 已提交
20

茶陵後's avatar
茶陵後 已提交
21
![number 5](https://docs.spring.io/spring-security/reference/_images/icons/number_5.png)应用程序中的某个内容,必须[呈现登录页面](#servlet-authentication-form-custom)
dallascao's avatar
dallascao 已提交
22 23 24

当提交用户名和密码时,`UsernamePasswordAuthenticationFilter`将对用户名和密码进行身份验证。`UsernamePasswordAuthenticationFilter`扩展了[抽象处理过滤器](../architecture.html#servlet-authentication-abstractprocessingfilter),所以这个图看起来应该很相似。

茶陵後's avatar
茶陵後 已提交
25
![用户名 passwordauthenticationfilter ](https://docs.spring.io/spring-security/reference/_images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png)
dallascao's avatar
dallascao 已提交
26 27 28 29 30

图 2。验证用户名和密码

该图构建于我们的[`SecurityFilterChain`](../../architecture.html# Servlet-SecurityFilterchain)图。

茶陵後's avatar
茶陵後 已提交
31
![number 1](https://docs.spring.io/spring-security/reference/_images/icons/number_1.png)当用户提交他们的用户名和密码时,`UsernamePasswordAuthenticationFilter`通过从`HttpServletRequest`中提取用户名和密码,创建一个`UsernamePasswordAuthenticationToken`,这是一种[`Authentication`](../architecture.html# Servlet-authentication-authentication)的类型。
dallascao's avatar
dallascao 已提交
32

茶陵後's avatar
茶陵後 已提交
33
![number 2](https://docs.spring.io/spring-security/reference/_images/icons/number_2.png)接下来,将`UsernamePasswordAuthenticationToken`传递到`AuthenticationManager`中以进行身份验证。`AuthenticationManager`的详细内容取决于[用户信息被存储](index.html#servlet-authentication-unpwd-storage)的方式。
dallascao's avatar
dallascao 已提交
34

茶陵後's avatar
茶陵後 已提交
35
![number 3](https://docs.spring.io/spring-security/reference/_images/icons/number_3.png)如果身份验证失败,则*失败*
dallascao's avatar
dallascao 已提交
36 37 38 39 40 41 42

* [SecurityContextholder ](../architecture.html#servlet-authentication-securitycontextholder)被清除。

* 调用`RememberMeServices.loginFail`。如果 Remember Me 没有配置,这是一个禁止操作。

* 调用`AuthenticationFailureHandler`

茶陵後's avatar
茶陵後 已提交
43
![number 4](https://docs.spring.io/spring-security/reference/_images/icons/number_4.png)如果身份验证成功,则*成功*
dallascao's avatar
dallascao 已提交
44 45 46 47 48 49 50 51 52 53 54

* `SessionAuthenticationStrategy`被通知有一个新的登录。

* [认证](../architecture.html#servlet-authentication-authentication)设置在[SecurityContextholder ](../architecture.html#servlet-authentication-securitycontextholder)上。

* 调用`RememberMeServices.loginSuccess`。如果 Remember Me 没有配置,这是一个禁止操作。

* `ApplicationEventPublisher`发布`InteractiveAuthenticationSuccessEvent`

* 调用`AuthenticationSuccessHandler`。通常这是一个`SimpleUrlAuthenticationSuccessHandler`,当我们重定向到登录页面时,它将重定向到由[`ExceptionTranslationFilter`](../../architecture.html# Servlet-ExceptionTranslationFilter)保存的请求。

爱摸鱼的阿恒's avatar
爱摸鱼的阿恒 已提交
55
Spring 默认情况下启用安全表单登录。然而,只要提供了任何基于 Servlet 的配置,就必须显式地提供基于表单的登录。可以在下面找到最小的、显式的 Java 配置:
dallascao's avatar
dallascao 已提交
56 57 58

例 1。表单登录

爱摸鱼的阿恒's avatar
爱摸鱼的阿恒 已提交
59
Java
dallascao's avatar
dallascao 已提交
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

```
protected void configure(HttpSecurity http) {
	http
		// ...
		.formLogin(withDefaults());
}
```

XML

```
<http>
	<!-- ... -->
	<form-login />
</http>
```

Kotlin

```
fun configure(http: HttpSecurity) {
	http {
		// ...
		formLogin { }
	}
}
```

在这种配置 Spring 中,Security 将呈现一个默认的登录页面。大多数生产应用程序将需要一个自定义日志的形式。

下面的配置演示了如何在表单中提供自定义日志。

例 2。自定义登录表单配置

爱摸鱼的阿恒's avatar
爱摸鱼的阿恒 已提交
95
Java
dallascao's avatar
dallascao 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

```
protected void configure(HttpSecurity http) throws Exception {
	http
		// ...
		.formLogin(form -> form
			.loginPage("/login")
			.permitAll()
		);
}
```

XML

```
<http>
	<!-- ... -->
	<intercept-url pattern="/login" access="permitAll" />
	<form-login login-page="/login" />
</http>
```

Kotlin

```
fun configure(http: HttpSecurity) {
	http {
		// ...
		formLogin {
			loginPage = "/login"
			permitAll()
		}
	}
}
```

当登录页面在 Spring 安全配置中指定时,你负责呈现该页面。下面是一个[Thymeleaf](https://www.thymeleaf.org/)模板,该模板生成符合`/login`登录页面的 HTML 登录表单:

例 3。登录表单

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 表单,有几个关键点:

* 表单应该执行`post``/login`

* 表单将需要包含一个[CSRF Token](../../exploits/csrf.html#servlet-csrf),由 ThymeLeaf 表示为[自动包含](../../exploits/csrf.html#servlet-csrf-include-form-auto)

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

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

* 如果发现 HTTP 参数错误,则表示用户未能提供有效的用户名/密码。

* 如果找到了 HTTP 参数注销,则表示用户已成功注销。

许多用户只需要定制登录页面就可以了。然而,如果需要的话,上面的所有内容都可以通过额外的配置进行定制。

如果你正在使用 Spring MVC,你将需要一个控制器,该控制器将`GET /login`映射到我们创建的登录模板。下面可以看到最小样本`LoginController`:

例 4。登录控制器

Java

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

Kotlin

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

[读取用户名/密码](input.html)[Basic](basic.html)