一般问题

本常见问题解答回答了以下一般问题:Spring中文文档

Spring Security 可以满足我的所有应用程序安全要求吗?

Spring Security 为您的身份验证和授权要求提供了一个灵活的框架,但构建安全应用程序还有许多其他注意事项超出了其范围。 Web 应用程序容易受到各种攻击的攻击,您应该熟悉这些攻击,最好在开始开发之前,以便从一开始就考虑到它们进行设计和编码。 查看 OWASP 网站,了解有关 Web 应用程序开发人员面临的主要问题以及可用于对付这些问题的对策的信息。Spring中文文档

为什么不使用 web.xml 安全性?

假设您正在开发一个基于 Spring 的企业应用程序。 通常需要解决四个安全问题:身份验证、Web 请求安全、服务层安全(实现业务逻辑的方法)和域对象实例安全性(不同的域对象可能具有不同的权限)。考虑到这些典型要求,我们有以下注意事项:Spring中文文档

  • 身份验证:Servlet 规范提供了一种身份验证方法。 但是,您需要将容器配置为执行身份验证,这通常需要编辑特定于容器的“领域”设置。 这使得配置不可移植。此外,如果您需要编写一个实际的 Java 类来实现容器的身份验证接口,它将变得更加不可移植。 使用 Spring Security,您可以实现完全的可移植性——直至 WAR 级别。 此外,Spring Security 还提供了经过生产验证的身份验证提供程序和机制的选择,这意味着您可以在部署时切换身份验证方法。 这对于编写需要在未知目标环境中工作的产品的软件供应商来说特别有价值。Spring中文文档

  • Web 请求安全性:Servlet 规范提供了一种保护请求 URI 的方法。 但是,这些 URI 只能用 servlet 规范自己的有限 URI 路径格式表示。 Spring Security 提供了一种更全面的方法。 例如,您可以使用 Ant 路径或正则表达式,您可以考虑 URI 的一部分,而不仅仅是请求的页面(例如, 您可以考虑 HTTP GET 参数),并且可以实现自己的配置数据运行时源。 这意味着您可以在 Web 应用程序的实际执行期间动态更改 Web 请求安全性。Spring中文文档

  • 服务层和域对象安全性:Servlet 规范中缺少对服务层安全性或域对象实例安全性的支持,这代表了多层应用程序的严重限制。 通常,开发人员要么忽略这些要求,要么在其 MVC 控制器代码中(或者更糟糕的是,在视图中)实现安全逻辑。这种方法存在严重的缺点:Spring中文文档

    • 关注点分离:授权是一个贯穿各领域的问题,应按此方式实施。 实现授权代码的 MVC 控制器或视图使得测试控制器和授权逻辑变得更加困难,更难调试,并且经常导致代码重复。Spring中文文档

    • 支持富客户端和 Web 服务:如果最终必须支持其他客户端类型,则嵌入在 Web 层中的任何授权代码都是不可重用的。 应该考虑 Spring 远程处理导出器仅导出服务层 bean(而不是 MVC 控制器)。因此,授权逻辑需要位于服务层中,以支持多种客户端类型。Spring中文文档

    • 分层问题:MVC 控制器或视图是不正确的体系结构层,在其中实现有关服务层方法或域对象实例的授权决策。 虽然主体可以传递到服务层以使其能够做出授权决策,但这样做会在每个服务层方法上引入一个额外的参数。 一种更优雅的方法是使用 a 来持有主体,尽管这可能会增加开发时间,使使用专用安全框架变得更加经济(基于成本效益)。ThreadLocalSpring中文文档

    • 授权码质量:人们常说 Web 框架“使做正确的事情变得更容易,做错误的事情变得更难”。安全框架是相同的,因为它们是以抽象的方式设计的,用于广泛的目的。 从头开始编写自己的授权代码并不能提供框架所能提供的“设计检查”,而且内部授权代码通常缺乏广泛部署、同行评审和新版本带来的改进。Spring中文文档

对于简单的应用程序,servlet 规范安全性可能就足够了。 尽管在 Web 容器可移植性、配置要求、有限的 Web 请求安全灵活性以及不存在的服务层和域对象实例安全性的上下文中考虑时,开发人员经常寻求替代解决方案的原因变得很清楚。Spring中文文档

需要哪些 Java 和 Spring Framework 版本?

Spring Security 3.0 和 3.1 至少需要 JDK 1.5,并且至少需要 Spring 3.0.3。 理想情况下,您应该使用最新版本以避免出现问题。Spring中文文档

