10.2 核心Security过滤器

在使用Spring Security的Web应用程序中,总会用到一些关键的过滤器,所以我们首先来看看这些过滤器及其支持类和接口。我们不会涵盖每一个特性,因此如果您想获得完整的图片,一定要查看JavaDoc中的特性。

10.2.1 FilterSecurityInterceptor

在讨论 access-control in general时,我们已经简要地看过了 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资源的安全性。它需要对 AuthenticationManager AccessDecisionManager的引用。它还提供了适用于不同HTTP URL请求的配置属性。请参阅技术介绍 the original discussion on these 。

FilterSecurityInterceptor 可以通过两种方式携带属性参数进行配置。第一个是使用<filter-security-metadata-source>namespace元素,如上图所示。这类似于命名空间章节中的<http>元素,但<intercept url>子元素仅使用pattern access 属性。逗号用于分隔应用于每个HTTP URL的不同配置属性。第二种选择是编写自己的SecurityMetadataSource,但这超出了本文档的范围。无论使用何种方法,SecurityMetadataSource 都负责返回一个包含与单个安全HTTP URL关联的所有配置属性的 List<ConfigAttribute>。

应该注意,FilterSecurityInterceptor.setSecurityMetadataSource() 方法实际需要FilterInvocationSecurityMetadataSource 的实例。这是SecurityMetadataSource类的一个子类。它只是表示SecurityMetadataSource 理解 FilterInvocations 。为了简单起见,我们将继续将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>

Patterns 总是按照其定义的顺序进行评估。因此,在列表中定义更具体的模式比定义不那么具体的模式要更靠前。这反映在上面的示例中,其中更具体的 /secure/super/ pattern比不那么具体的 /secure/ pattern 靠前。如果它们被颠倒,/secure/ pattern将始终匹配,而 /secure/super/ pattern将永远不会被评估。

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

身份验证入口点 AuthenticationEntryPoint

如果用户请求受保护的HTTP资源,但未对其进行身份验证,则将调用 AuthenticationEntryPoint 。安全拦截器将沿着调用堆栈进一步抛出适当的 AuthenticationException AccessDeniedException ,从而在入口点触发 commence 方法。这样做的目的是向用户提供适当的响应,以便可以开始身份验证。我们在这里使用的是LoginLauthenAuthenticationEntryPoint,它将请求重定向到不同的URL(通常是登录页面)。实际使用的AuthenticationEntryPoint 实现类取决于您希望在应用程序中使用的身份验证机制。

访问拒绝控制器 AccessDeniedHandler

如果用户已经过身份验证通过,并且试图访问受保护的资源,会发生什么情况呢?在正常使用中,不应该发生这种情况,因为应用程序工作流应限制为用户可以访问的操作。例如,指向管理页面的HTML链接可能对没有管理角色的用户隐藏。但是,您不能依靠隐藏链接来实现安全性,因为用户总是有可能直接输入URL来绕过这些限制。或者他们可以修改一个RESTful URL来更改一些参数值。您的应用程序必须针对这些场景进行保护,否则它肯定是不安全的。通常,您将使用简单的Web层安全性来对基本URL应用约束,并在服务层接口上使用更具体的基于方法的安全性来真正确定允许的内容。

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

还可以在使用命名空间配置应用程序时提供自定义的 AccessDeniedHandler 。有关详细信息,请参阅 the namespace appendix

SavedRequests 和 RequestCache接口

ExceptionTranslationFilter 的另一个职责是在调用 AuthenticationEntryPoint 之前保存当前请求。这允许在用户进行身份验证后还原请求(请参阅先前的Web身份验证概述)。一个典型的例子是,用户使用一个表单登录,然后由默认的SavedRequestAwareAuthenticationSuccessHandler (见下文)重定向到原始URL。

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

在正常情况下,您不需要修改任何此功能,但保存的请求处理是一种“尽最大努力”的方法,并且可能存在默认配置无法处理的情况。这些接口的使用使得它可以从SpringSecurity3.0开始完全插入。

10.2.3 安全上下文持久性筛选器 SecurityContextPersistenceFilter

