# Servlet API集成

## Servlet 2.5+集成

### HttpServletRequest.getRemoteUser（）

[HttpServletRequest.getRemoteUser（）](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()）将返回`SecurityContextHolder.getContext().getAuthentication().getName()`的结果，这通常是当前的用户名。如果你希望在应用程序中显示当前用户名，这将非常有用。此外，检查此值是否为null可以用来指示用户是否已通过身份验证或是匿名的。了解用户是否经过了身份验证，对于确定某些UI元素是否应该显示是有用的（例如，只有在用户经过身份验证的情况下才应该显示注销链接）。

### HttpServletRequest.getUserPrincipal（）

[HttpServletRequest.getUserPrincipal（）](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()）将返回`SecurityContextHolder.getContext().getAuthentication()`的结果。这意味着它是`Authentication`，当使用基于用户名和密码的身份验证时，它通常是`UsernamePasswordAuthenticationToken`的一个实例。如果你需要有关你的用户的其他信息，这可能是有用的。例如，你可能已经创建了一个自定义`UserDetailsService`，它返回一个自定义的`UserDetails`，其中包含用户的姓和名。你可以通过以下方式获得此信息：

Java

```
Authentication auth = httpServletRequest.getUserPrincipal();
// assume integrated custom UserDetails called MyCustomUserDetails
// by default, typically instance of UserDetails
MyCustomUserDetails userDetails = (MyCustomUserDetails) auth.getPrincipal();
String firstName = userDetails.getFirstName();
String lastName = userDetails.getLastName();
```

Kotlin

```
val auth: Authentication = httpServletRequest.getUserPrincipal()
// assume integrated custom UserDetails called MyCustomUserDetails
// by default, typically instance of UserDetails
val userDetails: MyCustomUserDetails = auth.principal as MyCustomUserDetails
val firstName: String = userDetails.firstName
val lastName: String = userDetails.lastName
```

|   |应该注意的是，在整个应用程序中执行这么多逻辑通常是糟糕的做法。<br/>相反，应该将其集中以减少 Spring 安全性和 Servlet API的任何耦合。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### HttpServletRequest.isUserinRole（字符串）

[HttpServletRequest.isUserinRole（字符串）](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)）将确定`SecurityContextHolder.getContext().getAuthentication().getAuthorities()`是否包含带有传递到`isUserInRole(String)`的角色的`GrantedAuthority`。通常，用户不应该将“role\_”前缀传入此方法，因为它是自动添加的。例如，如果你想确定当前用户是否拥有“role\_admin”权限，可以使用以下方法：

Java

```
boolean isAdmin = httpServletRequest.isUserInRole("ADMIN");
```

Kotlin

```
val isAdmin: Boolean = httpServletRequest.isUserInRole("ADMIN")
```

这对于确定是否应该显示某些UI组件可能很有用。例如，你可能仅在当前用户是管理员的情况下才显示管理链接。

## Servlet 3+集成
下面的部分描述了 Spring 安全性集成的 Servlet 3种方法。

### HttpServletRequest.Authenticate（HttpServletRequest，HttpServletResponse）

可以使用[HttpServletRequest.Authenticate（HttpServletRequest，HttpServletResponse）](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#authenticate%28javax.servlet.http.HttpServletResponse%29)方法来确保对用户进行身份验证。如果未对它们进行身份验证，则将使用配置的身份验证中心点来请求用户进行身份验证（即重定向到登录页面）。

### HttpServletRequest.login（字符串，字符串）
可以使用[HttpServletRequest.login（字符串，字符串）](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login%28java.lang.String,%20java.lang.String%29)方法对当前`AuthenticationManager`的用户进行身份验证。例如，下面将尝试使用用户名“user”和密码“password”进行身份验证：

Java

```
try {
httpServletRequest.login("user","password");
} catch(ServletException ex) {
// fail to authenticate
}
```

Kotlin

```
try {
    httpServletRequest.login("user", "password")
} catch (ex: ServletException) {
    // fail to authenticate
}
```

|   |如果你希望 Spring 安全性来处理失败的身份验证尝试，那么不需要捕获ServletException。|
|---|---------------------------------------------------------------------------------------------------------------------------|

### HttpServletRequest.logout（）
可以使用[HttpServletRequest.logout（）](https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout%28%29)方法将当前用户注销。

通常，这意味着SecurityContextholder将被清除，HttpSession将无效，任何“记住我”的身份验证都将被清除，等等。然而，所配置的LogouthAndler实现将根据你的 Spring 安全配置而有所不同。需要注意的是，在调用了HttpServletRequest.logout（）之后，你仍然负责编写响应。通常情况下，这需要重定向到欢迎页面。

### AsyncContext.start（可运行）
确保你的凭据将被传播到新线程的[AsyncContext.start（可运行）](https://docs.oracle.com/javaee/6/api/javax/servlet/AsyncContext.html#start%28java.lang.Runnable%29)方法。利用 Spring Security的并发支持， Spring Security覆盖了AsyncContext.Start，以确保在处理Runnable时使用当前的SecurityContext。例如，下面将输出当前用户的身份验证：

Java

```
final AsyncContext async = httpServletRequest.startAsync();
async.start(new Runnable() {
	public void run() {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		try {
			final HttpServletResponse asyncResponse = (HttpServletResponse) async.getResponse();
			asyncResponse.setStatus(HttpServletResponse.SC_OK);
			asyncResponse.getWriter().write(String.valueOf(authentication));
			async.complete();
		} catch(Exception ex) {
			throw new RuntimeException(ex);
		}
	}
});
```

Kotlin

```
val async: AsyncContext = httpServletRequest.startAsync()
async.start {
    val authentication: Authentication = SecurityContextHolder.getContext().authentication
    try {
        val asyncResponse = async.response as HttpServletResponse
        asyncResponse.status = HttpServletResponse.SC_OK
        asyncResponse.writer.write(String.valueOf(authentication))
        async.complete()
    } catch (ex: Exception) {
        throw RuntimeException(ex)
    }
}
```

### 异步 Servlet 支持
如果你正在使用基于 Java 的配置，那么你已经准备好了。如果你正在使用XML配置，那么有一些更新是必要的。第一步是确保你已经更新了web.xml，以至少使用3.0模式，如下所示：

```
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">

</web-app>
```

接下来，你需要确保你的SpringSecurityFilterchain是为处理异步请求而设置的。

```
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
	org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
```

就是这样！现在 Spring 安全性将确保你的SecurityContext也在异步请求上传播。

那么，它是如何工作的呢？如果你真的不感兴趣，请跳过这一节的其余部分，否则请继续阅读。这其中的大部分是内置在 Servlet 规范中的，但是 Spring 安全性做了一些调整，以确保异步请求能够正常工作。在 Spring Security3.2之前，一旦提交了HttpServletResponse，来自SecurityContextholder的SecurityContext就会自动保存。这可能会在异步环境中引起问题。例如，考虑以下几点：

Java

```
httpServletRequest.startAsync();
new Thread("AsyncThread") {
	@Override
	public void run() {
		try {
			// Do work
			TimeUnit.SECONDS.sleep(1);

			// Write to and commit the httpServletResponse
			httpServletResponse.getOutputStream().flush();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}.start();
```

Kotlin

```
httpServletRequest.startAsync()
object : Thread("AsyncThread") {
    override fun run() {
        try {
            // Do work
            TimeUnit.SECONDS.sleep(1)

            // Write to and commit the httpServletResponse
            httpServletResponse.outputStream.flush()
        } catch (ex: java.lang.Exception) {
            ex.printStackTrace()
        }
    }
}.start()
```

问题是安全性不知道这个线程，因此SecurityContext不会传播到它。这意味着当我们提交HttpServletResponse时，不存在SecurityContext。 Spring 当Security在提交HttpServletResponse时自动保存SecurityContext时，它将丢失已登录的用户。

自版本3.2以来， Spring 安全性已经足够聪明，不再在调用HttpServletRequest.startasync（）时提交HttpServletResponse时自动保存SecurityContext。

## Servlet 3.1+集成
下面的部分描述了 Spring 安全性集成的 Servlet 3.1方法。

### HttpServletRequest#changesessionID（）
在 Servlet 3.1及更高版本中，[HttpServletRequest.changesessionID（）](https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#changeSessionId()）是防止[Session Fixation](../authentication/session-management.html#ns-session-fixation)攻击的默认方法。

[本地化](localization.html)[Spring Data](data.html)