Security Namespace Configuration

自 Spring Framework 的 2.0 版本以来,命名空间配置已经可用。它允许你使用来自其他 XML 模式的元素来补充传统 Spring Bean 应用程序上下文语法。你可以在 Spring Reference Documentation 中找到更多信息。你可以使用命名空间元素更简洁地配置单个 Bean,或者更强大地定义更紧密匹配问题域并向用户隐藏底层复杂性的替代配置语法。一个简单的元素可以隐藏这样的事实:多个 bean 和处理步骤正在添加到应用程序上下文中。例如,将 security 命名空间中的以下元素添加到应用程序上下文中,将启动一个嵌入式 LDAP 服务器,用于在应用程序中进行测试:

<security:ldap-server />

这比连接等效的 Apache Directory Server Bean 简单得多。最常见的替代配置要求由 ldap-server 元素上的属性支持,并且用户无需担心需要创建哪些 Bean 以及 Bean 属性名称是什么。可以在 LDAP Authentication 章节中找到有关使用 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(而不是 bean)用作默认命名空间,这意味着我们可以省略所有安全命名空间元素的前缀,从而使内容更易于阅读。如果您已将应用程序上下文分成不同的文件,并且将大部分安全配置都放在其中一个文件中,则您可能还想这样做。然后,您的安全应用程序上下文文件将由此开始:

<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>

我们假设从此章节开始将使用此语法。

Design of the Namespace

命名空间旨在捕获框架中最常见的用法,并提供一个简化且简洁的语法,以便在应用程序中启用它们。设计基于框架内的更大规模依赖关系,可分为以下领域:

  • _Web/HTTP Security_是最复杂的部分。它设置用于应用框架身份验证机制、保护 URL、呈现登录和错误页面等更多内容的过滤器和相关服务 Bean。

  • _Business Object (Method) Security_定义了保护服务层的选项。

  • _AuthenticationManager_处理来自框架其他部分的身份验证请求。

  • _AccessDecisionManager_提供了 Web 和方法安全性的访问决策。已注册一个默认值,但您可以选择使用使用普通 Spring bean 语法声明的自定义值。

  • _AuthenticationProvider_实例提供了身份验证管理器用来对用户进行身份验证的机制。命名空间支持几种标准选项,并提供添加使用传统语法声明的自定义 Bean 的方式。

  • _UserDetailsService_与身份验证提供程序密切相关,但经常也需要其他 Bean。

我们将在以下部分中了解如何配置这些领域。

Getting Started with Security Namespace Configuration

本节介绍如何构建命名空间配置,以使用框架的一些主要特性。我们假设您最初希望尽快启动并运行,并将身份验证支持和访问控制添加到现有 Web 应用程序,并进行一些测试登录。然后,我们来看看如何切换到对数据库或其他安全存储库进行身份验证。在后面的部分中,我们将介绍更高级的命名空间配置选项。

web.xml Configuration

您需要做的第一件事是将以下过滤器声明添加到您的 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> 元素配置。

A Minimal <http> Configuration

要启用 Web 安全,您需要以下配置:

<http>
<intercept-url pattern="/**" access="hasRole('USER')" />
<form-login />
<logout />
</http>

该列表说明我们希望:

  • 需要我们应用程序中的所有 URL 被保护,要求角色 `ROLE_USER`来访问它们

  • 使用具有用户名和密码的表单登录应用程序

  • 注册了允许我们注销应用程序的注销 URL

<http> 元素是所有与 Web 相关的命名空间功能的父类。<intercept-url> 元素定义了一个 pattern,它使用 Ant 路径语法与传入请求的 URL 匹配。有关实际执行匹配的更多详细信息,请参阅有关 HttpFirewall 的部分。您还可以使用正则表达式匹配作为替代方案(有关更多详细信息,请参阅命名空间附录)。access 属性定义与给定模式匹配的请求的访问要求。在默认配置中,这通常是逗号分隔的角色列表,用户必须具有其中一个角色才能获得发出请求的权限。ROLE_ 前缀是一个标记,指示应该与用户的权限进行简单比较。换句话说,应该使用基于正常角色的检查。Spring Security 中的访问控制不仅仅限于使用简单角色(因此使用前缀来区分不同类型的安全属性)。我们稍后会看到解释如何变化。access 属性中逗号分隔值的解释取决于使用 <<`AccessDecisionManager`,ns-access-manager>> 的实现。自 Spring Security 3.0 以来,您还可以填充 EL expression 的属性。

