# 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) (opens new window)方法来确保对用户进行身份验证。如果未对它们进行身份验证,则将使用配置的身份验证中心点来请求用户进行身份验证(即重定向到登录页面)。
# HttpServletRequest.login(字符串,字符串)
可以使用HttpServletRequest.login(字符串,字符串) (opens new window)方法对当前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() (opens new window)方法将当前用户注销。
通常,这意味着SecurityContextholder将被清除,HttpSession将无效,任何“记住我”的身份验证都将被清除,等等。然而,所配置的LogouthAndler实现将根据你的 Spring 安全配置而有所不同。需要注意的是,在调用了HttpServletRequest.logout()之后,你仍然负责编写响应。通常情况下,这需要重定向到欢迎页面。
# AsyncContext.start(可运行)
确保你的凭据将被传播到新线程的AsyncContext.start(可运行) (opens new window)方法。利用 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攻击的默认方法。
← 本地化 Spring 数据集成 →