Spring Security 2.0.x 要求最低 JDK 版本为 1.4,并且是针对 Spring 2.0.x 构建的。 它还应该与使用 Spring 2.5.x 的应用程序兼容。Spring中文文档

我有一个复杂的场景。可能出了什么问题?

(此答案通常通过处理特定方案来解决复杂方案。Spring中文文档

假设您是 Spring Security 的新手,并且需要构建一个支持 CAS 单点登录的应用程序,同时允许对某些 URL 进行本地基本身份验证,并针对多个后端用户信息源(LDAP 和 JDBC)进行身份验证。您复制了一些配置文件,但发现它不起作用。可能出了什么问题?Spring中文文档

您需要先了解要使用的技术,然后才能使用它们成功构建应用程序。 安全性很复杂。 使用登录表单和一些具有 Spring Security 命名空间的硬编码用户来设置简单的配置相当简单。 改用受支持的 JDBC 数据库也很容易。 但是,如果您尝试直接跳转到像此方案这样的复杂部署方案,您几乎肯定会感到沮丧。 设置 CAS 等系统、配置 LDAP 服务器和正确安装 SSL 证书所需的学习曲线有很大的飞跃。 所以你需要一步一个脚印。Spring中文文档

从 Spring Security 的角度来看,您应该做的第一件事是按照网站上的“入门”指南进行操作。 这将引导您完成一系列步骤来启动和运行,并了解框架的运行方式。 如果您使用不熟悉的其他技术,则应进行一些研究,并尝试确保在将它们组合到复杂系统中之前可以单独使用它们。Spring中文文档

常见问题

本节介绍了人们在使用 Spring Security 时遇到的最常见问题:Spring中文文档

当我尝试登录时,我收到一条错误消息,上面写着“凭据错误”。怎么了?

这意味着身份验证失败。 它没有说明原因,因为最好避免提供可能有助于攻击者猜测帐户名称或密码的详细信息。Spring中文文档

这也意味着,如果您在网上提出这个问题,除非您提供更多信息,否则您不应该期望得到答案。 与任何问题一样,您应该检查调试日志的输出,并记下任何异常堆栈跟踪和相关消息。 应单步执行调试器中的代码,以查看身份验证失败的位置和原因。 您还应该编写一个测试用例,用于在应用程序外部执行身份验证配置。 如果使用哈希密码,请确保存储在数据库中的值与应用程序中配置的密码生成的值完全相同PasswordEncoderSpring中文文档

当我尝试登录时,我的应用程序进入“无限循环”。这是怎么回事?

无限循环和重定向到登录页面的常见用户问题是由于意外将登录页面配置为“安全”资源引起的。 确保您的配置允许匿名访问登录页面,方法是将其从安全筛选器链中排除或将其标记为需要 .ROLE_ANONYMOUSSpring中文文档

如果包含 ,则可以使用该属性。如果使用标准命名空间配置设置,则此功能将自动可用。AccessDecisionManagerAuthenticatedVoterIS_AUTHENTICATED_ANONYMOUSLYSpring中文文档

从 Spring Security 2.0.1 开始,当您使用基于命名空间的配置时,会在加载应用程序上下文时进行检查,并在登录页面显示为受保护时记录警告消息。Spring中文文档

我收到异常,消息为“访问被拒绝(用户是匿名的);”。怎么了?

这是匿名用户首次尝试访问受保护资源时发生的调试级别消息。Spring中文文档

DEBUG [ExceptionTranslationFilter] - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.AccessDeniedException: Access is denied
at org.springframework.security.vote.AffirmativeBased.decide(AffirmativeBased.java:68)
at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:262)

这是正常的,不应该有什么可担心的。Spring中文文档

为什么即使我注销了应用程序,我仍然可以看到安全页面?

最常见的原因是您的浏览器已缓存该页面,并且您看到正在从浏览器缓存中检索的副本。 通过检查浏览器是否实际发送请求来验证这一点(检查您的服务器访问日志和调试日志或使用合适的浏览器调试插件,例如Firefox的“篡改数据”)。这与 Spring Security 无关,您应该配置应用程序或服务器以设置适当的响应标头。 请注意,从不缓存 SSL 请求。Cache-ControlSpring中文文档

我收到一个异常,并显示“在 SecurityContext 中找不到身份验证对象”的消息。怎么了?

