安全命名空间配置
命名空间配置自 Spring Framework 2.0 版本起可用。它允许您使用其他 XML 架构中的元素来补充传统的 Spring bean 应用程序上下文语法。您可以在 Spring 参考文档 中找到更多信息。您可以使用命名空间元素更简洁地配置单个 bean,或者更强大地定义更贴近问题域的替代配置语法,并向用户隐藏底层复杂性。一个简单的元素可以隐藏正在向应用程序上下文添加多个 bean 和处理步骤这一事实。例如,将以下元素从 security
命名空间添加到应用程序上下文中,将启动一个嵌入式 LDAP 服务器,以便在应用程序中用于测试
<security:ldap-server />
这比连接等效的 Apache Directory Server bean 简单得多。ldap-server
元素上的属性支持最常见的替代配置需求,并且用户不必担心需要创建哪些 bean 以及 bean 属性名称是什么。您可以在 LDAP 身份验证 章节中了解有关 ldap-server
元素的更多信息。编辑应用程序上下文文件时,一个好的 XML 编辑器应提供有关可用属性和元素的信息。我们建议您尝试 Spring Tool Suite,因为它具有用于处理标准 Spring 命名空间的特殊功能。
要开始在应用程序上下文中使用 security
命名空间,请将 spring-security-config
jar 添加到类路径。然后,只需将架构声明添加到应用程序上下文文件即可
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd">
...
</beans>
在许多示例中,您会看到(以及在示例应用程序中),我们经常使用 security
(而不是 beans
)作为默认命名空间,这意味着我们可以省略所有安全命名空间元素上的前缀,从而使内容更易于阅读。如果您将应用程序上下文分成单独的文件,并且将大部分安全配置放在其中一个文件中,您可能还想这样做。您的安全应用程序上下文文件将以这种方式开始
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd">
...
</beans:beans>
我们假设从现在开始在本章中使用此语法。
命名空间设计
命名空间旨在捕获框架中最常见的用法,并提供简化且简洁的语法,以便在应用程序中启用它们。该设计基于框架内的大规模依赖关系,可以分为以下几个方面
-
Web/HTTP 安全是最复杂的部分。它设置过滤器和相关服务 bean,用于应用框架身份验证机制,保护 URL,呈现登录和错误页面,等等。
-
业务对象(方法)安全定义了保护服务层选项。
-
AuthenticationManager 处理来自框架其他部分的身份验证请求。
-
AccessDecisionManager 为 Web 和方法安全提供访问决策。已注册一个默认值,但您可以选择使用自定义值,并使用常规 Spring bean 语法声明。
-
AuthenticationProvider 实例提供身份验证管理器用来对用户进行身份验证的机制。该命名空间支持多种标准选项,并提供添加使用传统语法声明的自定义 bean 的方法。
-
UserDetailsService 与身份验证提供程序密切相关,但其他 bean 通常也需要它。
我们将在以下部分中了解如何配置这些内容。
开始使用安全命名空间配置
本节介绍如何构建命名空间配置以使用框架的一些主要功能。我们假设您最初希望尽快启动并运行,并为现有 Web 应用程序添加身份验证支持和访问控制,并进行一些测试登录。然后,我们了解如何切换到针对数据库或其他安全存储库进行身份验证。在后面的章节中,我们将介绍更高级的命名空间配置选项。
web.xml 配置
您需要做的第一件事是将以下过滤器声明添加到您的 web.xml
文件
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
DelegatingFilterProxy
是一个 Spring Framework 类,它委托给在应用程序上下文中定义为 Spring bean 的过滤器实现。在此情况下,bean 被命名为 springSecurityFilterChain
,它是由命名空间创建的用于处理 Web 安全的内部基础设施 bean。在此情况下,bean 被命名为“springSecurityFilterChain”,它是由命名空间创建的用于处理 Web 安全的内部基础设施 bean。请注意,您不应自己使用此 bean 名称。将此 bean 添加到 web.xml
后,您就可以开始编辑应用程序上下文文件了。Web 安全服务由 <http>
元素配置。
最小 <http> 配置
要启用 Web 安全,您需要以下配置
<http>
<intercept-url pattern="/**" access="hasRole('USER')" />
<form-login />
<logout />
</http>
该清单表示我们希望
-
应用程序中的所有 URL 都受到保护,需要角色
ROLE_USER
才能访问它们 -
使用带有用户名和密码的表单登录应用程序
-
注册一个注销 URL,它将允许我们注销应用程序
<http>
元素是所有与 Web 相关的命名空间功能的父级。<intercept-url>
元素定义了一个 pattern
,该 pattern
使用 Ant 路径语法与传入请求的 URL 进行匹配。有关如何实际执行匹配的更多详细信息,请参阅有关 HttpFirewall
的部分。您还可以使用正则表达式匹配作为替代(有关更多详细信息,请参阅命名空间附录)。access
属性定义与给定模式匹配的请求的访问要求。在默认配置中,这通常是一个角色的逗号分隔列表,用户必须拥有其中一个角色才能被允许发出请求。ROLE_
前缀是一个标记,表示应与用户的权限进行简单比较。换句话说,应使用基于正常角色的检查。Spring Security 中的访问控制不仅限于使用简单角色(因此使用前缀来区分不同类型的安全属性)。我们稍后会看到解释如何变化。access
属性中逗号分隔值的解释取决于所使用的 AccessDecisionManager
的实现。自 Spring Security 3.0 以来,您还可以使用 EL 表达式 填充该属性。
您可以使用多个 |
要添加用户,您可以在命名空间中直接定义一组测试数据
<authentication-manager>
<authentication-provider>
<user-service>
<!-- Password is prefixed with {noop} to indicate to DelegatingPasswordEncoder that
NoOpPasswordEncoder should be used. This is not safe for production, but makes reading
in samples easier. Normally passwords should be hashed using BCrypt -->
<user name="jimi" password="{noop}jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="{noop}bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
前一个清单显示了存储相同密码的安全方法的示例。密码以 {bcrypt}
为前缀,以指示 DelegatingPasswordEncoder
,它支持任何配置的 PasswordEncoder
进行匹配,密码使用 BCrypt 进行哈希
<authentication-manager>
<authentication-provider>
<user-service>
<user name="jimi" password="{bcrypt}$2a$10$ddEWZUl8aU0GdZPPpy7wbu82dvEw/pBpbRvDQRqA41y6mK1CoH00m"
authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="{bcrypt}$2a$10$/elFpMBnAYYig6KRR5bvOOYeZr1ie1hSogJryg9qDlhza4oCw1Qka"
authorities="ROLE_USER" />
<user name="jimi" password="{noop}jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="{noop}bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
前面的配置定义了两个用户、他们的密码以及他们在应用程序中的角色(用于访问控制)。您还可以通过在 user-service
元素上设置 properties
属性从标准属性文件加载用户信息。有关文件格式的更多详细信息,请参阅 内存认证 部分。使用 <authentication-provider>
元素意味着用户信息由认证管理器用于处理认证请求。您可以有多个 <authentication-provider>
元素来定义不同的认证源。每个元素都会依次咨询。
此时,您应该能够启动应用程序,并且需要登录才能继续。试一试,或尝试使用项目附带的“教程”示例应用程序。
设置默认登录后目标
如果表单登录不是由尝试访问受保护资源提示的,则 default-target-url
选项会发挥作用。这是用户成功登录后被带到的 URL。它默认为 /
。您还可以配置一些内容,以便用户始终最终进入此页面(无论登录是“按需”还是他们明确选择登录),方法是将 always-use-default-target
属性设置为 true
。如果您的应用程序始终要求用户从“主页”开始,这将非常有用,例如
<http pattern="/login.htm*" security="none"/>
<http use-expressions="false">
<intercept-url pattern='/**' access='ROLE_USER' />
<form-login login-page='/login.htm' default-target-url='/home.htm'
always-use-default-target='true' />
</http>
为了对目标进行更多控制,您可以使用 authentication-success-handler-ref
属性作为 default-target-url
的替代。引用的 Bean 应为 AuthenticationSuccessHandler
的实例。
高级 Web 功能
本部分涵盖了超越基本知识的各种功能。
添加自己的过滤器
如果您以前使用过 Spring Security,您就会知道该框架维护了一个过滤器链,用于应用其服务。您可能希望在特定位置将自己的过滤器添加到堆栈中,或使用 Spring Security 过滤器,而目前没有名称空间配置选项(例如 CAS)。或者,您可能希望使用标准名称空间过滤器的自定义版本,例如 UsernamePasswordAuthenticationFilter
(由 <form-login>
元素创建),以利用显式使用 Bean 时可用的某些额外配置选项。由于过滤器链没有直接公开,您如何使用名称空间配置来执行此操作?
当您使用名称空间时,过滤器顺序始终严格执行。在创建应用程序上下文时,过滤器 Bean 由名称空间处理代码排序,每个标准 Spring Security 过滤器在名称空间中都有一个别名和一个众所周知的位置。
在之前的版本中,排序在创建过滤器实例后,在应用程序上下文的后期处理期间进行。在 3.0+ 版本中,排序现在在类实例化之前,在 bean 元数据级别完成。这会影响你将自己的过滤器添加到堆栈的方式,因为在解析 |
创建过滤器的过滤器、别名和命名空间元素和属性按其在过滤器链中出现的顺序显示在以下表格中
别名 | 过滤器类 | 命名空间元素或属性 |
---|---|---|
DISABLE_ENCODE_URL_FILTER |
|
|
FORCE_EAGER_SESSION_FILTER |
|
|
CHANNEL_FILTER |
|
|
SECURITY_CONTEXT_FILTER |
|
|
CONCURRENT_SESSION_FILTER |
|
|
HEADERS_FILTER |
|
|
CSRF_FILTER |
|
|
LOGOUT_FILTER |
|
|
X509_FILTER |
|
|
PRE_AUTH_FILTER |
|
无 |
CAS_FILTER |
|
无 |
FORM_LOGIN_FILTER |
|
|
BASIC_AUTH_FILTER |
|
|
SERVLET_API_SUPPORT_FILTER |
|
|
JAAS_API_SUPPORT_FILTER |
|
|
REMEMBER_ME_FILTER |
|
|
ANONYMOUS_FILTER |
|
|
SESSION_MANAGEMENT_FILTER |
|
|
EXCEPTION_TRANSLATION_FILTER |
|
|
FILTER_SECURITY_INTERCEPTOR |
|
|
SWITCH_USER_FILTER |
|
无 |
你可以使用 custom-filter
元素和这些名称之一将自己的过滤器添加到堆栈,以指定你的过滤器应出现的的位置
<http>
<custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
</http>
<beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter"/>
如果您希望过滤器在堆栈中的其他过滤器之前或之后插入,您还可以使用 after
或 before
属性。您可以将 FIRST
和 LAST
与 position
属性一起使用,以指示您希望您的过滤器分别出现在整个堆栈之前或之后。
避免过滤器位置冲突
如果您插入可能占据与命名空间创建的标准过滤器相同位置的自定义过滤器,则不应错误地包含命名空间版本。删除创建您希望替换其功能的过滤器的任何元素。 请注意,您无法替换由 |
如果您替换需要身份验证入口点(即,未经身份验证的用户尝试访问受保护资源时触发身份验证过程)的命名空间过滤器,则还需要添加一个自定义入口点 Bean。
方法安全性
从 2.0 版开始,Spring Security 大力支持为您的服务层方法添加安全性。它支持 JSR-250 注释安全性以及框架的原始 @Secured
注释。从 3.0 版开始,您还可以利用 基于表达式的注释。您可以对单个 Bean 应用安全性(通过使用 intercept-methods
元素装饰 Bean 声明),或者可以使用 AspectJ 样式切入点保护整个服务层中的多个 Bean。
默认 AccessDecisionManager
本节假定您对 Spring Security 中访问控制的基本架构有所了解。如果您不了解,您可以跳过它,稍后再回来,因为本节仅与需要进行一些自定义以使用更多基于角色的安全性的人员相关。
当您使用命名空间配置时,将自动为您注册 AccessDecisionManager
的默认实例,并根据您在 intercept-url
和 protect-pointcut
声明中指定的访问属性(以及在注释中,如果您使用注释来保护方法)对方法调用和 Web URL 访问进行访问决策。
默认策略是使用带有 RoleVoter
和 AuthenticatedVoter
的 AffirmativeBased
AccessDecisionManager
。您可以在 授权章节中找到更多关于这些内容的信息。
自定义 AccessDecisionManager
如果您需要使用更复杂的访问控制策略,则可以为方法和 Web 安全设置一个替代方案。
对于方法安全性,您可以通过将 global-method-security
上的 access-decision-manager-ref
属性设置为应用程序上下文中相应 AccessDecisionManager
bean 的 id
来实现
<global-method-security access-decision-manager-ref="myAccessDecisionManagerBean">
...
</global-method-security>
Web 安全性的语法相同,但该属性位于 http
元素上
<http access-decision-manager-ref="myAccessDecisionManagerBean">
...
</http>