跳到主要内容
版本:6.4.3

Servlet API 集成

QWen Max 中英对照 Servlet APIs Servlet API integration

Servlet 2.5+ 集成​

本节描述了 Spring Security 如何与 Servlet 2.5 规范集成。

HttpServletRequest.getRemoteUser()​

HttpServletRequest.getRemoteUser() 返回 SecurityContextHolder.getContext().getAuthentication().getName() 的结果,这通常是当前用户名。如果你希望在应用程序中显示当前用户名,这会很有用。此外,你可以检查其是否为 null 以确定用户是否已认证或为匿名。知道用户是否已认证对于决定某些 UI 元素是否应该显示非常有用(例如,仅在用户已认证时才显示的注销链接)。

HttpServletRequest.getUserPrincipal()​

HttpServletRequest.getUserPrincipal() 返回 SecurityContextHolder.getContext().getAuthentication() 的结果。这意味着它是一个 Authentication,在使用基于用户名和密码的身份验证时,通常是一个 UsernamePasswordAuthenticationToken 的实例。如果你需要关于用户的一些额外信息,这可能会很有用。例如,你可能创建了一个自定义的 UserDetailsService,它返回一个包含用户的名字和姓氏的自定义 UserDetails。你可以通过以下方式获取这些信息:

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();
备注

需要注意的是,在整个应用程序中执行如此多的逻辑通常是不好的做法。相反,应该将其集中起来,以减少 Spring Security 和 Servlet API 之间的任何耦合。

HttpServletRequest.isUserInRole(String)​

HttpServletRequest.isUserInRole(String) 用于确定 SecurityContextHolder.getContext().getAuthentication().getAuthorities() 是否包含传递给 isUserInRole(String) 的角色的 GrantedAuthority。通常,用户不应该将 ROLE_ 前缀传递给此方法,因为该前缀会自动添加。例如,如果你想确定当前用户是否有 "ROLE_ADMIN" 权限,你可以使用以下代码:

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

这可能有助于确定是否应显示某些 UI 组件。例如,你可能只在当前用户是管理员时才显示管理员链接。

Servlet 3+ 集成​

以下章节描述了Spring Security所集成的Servlet 3方法。

HttpServletRequest.authenticate(HttpServletResponse)​

你可以使用 HttpServletRequest.authenticate(HttpServletResponse) 方法来确保用户已通过身份验证。如果用户未通过身份验证,将使用配置的 AuthenticationEntryPoint 请求用户进行身份验证(重定向到登录页面)。

HttpServletRequest.login(String,String)​

您可以使用 HttpServletRequest.login(String,String) 方法通过当前的 AuthenticationManager 对用户进行身份验证。例如,以下代码将尝试使用用户名 user 和密码 password 进行身份验证:

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

如果您希望 Spring Security 处理失败的认证尝试,则不需要捕获 ServletException。

HttpServletRequest.logout()​

你可以使用 HttpServletRequest.logout() 方法来注销当前用户。

通常,这意味着 SecurityContextHolder 会被清空,HttpSession 会被失效,任何“记住我”认证会被清理,等等。但是,配置的 LogoutHandler 实现会根据你的 Spring Security 配置有所不同。请注意,在调用 HttpServletRequest.logout() 之后,你仍然需要负责写入响应。通常,这将涉及重定向到欢迎页面。

AsyncContext.start(Runnable)​

AsyncContext.start(Runnable) 方法确保您的凭证被传播到新的 Thread。通过使用 Spring Security 的并发支持,Spring Security 会覆盖 AsyncContext.start(Runnable),以确保在处理 Runnable 时使用当前的 SecurityContext。以下示例输出当前用户的 Authentication:

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);
}
}
});

异步 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 Security 确保你的 SecurityContext 也会在异步请求中传播。

那么它是如何工作的呢?如果你对此不感兴趣,可以跳过本节的剩余部分。大部分内容都内置于 Servlet 规范中,但 Spring Security 进行了一些微调,以确保异步请求能够正常工作。在 Spring Security 3.2 之前,SecurityContext 会在 HttpServletResponse 被提交时自动从 SecurityContextHolder 中保存。这在异步环境中可能会导致问题。请考虑以下示例:

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();

问题是这个 Thread 对 Spring Security 来说是未知的,因此 SecurityContext 没有被传播到它。这意味着,当我们提交 HttpServletResponse 时,没有 SecurityContext。当 Spring Security 在提交 HttpServletResponse 时自动保存 SecurityContext,它会丢失一个已登录的用户。

从 3.2 版本开始,Spring Security 足够智能,不再在调用 HttpServletRequest.startAsync() 后立即在提交 HttpServletResponse 时自动保存 SecurityContext。

Servlet 3.1+ 集成​

以下部分描述了Spring Security所集成的Servlet 3.1方法。

HttpServletRequest#changeSessionId()​

HttpServletRequest.changeSessionId() 是 Servlet 3.1 及更高版本中用于防范 会话固定 攻击的默认方法。