14. 核心安全过滤器

有一些关键过滤器将始终在使用 Spring Security 的 Web 应用程序中使用,因此我们将首先研究这些过滤器及其支持的类和接口。我们不会涵盖所有功能,因此,如果您想获得完整的图片,请务必查看 Javadoc。

14.1 FilterSecurityInterceptor

在讨论一般的访问控制时,我们已经简短地看到过FilterSecurityInterceptor,并且已经将其与<intercept-url>元素组合以在内部进行配置的名称空间一起使用。现在,我们将看到如何显式配置它与FilterChainProxy及其配套过滤器ExceptionTranslationFilter一起使用。一个典型的配置示例如下所示:

<bean id="filterSecurityInterceptor"
	class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource">
	<security:filter-security-metadata-source>
	<security:intercept-url pattern="/secure/super/**" access="ROLE_WE_DONT_HAVE"/>
	<security:intercept-url pattern="/secure/**" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
	</security:filter-security-metadata-source>
</property>
</bean>

FilterSecurityInterceptor负责处理 HTTP 资源的安全性。它需要引用AuthenticationManagerAccessDecisionManager。它还提供了适用于不同 HTTP URL 请求的配置属性。请参考技术简介中的这些的原始讨论

可以通过两种方式使用配置属性来配置FilterSecurityInterceptor。如上所示,第一个使用的是<filter-security-metadata-source>名称空间元素。这类似于命名空间章节中的<http>元素,但<intercept-url>子元素仅使用patternaccess属性。逗号用于界定适用于每个 HTTP URL 的不同配置属性。第二种选择是编写自己的SecurityMetadataSource,但这超出了本文档的范围。与使用的方法无关,SecurityMetadataSource负责返回List<ConfigAttribute>,该List<ConfigAttribute>包含与单个安全 HTTP URL 相关联的所有配置属性。

应该注意的是FilterSecurityInterceptor.setSecurityMetadataSource()方法实际上期望一个FilterInvocationSecurityMetadataSource的实例。这是SecurityMetadataSource子类的标记接口。它仅表示SecurityMetadataSource理解FilterInvocation。为了简单起见,我们将 continue 将FilterInvocationSecurityMetadataSource称为SecurityMetadataSource,因为这种区别与大多数用户无关。

由名称空间语法创建的SecurityMetadataSource通过将请求 URL 与已配置的pattern属性进行匹配来获取特定FilterInvocation的配置属性。这与命名空间配置的行为相同。默认是将所有表达式视为 Apache Ant 路径,对于更复杂的情况也支持正则表达式。 request-matcher属性用于指定要使用的模式的类型。无法在同一定义中混合使用表达式语法。例如,使用正则表达式而不是 Ant 路径的先前配置将编写如下:

<bean id="filterInvocationInterceptor"
	class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="runAsManager" ref="runAsManager"/>
<property name="securityMetadataSource">
	<security:filter-security-metadata-source request-matcher="regex">
	<security:intercept-url pattern="\A/secure/super/.*\Z" access="ROLE_WE_DONT_HAVE"/>
	<security:intercept-url pattern="\A/secure/.*\" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
	</security:filter-security-metadata-source>
</property>
</bean>

模式始终按照定义的 Sequences 进行评估。因此,重要的是,在列表中将比不那么具体的模式定义更具体的模式更高。这在上面的示例中得到了反映,在此示例中,更具体的/secure/super/模式看上去比不那么具体的/secure/模式更高。如果它们相反,则/secure/模式将始终匹配,并且将永远不会评估/secure/super/模式。

14.2 ExceptionTranslationFilter

ExceptionTranslationFilter位于安全过滤器堆栈中的FilterSecurityInterceptor上方。它本身并没有执行任何实际的安全性强制措施,而是处理安全性拦截器引发的异常并提供适当的 HTTP 响应。

<bean id="exceptionTranslationFilter"
class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
<property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</bean>

<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
</bean>

<bean id="accessDeniedHandler"
	class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.htm"/>
</bean>

14.2.1 AuthenticationEntryPoint