以下列表显示了匿名用户首次尝试访问受保护资源时发生的另一条调试级消息。但是,此列表显示了在筛选器链配置中没有筛选器链时会发生什么情况:AnonymousAuthenticationFilterSpring中文文档

DEBUG [ExceptionTranslationFilter] - Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.AuthenticationCredentialsNotFoundException:
							An Authentication object was not found in the SecurityContext
at org.springframework.security.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:342)
at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:254)

这是正常的,不需要担心。Spring中文文档

我无法让 LDAP 身份验证正常工作。我的配置有什么问题?

请注意,LDAP 目录的权限通常不允许您读取用户的密码。 因此,通常无法使用 什么是UserDetailsService,我需要一个吗?,其中 Spring Security 将存储的密码与用户提交的密码进行比较。 最常见的方法是使用 LDAP“绑定”,这是 LDAP 协议支持的操作之一。使用这种方法,Spring Security 通过尝试以用户身份向目录进行身份验证来验证密码。Spring中文文档

LDAP 身份验证最常见的问题是缺乏对目录服务器树结构和配置的了解。 这因公司而异,因此您必须自己找出答案。 在将Spring Security LDAP配置添加到应用程序之前,您应该使用标准Java LDAP代码(不涉及Spring Security)编写一个简单的测试,并确保您可以首先使其正常工作。 例如,若要对用户进行身份验证,可以使用以下代码:Spring中文文档

@Test
public void ldapAuthenticationIsSuccessful() throws Exception {
		Hashtable<String,String> env = new Hashtable<String,String>();
		env.put(Context.SECURITY_AUTHENTICATION, "simple");
		env.put(Context.SECURITY_PRINCIPAL, "cn=joe,ou=users,dc=mycompany,dc=com");
		env.put(Context.PROVIDER_URL, "ldap://mycompany.com:389/dc=mycompany,dc=com");
		env.put(Context.SECURITY_CREDENTIALS, "joespassword");
		env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

		InitialLdapContext ctx = new InitialLdapContext(env, null);

}
@Test
fun ldapAuthenticationIsSuccessful() {
    val env = Hashtable<String, String>()
    env[Context.SECURITY_AUTHENTICATION] = "simple"
    env[Context.SECURITY_PRINCIPAL] = "cn=joe,ou=users,dc=mycompany,dc=com"
    env[Context.PROVIDER_URL] = "ldap://mycompany.com:389/dc=mycompany,dc=com"
    env[Context.SECURITY_CREDENTIALS] = "joespassword"
    env[Context.INITIAL_CONTEXT_FACTORY] = "com.sun.jndi.ldap.LdapCtxFactory"
    val ctx = InitialLdapContext(env, null)
}

会话管理

会话管理问题是问题的常见来源。 如果要开发 Java Web 应用程序,则应了解如何在 servlet 容器和用户浏览器之间维护会话。 您还应该了解安全 Cookie 和非安全 Cookie 之间的区别,以及使用 HTTP 和 HTTPS 并在两者之间切换的含义。 Spring Security 与维护会话或提供会话标识符无关。 这完全由 servlet 容器处理。Spring中文文档

我正在使用 Spring Security 的并发会话控制来防止用户同时登录多次。当我在登录后打开另一个浏览器窗口时,它不会阻止我再次登录。为什么我可以多次登录?

浏览器通常为每个浏览器实例维护一个会话。 您不能同时进行两个单独的会话。 因此,如果您在另一个窗口或选项卡中再次登录,则只是在同一会话中重新进行身份验证。 因此,如果您在另一个窗口或选项卡中再次登录,则将在同一会话中重新进行身份验证。 服务器对选项卡、窗口或浏览器实例一无所知。 它看到的只是 HTTP 请求,并根据它们包含的 cookie 的值将这些请求绑定到特定会话。 当用户在会话期间进行身份验证时,Spring Security 的并发会话控制会检查他们拥有的其他经过身份验证的会话的数量。 如果它们已使用同一会话进行身份验证,则重新身份验证不起作用。JSESSIONIDSpring中文文档

为什么当我通过 Spring Security 进行身份验证时,会话 ID 会发生变化?

使用默认配置,Spring Security 会在用户进行身份验证时更改会话 ID。 如果使用 Servlet 3.1 或更高版本的容器,则会话 ID 将仅更改。 如果您使用较旧的容器,Spring Security 会使现有会话失效,创建一个新会话,并将会话数据传输到新会话。 以这种方式更改会话标识符可防止“会话固定”攻击。 您可以在线和参考手册中找到有关此内容的更多信息。Spring中文文档