我们在技术概述一章中介绍了这个非常重要的过滤器的用途,因此您可能希望在此时重新阅读该部分。让我们先看看如何配置它与 FilterChainProxy 一起使用。基本配置只需要bean本身。

<bean id="securityContextPersistenceFilter"

class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>

正如我们之前看到的,这个过滤器有两个主要任务。它负责在HTTP请求之间存储SecurityContext内容,并在请求完成时清除 SecurityContextHolder。清除存储上下文的 ThreadLocal 是非常重要的,因为线程可能会被替换到servlet容器的线程池中,而特定用户的安全上下文仍然附加。该线程随后可能会在后期使用,使用错误的凭据执行操作。

安全上下文存储库 SecurityContextRepository

从SpringSecurity3.0开始,加载和存储安全上下文的工作现在被委托给一个单独的策略接口:

public interface SecurityContextRepository {

SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);

void saveContext(SecurityContext context, HttpServletRequest request,

        HttpServletResponse response);

}

HttpRequestResponseHolder 只是接收请求和响应对象的容器,允许用包装类的实现来替换这些对象。返回的内容将传递到过滤器链。

默认实现是 HttpSessionSecurityContextRepository,它将安全上下文存储为 HttpSession 属性。此实现最重要的配置参数是 allowSessionCreation 属性,该属性默认为 true,从而允许类在需要会话时为经过身份验证的用户存储安全上下文时创建 session(除非进行了身份验证,并且安全上下文的内容发生了改变,否则不会创建)。如果不希望创建session ,则可以将此属性设置为 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>

或者,你可以提供一个 NullSecurityContextRepository 实例,一个空对象实现,它将阻止存储安全上下文,即使在请求期间已经创建了会话。

10.2.4 UsernamePasswordAuthenticationFilter

我们现在看到了三个主要的过滤器,它们总是出现在Spring Security Web配置中。这三个元素也是由namespace <http> 元素自动创建的,不能用替代项替换。现在唯一缺少的是一个实际的身份验证机制,它允许用户进行身份验证。此筛选器是最常用的身份验证筛选器,也是最常用的自定义筛选器。它还提供由命名空间中的<form login>元素使用的实现。配置它需要三个阶段。

\bullet  像上面所做的那样,使用登录页面的URL配置一个LoginLauthenAuthenticationEntryPoint,并将其设置在ExceptionTranslationFilter上。

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

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

\bullet  将过滤器bean添加到您的过滤器链代理(确保您关注顺序)。

登录表单只包含用户名和密码输入字段,并发布到由过滤器监控的URL(默认为/login)。基本的过滤器配置如下所示:

<bean id="authenticationFilter" class=

"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">

<property name="authenticationManager" ref="authenticationManager"/>

</bean>

Application Flow on Authentication Success and Failure

过滤器调用配置的 AuthenticationManager 来处理每个身份验证请求。成功身份验证或身份验证失败后的目标分别由AuthenticationSuccessHandler AuthenticationFailureHandler 策略接口控制。过滤器具有允许您设置属性,因此您可以完全自定义行为。提供了一些标准实现,如 SimpleUrlAuthenticationSuccessHandlerSavedRequestAwareAuthenticationSuccessHandlerSimpleUrlAuthenticationFailureHandlerExceptionMappingAuthenticationFailureHandler DelegatingAuthenticationFailureHandler。多了解这些类的javadoc,以及 AbstractAuthenticationProcessingFilter ,了解它们的工作原理和支持的功能。

如果身份验证成功,则生成的 Authentication 将放入SecurityContextHolder。然后,将调用配置的AuthenticationSuccessHandler ,将用户重定向或转发到适当的目标。默认情况下,会使用SavedRequestAwareAuthenticationSuccessHandler ,这意味着用户将被重定向到请求登录之前所请求的原始目标。

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,009评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,808评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,891评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,283评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,285评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,409评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,809评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,487评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,680评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,499评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,548评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,268评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,815评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,872评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,102评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,683评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,253评论 2 341

推荐阅读更多精彩内容