对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

Servlet 2.5+ 集成

HttpServletRequest.getRemoteUser()

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

HttpServletRequest.getUserPrincipal()

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

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

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

HttpServletRequest.isUserInRole(字符串)

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

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

这对于确定是否应显示某些 UI 组件可能很有用。 例如,仅当当前用户是管理员时,才可以显示管理员链接。Spring中文文档

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

Servlet 3+ 集成

以下部分介绍了 Spring Security 集成的 Servlet 3 方法。Spring中文文档

HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse)

可以使用 HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse) 方法确保用户经过身份验证。 如果未通过身份验证,则配置的 AuthenticationEntryPoint 将用于请求用户进行身份验证(即重定向到登录页面)。Spring中文文档

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

HttpServletRequest.login(String,String) 方法可用于使用当前 . 例如,以下人员将尝试使用用户名“user”和密码“password”进行身份验证:AuthenticationManagerSpring中文文档

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中文文档

HttpServletRequest.logout()

HttpServletRequest.logout() 方法可用于注销当前用户。Spring中文文档

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

AsyncContext.start(可运行)

AsyncContext.start(Runnable) 方法,用于确保将凭据传播到新线程。 使用 Spring Security 的并发支持,Spring Security 会覆盖 AsyncContext.start(Runnable),以确保在处理 Runnable 时使用当前的 SecurityContext。 例如,以下内容将输出当前用户的身份验证:Spring中文文档

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中文文档

<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中文文档

<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中文文档

那么它是如何工作的呢?如果您真的不感兴趣,请随时跳过本节的其余部分,否则请继续阅读。 其中大部分都内置在 Servlet 规范中,但 Spring Security 做了一些调整,以确保事情正确地处理异步请求。 在 Spring Security 3.2 之前,SecurityContextHolder 中的 SecurityContext 会在提交 HttpServletResponse 后立即自动保存。 这可能会导致异步环境中出现问题。 例如,请考虑以下事项:Spring中文文档

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中文文档

从 3.2 版开始,Spring Security 足够聪明,一旦调用 HttpServletRequest.startAsync() ,就不再在提交 HttpServletResponse 时自动保存 SecurityContext。Spring中文文档

如果希望 Spring Security 处理失败的身份验证尝试,则无需捕获 ServletException。Spring中文文档

Servlet 3.1+ 集成

以下部分介绍了 Spring Security 集成的 Servlet 3.1 方法。Spring中文文档

HttpServletRequest#changeSessionId()

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