我使用 Tomcat(或其他一些 servlet 容器)并为我的登录页面启用了 HTTPS,然后切换回 HTTP。这是行不通的。身份验证后,我最终回到了登录页面。

它不起作用 - 我只是在身份验证后返回登录页面。Spring中文文档

发生这种情况是因为在HTTPS下创建的会话(其会话cookie被标记为“安全”)随后无法在HTTP下使用。浏览器不会将 cookie 发送回服务器,并且任何会话状态(包括安全上下文信息)都将丢失。首先在 HTTP 中启动会话应该可以工作,因为会话 cookie 未标记为安全。 但是,Spring Security 的会话固定保护可能会干扰这一点,因为它会导致新的会话 ID cookie 被发送回用户的浏览器,通常带有安全标志。 要解决此问题,您可以禁用会话固定保护。但是,在较新的 Servlet 容器中,您还可以将会话 cookie 配置为从不使用安全标志。Spring中文文档

一般来说,在HTTP和HTTPS之间切换不是一个好主意,因为任何使用HTTP的应用程序都容易受到中间人攻击。 为了真正安全,用户应该开始以 HTTPS 访问您的网站并继续使用它,直到他们注销。 即使从通过 HTTP 访问的页面单击 HTTPS 链接也存在潜在风险。 如果您需要更有说服力,请查看像 sslstrip 这样的工具。Spring中文文档

我没有在 HTTP 和 HTTPS 之间切换,但我的会话仍然丢失。发生了什么事?

通过交换会话 cookie 或向 URL 添加参数来维护会话(如果使用 JSTL 输出 URL 或调用 URL(例如,在重定向之前),则会自动执行此操作)。如果客户端禁用了 Cookie,并且您没有重写 URL 以包含 ,则会话将丢失。 请注意,出于安全原因,最好使用 cookie,因为它不会在 URL 中公开会话信息。jsessionidHttpServletResponse.encodeUrljsessionidSpring中文文档

我正在尝试使用并发会话控制支持,但它不允许我重新登录,即使我确定我已经注销并且没有超过允许的会话。怎么了?

确保您已将侦听器添加到文件中。 必须确保在会话被销毁时通知 Spring Security 会话注册表。 如果没有它,会话信息不会从注册表中删除。 以下示例在文件中添加侦听器:web.xmlweb.xmlSpring中文文档

<listener>
		<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

Spring Security 通过将 create-session 属性设置为 never,在某处创建了一个会话,即使我已经配置了它不这样做。怎么了?

这通常意味着用户的应用程序正在某处创建会话,但他们不知道它。 最常见的罪魁祸首是 JSP。许多人不知道 JSP 默认情况下会创建会话。 要阻止 JSP 创建会话,请将该指令添加到页面顶部。<%@ page session="false" %>Spring中文文档

如果在确定创建会话的位置时遇到困难,可以添加一些调试代码来跟踪位置。执行此操作的一种方法是将调用方法的 添加到应用程序中。javax.servlet.http.HttpSessionListenerThread.dumpStack()sessionCreatedSpring中文文档

我在执行 POST 时得到 403 禁止。怎么了?

如果为 HTTP POST 返回 HTTP 403 禁止访问错误,但该错误适用于 HTTP GET,则该问题很可能与 CSRF 有关。提供 CSRF 令牌或禁用 CSRF 保护(不建议使用后者)。Spring中文文档

我正在使用 RequestDispatcher 将请求转发到另一个 URL,但未应用我的安全约束。

默认情况下,筛选器不应用于转发或包含。 如果确实希望将安全筛选器应用于转发或包含,则必须使用元素(元素的子元素)在文件中显式配置这些筛选器。web.xml<dispatcher><filter-mapping>Spring中文文档

我已将 Spring Security 的 <global-method-security> 元素添加到我的应用程序上下文中,但是,如果我将安全注释添加到我的 Spring MVC 控制器 bean(Struts 操作等),它们似乎没有效果。为什么不呢?