您可以使用多个 <intercept-url> 元素为不同的 URL 组定义不同的访问要求,但它们将按所列顺序进行评估,并使用第一个匹配项。因此,您必须将最具体的匹配项放在顶部。您还可以添加一个 method 属性,以将匹配限制为特定的 HTTP 方法(GETPOSTPUT 等)。

要添加用户,您可以在命名空间中直接定义一组测试数据:

<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>

元素负责创建 FilterChainProxy 及其使用的过滤器 bean。先前的常见问题,例如过滤器排序不正确,现在不再是问题,因为过滤器位置是预定义的。 元素创建一个 DaoAuthenticationProvider Bean, 元素创建一个 InMemoryDaoImpl。所有 authentication-provider 元素都必须是 元素的子元素,该元素创建一个 ProviderManager 并向其中注册身份验证提供程序。您可以在 xref:servlet/appendix/namespace/index.adoc#appendix-namespace[namespace appendix] 中找到有关所创建 Bean 的更详细信息。如果您想开始了解框架中的重要类以及如何使用它们,尤其是在您稍后想要定制内容时,应仔细检查此附录。

前面的配置定义了两个用户及其密码和在应用程序中的角色(用于访问控制)。您还可以通过设置 user-service 元素上的 properties 属性从标准属性文件加载用户信息。有关文件格式的更多详细信息,请参阅有关 in-memory authentication 的部分。使用 <authentication-provider> 元素意味着用户信息将被身份验证管理器用于处理身份验证请求。您可以使用多个 <authentication-provider> 元素来定义不同的身份验证来源。每次咨询一个。

在此时,您应该能够启动您的应用程序,并且您应该需要进行登录才能继续。试一试,或者尝试使用该项目附带的 “tutorial” 示例应用程序进行实验。

Setting a Default Post-Login Destination

如果尝试访问受保护资源未提示使用表单登录,那么 “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” 实例。

Advanced Web Features

本节将介绍超出基本内容的各种功能。

Adding in Your Own Filters

如果您之前用过 Spring Security,您就会知道该框架维护着它用来应用其服务的过滤器链。您可能希望在特定位置向堆栈中添加您自己的过滤器,或使用当前没有名称空间配置选项的 Spring Security 过滤器(例如 CAS)。或者,您可能希望使用已自定义的标准名称空间过滤器版本,比如通过 <form-login> 元素创建的 “UsernamePasswordAuthenticationFilter” ,以利用显式使用该 bean 时可用的某些额外配置选项。因为过滤器链并没有直接公开,您如何使用名称空间配置来完成这项操作?

当您使用名称空间时,始终严格执行过滤器的顺序。在创建应用程序上下文时,过滤器 bean 由名称空间处理代码进行排序,并且标准 Spring Security 过滤器在名称空间中都有别名和众所周知的位置。

在之前的版本中,排序在创建了过滤器实例之后进行,在对应用程序上下文进行后处理期间。在 3.0+ 版中,排序现在在 bean 元数据级别进行,在实例化类之前。这对您如何向堆栈中添加自己的过滤器产生影响,因为在解析 <http> 元素期间必须知道完整的过滤器列表,所以语法在 3.0 中略有更改。

过滤器、别名和创建过滤器的名称空间元素和属性显示在以下表格中,按它们在过滤器链中出现的顺序:

Table 1. Standard Filter Aliases and Ordering
Alias Filter Class Namespace Element or Attribute

DISABLE_ENCODE_URL_FILTER