如果用户请求安全的 HTTP 资源但未通过身份验证,则将调用AuthenticationEntryPoint。安全拦截器将在调用堆栈的更下方抛出适当的AuthenticationExceptionAccessDeniedException,从而触发入口点上的commence方法。这样做的工作是向用户呈现适当的响应,以便可以开始身份验证。我们在这里使用的是LoginUrlAuthenticationEntryPoint,它将请求重定向到另一个 URL(通常是登录页面)。实际使用的实现将取决于您要在应用程序中使用的身份验证机制。

14.2.2 AccessDeniedHandler

如果用户已经通过身份验证并尝试访问受保护的资源,会发生什么情况?在正常使用情况下,不应发生这种情况,因为应用程序工作流应限于用户有权访问的操作。例如,没有 Management 员角色的用户可能会隐藏指向 Management 页面的 HTML 链接。但是,您不能依靠隐藏链接来确保安全性,因为用户总是有可能直接 Importing URL 来尝试绕过限制。或者,他们可能会修改 RESTful URL 来更改某些参数值。您的应用程序必须受到保护以防出现这些情况,否则肯定是不安全的。通常,您将使用简单的 Web 层安全性将约束应用于基本 URL,并在服务层接口上使用基于方法的更特定的安全性来 true 确定允许的内容。

如果抛出AccessDeniedException并且用户已通过身份验证,则意味着已尝试操作该用户没有足够权限的操作。在这种情况下,ExceptionTranslationFilter将调用第二种策略AccessDeniedHandler。默认情况下,使用AccessDeniedHandlerImpl,它仅向 Client 端发送 403(禁止访问)响应。或者,您可以显式配置实例(如上例所示),并设置错误页面 URL,它将请求转发至[11]。这可以是简单的“访问被拒绝”页面,例如 JSP,也可以是更复杂的处理程序,例如 MVC 控制器。当然,您可以自己实现接口并使用自己的实现。

使用命名空间配置应用程序时,也可以提供自定义的AccessDeniedHandler。有关更多详细信息,请参见命名空间附录

14.2.3 SavedRequest 和 RequestCache 接口

ExceptionTranslationFilter职责的另一个职责是在调用AuthenticationEntryPoint之前保存当前请求。这允许在用户认证之后恢复请求(请参见web authentication的先前概述)。一个典型的示例是用户使用表单登录,然后通过默认的SavedRequestAwareAuthenticationSuccessHandler(请参阅below)重定向到原始 URL。

RequestCache封装了存储和检索HttpServletRequest实例所需的功能。默认情况下使用HttpSessionRequestCache,它将请求存储在HttpSession中。 RequestCacheFilter的工作是当用户重定向到原始 URL 时从缓存中实际恢复已保存的请求。

通常情况下,您不需要修改任何此功能,但是保存请求处理是一种“尽力而为”的方法,并且在某些情况下默认配置无法处理。这些接口的使用使其可以从 Spring Security 3.0 开始完全插入。

14.3 SecurityContextPersistenceFilter

我们已在Technical Overview一章中介绍了此至关重要的过滤器的用途,因此您现在可能希望重新阅读该部分。首先让我们看一下如何配置它与FilterChainProxy结合使用。基本配置仅需要 bean 本身

<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>

正如我们之前看到的,此过滤器有两个主要任务。它负责在 HTTP 请求之间存储SecurityContext内容,并在请求完成时清除SecurityContextHolder。清除存储上下文的ThreadLocal是必不可少的,因为否则可能会将线程替换为 servlet 容器的线程池,同时仍附加特定用户的安全上下文。然后可能在以后的阶段使用此线程,并以错误的凭据执行操作。

14.3.1 SecurityContextRepository

从 Spring Security 3.0 开始,现在将安全性上下文的加载和存储工作委托给一个单独的策略接口:

public interface SecurityContextRepository {

SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);

void saveContext(SecurityContext context, HttpServletRequest request,
		HttpServletResponse response);
}

HttpRequestResponseHolder只是传入请求和响应对象的容器,允许实现将其替换为包装器类。返回的内容将传递到过滤器链。