在 Spring Web 应用程序中,保存调度程序 Servlet 的 Spring MVC Bean 的应用程序上下文通常与主应用程序上下文分开。 它通常在一个名为 的文件中定义,其中是分配给文件中 Spring 的名称。一个应用程序可以有多个实例,每个实例都有自己独立的应用程序上下文。 这些“子”上下文中的 Bean 对应用程序的其余部分不可见。 “父”应用程序上下文由您在文件中定义的 加载,并且对所有子上下文可见。 此父上下文通常是定义安全配置(包括元素)的位置。因此,不会强制执行应用于这些 Web Bean 中的方法的任何安全约束,因为无法从上下文中看到这些 Bean。 您需要将声明移动到 Web 上下文,或者将要保护的 Bean 移动到主应用程序上下文中。myapp-servlet.xmlmyappDispatcherServletweb.xmlDispatcherServletContextLoaderListenerweb.xml<global-method-security>DispatcherServlet<global-method-security>Spring中文文档

通常,我们建议在服务层应用方法安全性,而不是在单个 Web 控制器上应用方法安全性。Spring中文文档

我有一个用户肯定已经过身份验证,但是,当我在某些请求期间尝试访问 SecurityContextHolder 时,身份验证为 null。为什么我看不到用户信息?

为什么我看不到用户信息?Spring中文文档