DisableEncodeUrlFilter

http@disable-url-rewriting

FORCE_EAGER_SESSION_FILTER

ForceEagerSessionCreationFilter

http@create-session="ALWAYS"

CHANNEL_FILTER

ChannelProcessingFilter

http/intercept-url@requires-channel

SECURITY_CONTEXT_FILTER

SecurityContextPersistenceFilter

http

CONCURRENT_SESSION_FILTER

ConcurrentSessionFilter

session-management/concurrency-control

HEADERS_FILTER

HeaderWriterFilter

http/headers

CSRF_FILTER

CsrfFilter

http/csrf

LOGOUT_FILTER

LogoutFilter

http/logout

X509_FILTER

X509AuthenticationFilter

http/x509

PRE_AUTH_FILTER

AbstractPreAuthenticatedProcessingFilter Subclasses

N/A

CAS_FILTER

CasAuthenticationFilter

N/A

FORM_LOGIN_FILTER

UsernamePasswordAuthenticationFilter

http/form-login

BASIC_AUTH_FILTER

BasicAuthenticationFilter

http/http-basic

SERVLET_API_SUPPORT_FILTER

SecurityContextHolderAwareRequestFilter

http/@servlet-api-provision

JAAS_API_SUPPORT_FILTER

JaasApiIntegrationFilter

http/@jaas-api-provision

REMEMBER_ME_FILTER

RememberMeAuthenticationFilter

http/remember-me

ANONYMOUS_FILTER

AnonymousAuthenticationFilter

http/anonymous

SESSION_MANAGEMENT_FILTER

SessionManagementFilter

session-management

EXCEPTION_TRANSLATION_FILTER

ExceptionTranslationFilter

http

FILTER_SECURITY_INTERCEPTOR

FilterSecurityInterceptor

http

SWITCH_USER_FILTER

SwitchUserFilter

N/A

您可以使用 “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” 属性一起使用,以指示希望您的过滤器分别位于整个堆栈之前或之后。

Avoiding filter position conflicts

如果您插入了可能占据与该名称空间创建的某个标准过滤器相同位置的自定义过滤器,您就不应该错误地包括名称空间版本。删除创建了您希望替换其功能的过滤器的任何元素。 请注意,您无法替换由使用 <http> 元素本身创建的筛选器:SecurityContextPersistenceFilterExceptionTranslationFilterFilterSecurityInterceptor。默认情况下,添加了一个 AnonymousAuthenticationFilter,除非您禁用了 session-fixation protection,否则还向筛选器链添加了一个 SessionManagementFilter

如果您替换了需要某个身份验证入口点(即,身份验证过程是由未经身份验证的用户试图访问安全资源触发的)的名称空间过滤器,您还需要添加一个自定义入口点 bean。

Method Security

自 2.0 版本以来,Spring Security 为向服务层方法中添加安全性提供了大量的支持。它提供了对 JSR-250 注释安全的支持,以及该框架的原始 @Secured 注释。自 3.0 版本以来,您还可以使用 expression-based annotations。您可以通过使用 intercept-methods 元素装饰 Bean 声明将安全性应用到单个 Bean,或者可以使用 AspectJ 样式切入点保护整个服务层中的多个 Bean。

The Default AccessDecisionManager

本节假定您对 Spring Security 内访问控制中底层架构具有一定了解。如果没有,您可以跳过它并稍后再回头阅读,因为本节仅与需要进行一些自定义以使用不仅仅是基于角色的安全的人员相关。

当您使用名称空间配置时,会自动为您注册 “AccessDecisionManager” 的默认实例,并且使用它根据您在 “intercept-url” 和 “protect-pointcut” 声明中指定的访问属性(以及如果您使用注释保护方法,在注释中)对方法调用和 Web URL 访问做出访问决策。

默认策略是使用带有 RoleVoterAuthenticatedVoterAffirmativeBased AccessDecisionManager。您可以在 authorization 一章中找到有关它们的更多信息。

Customizing the 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>