# 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
```
| |应该注意的是,在整个应用程序中执行这么多逻辑通常是糟糕的做法。
相反,应该将其集中以减少 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模式,如下所示:
```
```
接下来,你需要确保你的SpringSecurityFilterchain是为处理异步请求而设置的。
```
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
true
springSecurityFilterChain
/*
REQUEST
ASYNC
```
就是这样!现在 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)