如果使用与 URL 模式匹配的元素中的属性从安全筛选器链中排除了请求,则不会为该请求填充 。 检查调试日志,查看请求是否通过筛选器链。 (您正在阅读调试日志,对吧?filters='none'<intercept-url>SecurityContextHolderSpring中文文档

使用 URL 属性时,authorize JSP 标记不遵循 my 方法安全注释。为什么不呢?

当使用 中的属性时,方法安全性不会隐藏链接,因为我们不能轻易地对映射到哪个控制器端点的 URL 进行逆向工程。我们受到限制,因为控制器可以依赖标头、当前用户和其他详细信息来确定要调用的方法。url<sec:authorize>Spring中文文档

一般来说,在HTTP和HTTPS之间切换不是一个好主意,因为任何使用HTTP的应用程序都容易受到中间人攻击。 为了真正安全,用户应该开始以 HTTPS 访问您的网站并继续使用它,直到他们注销。 即使从通过 HTTP 访问的页面单击 HTTPS 链接也存在潜在风险。 如果您需要更有说服力,请查看像 sslstrip 这样的工具。Spring中文文档

Spring Security 架构问题

本节将解决常见的 Spring Security 架构问题:Spring中文文档

我如何知道 X 属于哪个包类?

查找类的最佳方法是在 IDE 中安装 Spring Security 源代码。该发行版包括项目划分到的每个模块的源代码 jar。 将这些添加到项目源路径中,然后您可以直接导航到 Spring Security 类(在 Eclipse 中)。这也使调试更容易,并允许你通过直接查看发生异常的代码来了解那里发生了什么,从而对异常进行故障排除。Ctrl-Shift-TSpring中文文档

命名空间元素如何映射到传统的 Bean 配置?

参考指南的命名空间附录中提供了有关命名空间创建哪些 Bean 的一般概述。 blog.springsource.com 上还有一篇详细的博客文章,名为“Behind the Spring Security Namespace”。如果您想知道完整的详细信息,则代码位于 Spring Security 3.0 发行版中的模块中。 您可能应该先阅读标准 Spring Framework 参考文档中有关命名空间解析的章节。spring-security-configSpring中文文档

“ROLE_”是什么意思,为什么我的角色名称需要它?

Spring Security 具有基于投票者的架构,这意味着访问决策是由一系列实例做出的。 投票者对“配置属性”执行操作,这些属性是为受保护的资源(例如方法调用)指定的。使用这种方法时,并非所有属性都可能与所有投票者相关,投票者需要知道何时应忽略某个属性(弃权),以及何时应根据属性值投票授予或拒绝访问权限。 最常见的投票者是 ,默认情况下,每当它找到带有前缀的属性时,它就会投票。 它将属性(例如)与当前用户已分配的权限名称进行简单比较。 如果它找到匹配项(他们有一个称为 的权限),它会投票授予访问权限。否则,它将投票拒绝访问。AccessDecisionVoterRoleVoterROLE_ROLE_USERROLE_USERSpring中文文档

可以通过设置 的属性来更改前缀。如果只需要在应用程序中使用角色,而不需要其他自定义投票者,则可以将前缀设置为空字符串。在这种情况下,会将所有属性视为角色。rolePrefixRoleVoterRoleVoterSpring中文文档

我如何知道要将哪些依赖项添加到我的应用程序中以使用 Spring Security?

这取决于您正在使用的功能以及您正在开发的应用程序类型。 在 Spring Security 3.0 中,项目 jar 被划分为明显不同的功能区域,因此从应用程序要求中确定需要哪些 Spring Security jar 是很简单的。 所有应用程序都需要 jar。 如果您正在开发 Web 应用程序,则需要 jar。 如果使用安全命名空间配置,则需要 jar。对于 LDAP 支持,您需要 jar。等等。spring-security-corespring-security-webspring-security-configspring-security-ldapSpring中文文档

对于第三方罐子来说,情况并不总是那么明显。 一个好的起点是从预构建的示例应用程序目录之一复制它们。 对于基本应用程序,可以从教程示例开始。 对于基本应用程序,可以从教程示例开始。 如果要将 LDAP 用于嵌入式测试服务器,请使用 LDAP 示例作为起点。 参考手册还包括一个附录,其中列出了每个 Spring Security 模块的第一级依赖项,以及有关它们是否可选以及何时需要它们的一些信息。WEB-INF/libSpring中文文档

如果您使用 Maven 构建项目,则将相应的 Spring Security 模块作为依赖项添加到您的文件中会自动拉入框架所需的核心 jar。 如果需要,必须在 Spring Security 文件中标记为“可选”的任何文件都必须添加到您自己的文件中。pom.xmlpom.xmlpom.xmlSpring中文文档

运行嵌入式 ApacheDS LDAP 服务器需要哪些依赖项?

如果使用 Maven,则需要将以下内容添加到文件依赖项中:pom.xmlSpring中文文档

<dependency>
		<groupId>org.apache.directory.server</groupId>
		<artifactId>apacheds-core</artifactId>
		<version>1.5.5</version>
		<scope>runtime</scope>
</dependency>
<dependency>
		<groupId>org.apache.directory.server</groupId>
		<artifactId>apacheds-server-jndi</artifactId>
		<version>1.5.5</version>
		<scope>runtime</scope>
</dependency>

其他必需的罐子应以传递方式拉入。Spring中文文档

什么是 UserDetailsService,我需要一个吗?

UserDetailsService是一个 DAO 接口,用于加载特定于用户帐户的数据。 除了加载该数据以供框架中的其他组件使用外,它没有其他功能。 它不负责对用户进行身份验证。 使用用户名和密码组合对用户进行身份验证通常由 ,该 注入 以使其加载用户的密码(和其他数据),以将其与提交的值进行比较。 请注意,如果使用 LDAP,此方法可能不起作用DaoAuthenticationProviderUserDetailsServiceSpring中文文档

如果要自定义身份验证过程,则应自行实现。 有关将 Spring Security 身份验证与 Google App Engine 集成的示例,请参阅此博客文章AuthenticationProviderSpring中文文档

常见操作方法问题

本节介绍有关 Spring Security 的常见操作方法问题:Spring中文文档

我需要使用更多信息登录,而不仅仅是用户名。如何添加对额外登录字段(如公司名称)的支持?

这个问题反复出现,因此您可以通过在线搜索找到更多信息。Spring中文文档

提交的登录信息由 的实例处理。您需要自定义此类以处理额外的数据字段。一种选择是使用您自己的自定义身份验证令牌类(而不是标准)。另一种选择是将多余的字段与用户名连接起来(例如,使用字符作为分隔符),并将它们传递到 的 username 属性中。UsernamePasswordAuthenticationFilterUsernamePasswordAuthenticationToken:UsernamePasswordAuthenticationTokenSpring中文文档

您还需要自定义实际的身份验证过程。 例如,如果使用自定义身份验证令牌类,则必须编写(或扩展标准)来处理它。如果已连接这些字段,则可以实现自己的字段来拆分它们并加载相应的用户数据以进行身份验证。AuthenticationProviderDaoAuthenticationProviderUserDetailsServiceSpring中文文档

如何应用不同的 intercept-url 约束,其中只有请求的 URL 的片段值不同(例如 /thing1#thing2 和 /thing1#thing3)?

您不能这样做,因为片段不会从浏览器传输到服务器。 从服务器的角度来看,URL 是相同的。 这是GWT用户的常见问题。Spring中文文档

如何在 UserDetailsService 中访问用户的 IP 地址(或其他 Web 请求数据)?

你不能(不求助于线程局部变量之类的东西),因为提供给接口的唯一信息是用户名。 而不是实现 ,您应该直接实现并从提供的令牌中提取信息。UserDetailsServiceAuthenticationProviderAuthenticationSpring中文文档

在标准 Web 设置中,对象上的方法将返回 的实例。如果需要其他信息,可以将自定义注入到正在使用的身份验证筛选器中。 如果使用命名空间(例如元素),则应删除此元素并将其替换为指向显式配置的声明。getDetails()AuthenticationWebAuthenticationDetailsAuthenticationDetailsSource<form-login><custom-filter>UsernamePasswordAuthenticationFilterSpring中文文档

如何从 UserDetailsService 访问 HttpSession?

你不能,因为没有 servlet API 的感知。如果要存储自定义用户数据,则应自定义返回的对象。 然后,可以在任何时候通过 thread-local 访问它。对返回此自定义对象的调用。UserDetailsServiceUserDetailsSecurityContextHolderSecurityContextHolder.getContext().getAuthentication().getPrincipal()Spring中文文档

如果确实需要访问会话,则必须通过自定义 Web 层来实现。Spring中文文档

如何在 UserDetailsService 中访问用户的密码?

你不能(也不应该,即使你找到了这样做的方法)。您可能误解了它的目的。 请参阅前面常见问题解答中的“什么是 UserDetailsService?”Spring中文文档

如何在应用程序中动态定义受保护的 URL?

人们经常询问如何将安全 URL 和安全元数据属性之间的映射存储在数据库中,而不是存储在应用程序上下文中。Spring中文文档

你应该问自己的第一件事是你是否真的需要这样做。 如果应用程序需要安全,则还要求根据定义的策略对安全性进行彻底测试。 在将其推出到生产环境之前,可能需要进行审核和验收测试。 一个具有安全意识的组织应该意识到,通过在运行时通过更改配置数据库中的一两行来修改安全设置,其勤奋测试过程的好处可能会立即消失。 如果您已经考虑到了这一点(可能是通过在应用程序中使用多层安全性),Spring Security 允许您完全自定义安全元数据的来源。 如果您愿意,您可以将其设置为完全动态。Spring中文文档

方法和 Web 安全性都受 的子类保护,该子类配置了一个子类,从中获取特定方法或过滤器调用的元数据。 对于 Web 安全,拦截器类是 ,它使用标记接口。它操作的“安全对象”类型是 .默认实现(在命名空间中和显式配置拦截器时使用)将 URL 模式列表及其相应的“配置属性”列表(实例)存储在内存映射中。AbstractSecurityInterceptorSecurityMetadataSourceFilterSecurityInterceptorFilterInvocationSecurityMetadataSourceFilterInvocation<http>ConfigAttributeSpring中文文档

要从替代源加载数据,您必须使用显式声明的安全过滤器链(通常是 Spring Security 的 )来定制 bean。 不能使用命名空间。 然后,您将实现为特定 .该对象包含 ,因此您可以根据返回的属性列表所包含的内容获取 URL 或任何其他相关信息,以作为决策的依据。基本大纲将类似于以下示例:FilterChainProxyFilterSecurityInterceptorFilterInvocationSecurityMetadataSourceFilterInvocationFilterInvocationHttpServletRequestSpring中文文档

	public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

		public List<ConfigAttribute> getAttributes(Object object) {
			FilterInvocation fi = (FilterInvocation) object;
				String url = fi.getRequestUrl();
				String httpMethod = fi.getRequest().getMethod();
				List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();

				// Lookup your database (or other source) using this information and populate the
				// list of attributes

				return attributes;
		}

		public Collection<ConfigAttribute> getAllConfigAttributes() {
			return null;
		}

		public boolean supports(Class<?> clazz) {
			return FilterInvocation.class.isAssignableFrom(clazz);
		}
	}
class MyFilterSecurityMetadataSource : FilterInvocationSecurityMetadataSource {
    override fun getAttributes(securedObject: Any): List<ConfigAttribute> {
        val fi = securedObject as FilterInvocation
        val url = fi.requestUrl
        val httpMethod = fi.request.method

        // Lookup your database (or other source) using this information and populate the
        // list of attributes
        return ArrayList()
    }

    override fun getAllConfigAttributes(): Collection<ConfigAttribute>? {
        return null
    }

    override fun supports(clazz: Class<*>): Boolean {
        return FilterInvocation::class.java.isAssignableFrom(clazz)
    }
}

有关详细信息,请查看 的代码。DefaultFilterInvocationSecurityMetadataSourceSpring中文文档

如何针对 LDAP 进行身份验证,但从数据库加载用户角色?

bean(在Spring Security中处理正常的LDAP身份验证)配置了两个单独的策略接口,一个用于执行身份验证,另一个用于加载用户权限,分别称为和。 从 LDAP 目录加载用户权限,并具有各种配置参数,以便指定如何检索这些参数。LdapAuthenticationProviderLdapAuthenticatorLdapAuthoritiesPopulatorDefaultLdapAuthoritiesPopulatorSpring中文文档

要改用 JDBC,您可以使用适合您的模式的任何 SQL 来自行实现接口:Spring中文文档

public class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator {
    @Autowired
    JdbcTemplate template;

    List<GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
        return template.query("select role from roles where username = ?",
                new String[] {username},
                new RowMapper<GrantedAuthority>() {
             /**
             *  We're assuming here that you're using the standard convention of using the role
             *  prefix "ROLE_" to mark attributes which are supported by Spring Security's RoleVoter.
             */
            @Override
            public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException {
                return new SimpleGrantedAuthority("ROLE_" + rs.getString(1));
            }
        });
    }
}
class MyAuthoritiesPopulator : LdapAuthoritiesPopulator {
    @Autowired
    lateinit var template: JdbcTemplate

