Security 命名空间(Namespace) 配置

本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。

命名空间配置从Spring框架的2.0版本开始就可用。它可以让你用额外的XML模式的元素来补充传统的Spring beans应用上下文语法。你可以在 Spring参考文档 中找到更多信息。你可以使用命名空间元素来更简洁地配置单个Bean,或者更有力地定义另一种配置语法,使其更接近问题领域,并向用户隐藏其背后的复杂性。一个简单的元素可以掩盖多个Bean和处理步骤被添加到应用环境中的事实。例如,从 security 命名空间中添加以下元素到应用程序上下文中,启动一个嵌入式LDAP服务器,以便在应用程序中测试使用。

<security:ldap-server />

这比连接同等的 Apache Directory Server bean 要简单得多。最常见的替代配置要求由 ldap-server 元素上的属性来支持,用户可以不用担心他们需要创建哪些Bean以及Bean的属性名称是什么。你可以在关于 LDAP认证 的章节中找到更多关于 ldap-server 元素的使用。在编辑 application context 文件时,一个好的XML编辑器应该提供关于可用属性和元素的信息。我们推荐你尝试使用 Spring Tool Suite,因为它具有处理标准Spring命名空间的特殊功能。

要在你的应用程序上下文中开始使用 security 命名空间,请将 spring-security-config jar 添加到你的classpath。然后,你需要做的就是在你的 application context 文件中添加 schema 声明。

<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)作为默认命名空间,这意味着我们可以省略所有 security 命名空间元素的前缀,使内容更容易阅读。如果你把你的 application context 划分为不同的文件,并把大部分 security 配置放在其中一个文件中,你可能也想这样做。你的 security application context 文件就会像这样开始。

<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 Security 是最复杂的部分。 它设置了过滤器和相关的 service bean,用于应用框架认证机制,以保证URL的安全,呈现登录和错误页面,以及更多。

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

  • AuthenticationManager 处理来自框架其他部分的认证请求。

  • AccessDecisionManager 提供web和方法安全(method security)的访问决定。注册了一个默认的,但你可以选择使用一个自定义的,用正常的Spring Bean语法声明。

  • AuthenticationProvider 实例提供了认证管理器(authentication manager)验证用户的机制。该命名空间提供了对几个标准选项的支持,并提供了添加以传统语法声明的自定义Bean的方法。

  • UserDetailsService 与认证提供者密切相关,但通常也被其他Bean所需要。

我们在下面的章节中看如何配置这些。

Security Namespace 配置入门

本节将探讨如何建立一个命名空间配置来使用该框架的一些主要功能。我们假设你最初想尽快启动和运行,并在现有的Web应用程序中添加认证支持和访问控制,并进行一些测试登录。然后我们看看如何转变为针对数据库或其他 security repository 的认证。在后面的章节中,我们将介绍更高级的命名空间配置选项。

web.xml 配置

你需要做的第一件事是在你的 web.xml 文件中添加以下filter声明。

<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框架类,它委托给一个过滤器实现,该过滤器在你的 application context 中被定义为一个Spring Bean。在本例中,该Bean被命名为 springSecurityFilterChain,这是一个由命名空间创建的内部基础设施Bean,用于处理Web安全。在这种情况下,该Bean被命名为 "springSecurityFilterChain",这是一个由命名空间创建的内部基础设施Bean,用于处理Web安全。注意,你不应该自己使用这个Bean名称。一旦你把这个Bean添加到你的 web.xml 中,你就可以开始编辑你的 application context 文件了。Web security 服务是由 <http> 元素配置的。

一个最小的 <http> 配置

要启用 web security,你需要以下配置。

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

该清单说,我们希望:

  • 我们应用程序中的所有URL都是受安全保护的,需要 ROLE_USER 角色来访问它们。

  • 使用带有 username 和 password 的表单来登录应用程序

  • 注册了一个 logout URL,这将允许我们注销应用程序

<http> 元素是所有 web 相关命名空间功能的父元素。<intercept-url> 元素定义了一个 pattern,它使用Ant path语法与传入的请求的URL进行匹配。关于如何实际进行匹配的更多细节,请参见 HttpFirewall 部分。你也可以使用正则表达式匹配作为替代(更多细节见命名空间附录)。access 属性定义了匹配给定模式的请求的访问要求。在默认配置下,这通常是一个逗号分隔的角色列表,用户必须拥有其中一个角色才能被允许进行请求。ROLE_ 前缀是一个标记,表示应该与用户的权限进行简单比较。换句话说,应该使用一个正常的基于角色的检查。Spring Security 的访问控制并不局限于使用简单的角色(因此使用前缀来区分不同类型的 security attribute)。我们在后面会看到解释是如何变化的。对 access 属性中的逗号分隔值的解释取决于所使用的 AccessDecisionManager 的实现。从Spring Security 3.0开始,你也可以用 EL 表达式 来填充该属性。

你可以使用多个 <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 进行hash。

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

