对于最新的稳定版本,请使用 Spring Security 6.4.1spring-doc.cn

Servlet API 集成

Servlet 2.5+ 集成

HttpServletRequest.getRemoteUser()

HttpServletRequest.getRemoteUser() 将返回其结果,其结果通常是当前用户名。 如果要在应用程序中显示当前用户名,这可能很有用。 此外,检查此字段是否为 null 可用于指示用户是否已进行身份验证或匿名。 了解用户是否经过身份验证有助于确定是否应显示某些 UI 元素(即,仅当用户经过身份验证时,才应显示注销链接)。SecurityContextHolder.getContext().getAuthentication().getName()spring-doc.cn

HttpServletRequest.getUserPrincipal()

HttpServletRequest.getUserPrincipal() 将返回 的结果。 这意味着,当使用基于用户名和密码的身份验证时,它通常是一个实例。 如果您需要有关用户的其他信息,这可能很有用。 例如,您可能已创建一个自定义,该自定义返回包含用户的名字和姓氏的自定义。 您可以通过以下方式获取此信息:SecurityContextHolder.getContext().getAuthentication()AuthenticationUsernamePasswordAuthenticationTokenUserDetailsServiceUserDetailsspring-doc.cn

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

应该注意的是,在整个应用程序中执行如此多的 logic 通常是不好的做法。 相反,应该将其集中以减少 Spring Security 和 Servlet API 的任何耦合。spring-doc.cn

HttpServletRequest.isUserInRole(字符串)

HttpServletRequest.isUserInRole(String) 将确定是否包含 一个,并将角色传递给 。 通常,用户不应将 “ROLE_” 前缀传入此方法,因为它是自动添加的。 例如,如果要确定当前用户是否具有“ROLE_ADMIN”权限,则可以使用以下内容:SecurityContextHolder.getContext().getAuthentication().getAuthorities()GrantedAuthorityisUserInRole(String)spring-doc.cn

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

这对于确定是否应显示某些 UI 组件可能很有用。 例如,只有当当前用户是管理员时,您才可以显示管理员链接。spring-doc.cn

Servlet 3+ 集成

以下部分描述了 Spring Security 与之集成的 Servlet 3 方法。spring-doc.cn

HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse)

HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse) 方法可用于确保用户经过身份验证。 如果他们没有经过身份验证,则配置的 AuthenticationEntryPoint 将用于请求用户进行身份验证(即重定向到登录页面)。spring-doc.cn

HttpServletRequest.login(字符串,字符串)

HttpServletRequest.login(String,String) 方法可用于使用当前的 . 例如,以下将尝试使用用户名 “user” 和密码 “password” 进行身份验证:AuthenticationManagerspring-doc.cn

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

如果您希望 Spring Security 处理失败的身份验证尝试,则无需捕获ServletException。spring-doc.cn

HttpServletRequest.logout()

HttpServletRequest.logout() 方法可用于注销当前用户。spring-doc.cn

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

AsyncContext.start(可运行)

AsyncContext.start(Runnable) 方法,用于确保您的凭证将传播到新线程。 使用 Spring Security 的并发支持,Spring Security 覆盖AsyncContext.start(Runnable)以确保在处理Runnable时使用当前的SecurityContext。 例如,以下将输出当前用户的 Authentication:spring-doc.cn

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);
		}
	}
});
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 架构,如下所示:spring-doc.cn

<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已设置为处理异步请求。spring-doc.cn

<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也在异步请求上传播。spring-doc.cn

那么它是如何工作的呢?如果您不是真的感兴趣,请随时跳过本节的其余部分,否则请继续阅读。 其中大部分都内置在 Servlet 规范中,但 Spring Security 会进行一些调整,以确保异步请求正常工作。 在 Spring Security 3.2 之前,一旦提交HttpServletResponse,就会自动保存来自SecurityContextHolder的SecurityContext。 这可能会导致异步环境中出现问题。 例如,请考虑以下情况:spring-doc.cn

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

问题是 Spring Security 不知道这个线程,因此SecurityContext不会传播到它。 这意味着当我们提交HttpServletResponse时,没有SecurityContext。 当 Spring Security 在提交HttpServletResponse时自动保存SecurityContext时,它将丢失我们的登录用户。spring-doc.cn

从版本 3.2 开始,Spring Security 已经足够智能,一旦调用HttpServletRequest.startAsync(),就不再在提交HttpServletResponse时自动保存SecurityContext。spring-doc.cn

Servlet 3.1+ 集成

以下部分描述了 Spring Security 与之集成的 Servlet 3.1 方法。spring-doc.cn

HttpServletRequest#changeSessionId()

HttpServletRequest.changeSessionId() 是 Servlet 3.1 及更高版本中防止会话固定攻击的默认方法。spring-doc.cn