    override fun getGrantedAuthorities(userData: DirContextOperations, username: String): MutableList<GrantedAuthority?> {
        return template.query("select role from roles where username = ?",
            arrayOf(username)
        ) { rs, _ ->
            /**
             * We're assuming here that you're using the standard convention of using the role
             * prefix "ROLE_" to mark attributes which are supported by Spring Security's RoleVoter.
             */
            SimpleGrantedAuthority("ROLE_" + rs.getString(1))
        }
    }
}

然后,将此类型的 Bean 添加到应用程序上下文中,并将其注入 .参考手册的 LDAP 一章中关于使用显式 Spring Bean 配置 LDAP 的部分对此进行了介绍。 请注意,在这种情况下,不能使用命名空间进行配置。 您还应该查阅 security-api-url[Javadoc] 以获取相关的类和接口。LdapAuthenticationProviderSpring中文文档

我想修改由命名空间创建的 Bean 的属性,但架构中没有任何东西支持它。除了放弃命名空间的使用之外,我还能做些什么?

命名空间功能是有意限制的,因此它不包括您可以使用普通 Bean 执行的所有操作。 如果您想做一些简单的事情,例如修改 Bean 或注入不同的依赖关系,您可以通过在配置中添加 a 来实现。 您可以在 Spring 参考手册中找到更多信息。为此,您需要了解创建哪些 Bean,因此您还应该阅读前面问题中提到的关于命名空间如何映射到 Spring Bean 的博客文章。BeanPostProcessorSpring中文文档