<http> 元素负责创建一个 FilterChainProxy 和它所使用的 filter bean。以前常见的问题,比如 filter 排序不正确,现在已经不是问题了,因为 filter 的位置是预定义的。

<authentication-provider> 元素创建了一个 DaoAuthenticationProvider Bean,而 <user-service> 元素创建了一个 InMemoryDaoImpl。所有的认证提供者元素必须是 <authentication-manager> 元素的子元素,该元素创建了一个 ProviderManager 并在其中注册了认证提供者。你可以在 命名空间附录 中找到更多关于创建的bean的详细信息。如果你想开始了解框架中的重要类是什么,以及它们是如何被使用的,你应该检查这个附录,特别是当你以后想定制东西的时候。

前面的配置定义了两个用户,他们的密码,以及他们在应用程序中的角色(用于访问控制)。你也可以通过设置 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 功能

本节涵盖了超越基础的各种功能。

添加你自己的 Filter

如果你以前使用过 Spring Security,你就知道该框架维护着一个过滤器链,它用来应用其服务。你可能想在特定位置添加你自己的过滤器到栈中,或者使用目前没有命名空间配置选项的Spring Security过滤器(例如CAS)。另外,你可能想使用标准命名空间过滤器的定制版本,比如 UsernamePasswordAuthenticationFilter(由 <form-login> 元素创建),以利用一些额外的配置选项,当你明确使用Bean时,这些选项是可用的。由于过滤器链没有直接暴露出来,你怎么能用命名空间配置做到这一点?

当你使用命名空间时,过滤器的顺序总是被严格执行的。当应用上下文被创建时,过滤器bean被命名空间处理代码排序,标准的Spring Security过滤器在命名空间中都有一个别名和一个众所周知的位置。

在以前的版本中,排序是在过滤器实例被创建之后,在应用上下文的后处理过程中进行的。在3.0+版本中,排序现在是在Bean元数据级别进行的,在类被实例化之前。这对你如何将自己的过滤器添加到栈中有影响,因为整个过滤器列表必须在 <http> 元素的解析过程中被知道,所以语法在3.0中略有改变。

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

Table 1. Standard Filter Aliases and Ordering
别名 Filter 类 命名空间元素/属性

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

如果你想让你的过滤器在堆栈中的另一个过滤器之前或之后插入,你也可以使用 afterbefore 属性。你可以使用 FIRSTLASTposition 属性,分别表示你希望你的过滤器出现在整个栈的前面或后面。

Avoiding filter position conflicts

如果你插入的自定义过滤器可能与命名空间创建的一个标准过滤器占据相同的位置,你不应该错误地包括命名空间的版本。移除任何创建过滤器的元素,这些过滤器的功能是你想要替换的。

注意,你不能替换通过使用 <http> 元素本身创建的过滤器: SecurityContextPersistenceFilterExceptionTranslationFilter,或 FilterSecurityInterceptor。默认情况下, AnonymousAuthenticationFilter 被添加,除非你禁用了 会话固定保护,否则 SessionManagementFilter 也被添加到过滤器链中。

如果你替换了一个需要认证入口点的命名空间过滤器(也就是说,认证过程是由未认证的用户试图访问安全资源而触发的),你也需要添加一个自定义的入口点bean。

方法安全(Method Security)

从2.0版本开始,Spring Security对服务层方法的安全添加提供了大量支持。它提供了对JSR-250注解安全的支持,以及框架原有的 @Secured 注解。从3.0版本开始,你还可以利用 基于表达式的注解。你可以将安全性应用于单个Bean(通过使用 intercept-methods 元素来装饰Bean声明),或者使用AspectJ风格的 pointcuts 在整个服务层中保护多个Bean。

默认的 AccessDecisionManager

本节假设你对 Spring Security 的访问控制的底层架构有一定的了解。如果你没有,你可以跳过这一节,以后再来讨论,因为这一节只与需要做一些定制的人有关,以使用超过简单的基于角色的安全。

当你使用命名空间配置时,AccessDecisionManager 的默认实例会自动为你注册,并根据你在 intercept-urlprotect-pointcut 声明中指定的访问属性(如果你使用注解来保护方法,则在注解中),为方法调用和Web URL访问做出访问决定。

默认策略是使用一个 AffirmativeBased AccessDecisionManager 和一个 RoleVoter 以及一个 AuthenticatedVoter。你可以在关于 授权 的章节中找到更多关于这些的信息。

自定义 AccessDecisionManager

如果你需要使用更复杂的访问控制策略,你可以为方法和 web security 设置一个替代品。

对于方法安全,你可以通过将 global-method-security 上的 access-decision-manager-ref 属性设置为 application context 中适当的 AccessDecisionManager Bean 的 id 来实现。

<global-method-security access-decision-manager-ref="myAccessDecisionManagerBean">
...
</global-method-security>

web security 的语法是相同的,但属性是在 http 元素上。

<http access-decision-manager-ref="myAccessDecisionManagerBean">
...
</http>