默认实现是HttpSessionSecurityContextRepository,它将安全性上下文存储为HttpSession属性[12]。此实现最重要的配置参数是allowSessionCreation属性,该属性默认为true,从而允许该类在需要一个会话来存储经过身份验证的用户的安全上下文时创建会话(除非进行了身份验证,否则它不会创建一个会话)位置以及安全上下文的内容已更改)。如果您不想创建会话,则可以将此属性设置为false

<bean id="securityContextPersistenceFilter"
	class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<property name='securityContextRepository'>
	<bean class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
	<property name='allowSessionCreation' value='false' />
	</bean>
</property>
</bean>

或者,您可以提供NullSecurityContextRepositorynull object实现的实例,即使在请求期间已经创建了会话,也将阻止存储安全上下文。

14.4 UsernamePasswordAuthenticationFilter

现在,我们已经看到了 Spring Security Web 配置中始终存在的三个主要过滤器。这也是由名称空间<http>元素自动创建的三个,不能用替代项代替。现在唯一缺少的是一种实际的身份验证机制,该机制将允许用户进行身份验证。此过滤器是最常用的身份验证过滤器,也是最常定制的[13]。它还提供了命名空间中<form-login>元素使用的实现。对其进行配置需要三个阶段。

  • 像上面一样,使用登录页面的 URL 配置LoginUrlAuthenticationEntryPoint,并将其设置在ExceptionTranslationFilter上。

  • 实现登录页面(使用 JSP 或 MVC 控制器)。

  • 在应用程序上下文中配置UsernamePasswordAuthenticationFilter的实例

  • 将滤 bean 添加到您的过滤链代理(确保您注意 Sequences)。

登录表单仅包含usernamepasswordImporting 字段,并发布到由过滤器监视的 URL(默认为/login)。基本过滤器配置如下所示:

<bean id="authenticationFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>

14.4.1 身份验证成功和失败的应用程序流程

过滤器调用已配置的AuthenticationManager来处理每个身份验证请求。认证成功或认证失败后的目的地分别由AuthenticationSuccessHandlerAuthenticationFailureHandler策略接口控制。过滤器具有允许您设置这些属性的属性,因此您可以完全自定义行为[14]。提供了一些标准实现,例如SimpleUrlAuthenticationSuccessHandlerSavedRequestAwareAuthenticationSuccessHandlerSimpleUrlAuthenticationFailureHandlerExceptionMappingAuthenticationFailureHandlerDelegatingAuthenticationFailureHandler。查看这些类以及AbstractAuthenticationProcessingFilter的 Javadoc,以大致了解它们的工作方式和受支持的功能。

如果身份验证成功,则将结果Authentication对象放入SecurityContextHolder。然后将调用已配置的AuthenticationSuccessHandler来将用户重定向或转发到适当的目的地。默认情况下,使用SavedRequestAwareAuthenticationSuccessHandler,这意味着用户将被重定向到他们要求的原始目的地,然后才被要求登录。

Note

ExceptionTranslationFilter缓存用户发出的原始请求。当用户进行身份验证时,请求处理程序将使用此缓存的请求来获取原始 URL 并重定向到该 URL。然后,原始请求将被重建并用作替代。

如果身份验证失败,则将调用配置的AuthenticationFailureHandler


[11]我们使用转发,以便 SecurityContextHolder 仍包含主体的详细信息,这可能对显示给用户很有用。在旧版本的 Spring Security 中,我们依靠 servlet 容器来处理 403 错误消息,该消息缺少此有用的上下文信息。

[12]在 Spring Security 2.0 和更早的版本中,此过滤器称为HttpSessionContextIntegrationFilter,并且存储上下文的所有工作都是由过滤器本身完成的。如果您熟悉此类,那么现在可以在HttpSessionSecurityContextRepository上找到大多数可用的配置选项。

[13]由于历史原因,在 Spring Security 3.0 之前,此过滤器称为AuthenticationProcessingFilter,入口点称为AuthenticationProcessingFilterEntryPoint。由于该框架现在支持许多不同形式的身份验证,因此在 3.0 中都为它们都指定了更具体的名称。

[14]在 3.0 之前的版本中,此时的应用程序流程已 Developing 到由此类和策略插件上的属性混合控制的阶段。决定使用 3.0 重构代码以使这两种策略完全负责。