通常,您会将所需的功能添加到 的方法中。假设您要自定义 (由元素创建)使用的 (创建)。您希望提取从请求中调用的特定标头,并在对用户进行身份验证时使用它。 处理器类将如以下列表所示:postProcessBeforeInitializationBeanPostProcessorAuthenticationDetailsSourceUsernamePasswordAuthenticationFilterform-loginCUSTOM_HEADERSpring中文文档

public class CustomBeanPostProcessor implements BeanPostProcessor {

		public Object postProcessAfterInitialization(Object bean, String name) {
				if (bean instanceof UsernamePasswordAuthenticationFilter) {
						System.out.println("********* Post-processing " + name);
						((UsernamePasswordAuthenticationFilter)bean).setAuthenticationDetailsSource(
										new AuthenticationDetailsSource() {
												public Object buildDetails(Object context) {
														return ((HttpServletRequest)context).getHeader("CUSTOM_HEADER");
												}
										});
				}
				return bean;
		}

		public Object postProcessBeforeInitialization(Object bean, String name) {
				return bean;
		}
}
class CustomBeanPostProcessor : BeanPostProcessor {
    override fun postProcessAfterInitialization(bean: Any, name: String): Any {
        if (bean is UsernamePasswordAuthenticationFilter) {
            println("********* Post-processing $name")
            bean.setAuthenticationDetailsSource(
                AuthenticationDetailsSource<HttpServletRequest, Any?> { context -> context.getHeader("CUSTOM_HEADER") })
        }
        return bean
    }

    override fun postProcessBeforeInitialization(bean: Any, name: String?): Any {
        return bean
    }
}

然后,您将在应用程序上下文中注册此 Bean。 Spring 会在应用程序上下文中定义的 bean 上自动调用它。Spring中文文档