Spring Security Filter

Spring Security Filter

Filter

Spring Security 的底层是经过一系列的 Filter 来处置的,每个 Filter 都有其自己的功效,并且各个 Filter 在功效上还相关联联系,以是它们的程序也利害常要害的。

Filter 程序

Spring Security 仍旧设置了少许 Filter,尽管本质运用中你用到了哪些,它们该当维持如次程序。

ChannelProcessingFilter,即使你考察的 channel 错了,那开始就会在 channel 之间举行跳转,如 http 变为 https。

SecurityContextPersistenceFilter,如许的话在一发端举行 request 的功夫就不妨在 SecurityContextHolder 中创造一个 SecurityContext,而后在乞求中断的功夫,任何对 SecurityContext 的变换都不妨被 copy 到 HttpSession。

ConcurrentSessionFilter,由于它须要运用 SecurityContextHolder 的功效,并且革新对应 session 的结果革新功夫,以及经过 SessionRegistry 获得暂时的 SessionInformation 以查看暂时的 session 能否已过程期,过时则会挪用 LogoutHandler。

认证处置体制,如 UsernamePasswordAuthenticationFilter,CasAuthenticationFilter,BasicAuthenticationFilter 等,及至于 SecurityContextHolder 不妨被革新为包括一个灵验的 Authentication 乞求。

SecurityContextHolderAwareRequestFilter,它将会把 HttpServletRequest 封装成一个接受自 HttpServletRequestWrapper 的 SecurityContextHolderAwareRequestWrapper,同声运用 SecurityContext 实行了 HttpServletRequest 中与安定关系的本领。

JaasApiIntegrationFilter,即使 SecurityContextHolder 中具有的 Authentication 是一个 JaasAuthenticationToken,那么该 Filter 将运用包括在 JaasAuthenticationToken 中的 Subject 连接实行 FilterChain。

RememberMeAuthenticationFilter,即使之前的认证处置体制没有革新 SecurityContextHolder,而且用户乞求包括了一个 Remember-Me 对应的 cookie,那么一个对应的 Authentication 将会设给 SecurityContextHolder。

AnonymousAuthenticationFilter,即使之前的认证体制都没有革新 SecurityContextHolder 具有的 Authentication,那么一个 AnonymousAuthenticationToken 将会设给 SecurityContextHolder。

ExceptionTransactionFilter,用来处置在 FilterChain 范畴内抛出的 AccessDeniedException 和 AuthenticationException,并把它们变换为对应的 Http 缺点码归来大概对应的页面。

FilterSecurityInterceptor,养护 Web URI,而且在考察被中断时抛出特殊。

增添 Filter 到 FilterChain

当咱们在运用 NameSpace 时,Spring Security 是会机动为咱们创造对应的 FilterChain 以及个中的 Filter。但偶尔咱们大概须要增添咱们本人的 Filter 到 FilterChain,又大概是由于某些个性须要本人表露的设置 Spring Security 仍旧为咱们供给好的 Filter,而后再把它们增添到 FilterChain。运用 NameSpace 时增添 Filter 到 FilterChain 是经过 http 元素下的 custom-filter 元从来设置的。设置 custom-filter 时须要咱们经过 ref 属性指定其对应关系的是哪个 Filter,其余还须要经过 position、before 大概 after 指定该 Filter 安置的场所。诚如在上一节《Filter 程序》中所提到的那么,Spring Security 对 FilterChain 中 Filter 程序是有庄重的规则的。Spring Security 对那些内置的 Filter 都指定了一个别号,同声指定了它们的场所。咱们在设置 custom-filter 的 position、before 和 after 时运用的值即是对应着那些别号所处的场所。如 position=”CAS_FILTER” 就表白将设置的 Filter 放在 CAS_FILTER 对应的谁人场所,before=”CAS_FILTER” 就表白将设置的 Filter 放在 CAS_FILTER 之前,after=”CAS_FILTER” 就表白将设置的 Filter 放在 CAS_FILTER 之后。其余再有两个特出的场所不妨指定,FIRST 和 LAST,辨别对应第一个和结果一个 Filter,如你想把设置好的 Filter 放在结果,则不妨运用 after=”LAST”。

接下来咱们来看一下 Spring Security 给咱们设置好的 FilterChain 中 Filter 对应的场所程序、它们的别号以及将触发机动增添到 FilterChain 的元素或属性设置。底下的设置是按程序的。

别号

Filter 类

对应元素或属性

CHANNEL_FILTER

ChannelProcessingFilter

http/intercept-url@requires-channel

SECURITY_CONTEXT_FILTER

SecurityContextPersistenceFilter

http

CONCURRENT_SESSION_FILTER

ConcurrentSessionFilter

http/session-management/concurrency-control

LOGOUT_FILTER

LogoutFilter

http/logout

X509_FILTER

X509AuthenticationFilter

http/x509

PRE_AUTH_FILTER

AstractPreAuthenticatedProcessingFilter 的子类

CAS_FILTER

CasAuthenticationFilter

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

http/session-management

EXCEPTION_TRANSLATION_FILTER

ExceptionTranslationFilter

http

FILTER_SECURITY_INTERCEPTOR

FilterSecurityInterceptor

http

SWITCH_USER_FILTER

SwitchUserFilter

DelegatingFilterProxy

大概你会感触怪僻,咱们在 web 运用中运用 Spring Security 时只在 web.xml 文献中设置了如次如许一个 Filter,干什么你会说是一系列的 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>

并且即使你不在 web.xml 文献证明要运用的 Filter,那么 Servlet 容器将不会创造它们,它们又如何爆发效率呢?这即是上述摆设中 DelegatingFilterProxy 的效率了。

DelegatingFilterProxy 是 Spring 中设置的一个 Filter 实行类,其效率是代劳真实的 Filter 实行类,也即是说在挪用 DelegatingFilterProxy 的 doFilter() 本领时本质上挪用的是其代劳 Filter 的 doFilter() 本领。其代劳 Filter 必需是一个 Spring bean 东西,以是运用 DelegatingFilterProxy 的长处即是其代劳 Filter 类不妨运用 Spring 的依附注入体制简单自在的运用 ApplicationContext 中的 bean。那么 DelegatingFilterProxy 怎样领会其所代劳的 Filter 是哪个呢?这是经过其自己的一个叫 targetBeanName 的属性来决定的,经过该称呼,DelegatingFilterProxy 不妨从 WebApplicationContext 中获得指定的 bean 动作代劳东西。该属性不妨经过在 web.xml 中设置 DelegatingFilterProxy 时经过 init-param 来指定,即使未指定的话将默许取其在 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 代劳的即是名为 SpringSecurityFilterChain 的 Filter。

须要提防的是被代劳的 Filter 的初始化本领 init() 和废弃本领 destroy() 默许是不会被实行的。经过树立 DelegatingFilterProxy 的 targetFilterLifecycle 属性为 true,不妨使被代劳 Filter 与 DelegatingFilterProxy 具备同样的人命周期。

FilterChainProxy

Spring Security 底层是经过一系列的 Filter 来处事的,每个 Filter 都有其各自的功效,并且各个 Filter 之间还相关联联系,以是它们的拉拢程序也利害常要害的。

运用 Spring Security 时,DelegatingFilterProxy 代劳的即是一个 FilterChainProxy。一个 FilterChainProxy 中不妨包括有多个 FilterChain,然而某个乞求只会对应一个 FilterChain,而一个 FilterChain 中又不妨包括有多个 Filter。当咱们运用鉴于 Spring Security 的 NameSpace 举行摆设时,体例会机动为咱们备案一个名为 springSecurityFilterChain 典型为 FilterChainProxy 的 bean(这也是干什么咱们在运用 SpringSecurity 时须要在 web.xml 中证明一个 name 为 springSecurityFilterChain 典型为 DelegatingFilterProxy 的 Filter 了。),并且每一个 http 元素的设置都将具有本人的 FilterChain,而 FilterChain 中所具有的 Filter 则会按照设置的效劳机动增减。以是咱们不须要表露的再设置那些 Filter 对应的 bean 了,只有你想实行本人的论理,又大概你想设置的某个属性 NameSpace 没有供给对应扶助等。

Spring security 承诺咱们在摆设文献中摆设多个 http 元素,以对准各别情势的 URL 运用各别的安定遏制。Spring Security 将会为每一个 http 元素创造对应的 FilterChain,同声依照它们的证明程序介入到 FilterChainProxy。以是当咱们同声设置多个 http 元素时要保证将更具备个性的 URL 摆设在前。

<security:http pattern="/login*.jsp*" security="none"/>

<!-- http 元素的 pattern 属性指定暂时的 http 对应的 FilterChain 将配合哪些 URL,如未指定将配合一切的乞求 -->

<security:http pattern="/admin/**">

<security:intercept-url pattern="/**" access="ROLE_ADMIN"/>

</security:http>

<security:http>

<security:intercept-url pattern="/**" access="ROLE_USER"/>

</security:http>

须要提防的是 http 具有一个配合 URL 的 pattern,未指准时表白配合一切的乞求,其下的子元素 intercept-url 也有一个配合 URL 的 pattern,该 pattern 是在 http 元素对应 pattern 普通上的,也即是说一个乞求必需先满意 http 对应的 pattern 才有大概满意其下 intercept-url 对应的 pattern。

Spring Security 设置好的中心 Filter

经过前方的引见咱们领会 Spring Security 是经过 Filter 来处事的,为保护 Spring Security 的成功运转,其里面实行了一系列的 Filter。这个中有几个是在运用 Spring Security 的 Web 运用中必然会用到的。接下来咱们来扼要的引见一下 FilterSecurityInterceptor、ExceptionTranslationFilter、SecurityContextPersistenceFilter 和 UsernamePasswordAuthenticationFilter。在咱们运用 http 元素时前三者会机动增添到对应的 FilterChain 中,当咱们运用了 form-login 元素时 UsernamePasswordAuthenticationFilter 也会机动增添到 FilterChain 中。以是咱们在运用 custom-filter 往 FilterChain 中增添本人设置的那些 Filter 时须要提防它们的场所。

FilterSecurityInterceptor

FilterSecurityInterceptor 是用来养护 Http 资源的,它须要一个 AccessDecisionManager 和一个 AuthenticationManager 的援用。它会从 SecurityContextHolder 获得 Authentication,而后经过 SecurityMetadataSource 不妨得悉暂时乞求能否在乞求受养护的资源。对于乞求那些受养护的资源,即使 Authentication.isAuthenticated()归来 false 大概 FilterSecurityInterceptor 的 alwaysReauthenticate 属性为 true,那么将会运用其援用的 AuthenticationManager 再认证一次,认证之后再运用认证后的 Authentication 替代 SecurityContextHolder 中具有的谁人。而后即是运用 AccessDecisionManager 举行权力的查看。

咱们在运用鉴于 NameSpace 的摆设时所摆设的 intercept-url 就会跟 FilterChain 里面的 FilterSecurityInterceptor 绑定。即使要本人设置 FilterSecurityInterceptor 对应的 bean,那么该 bean 设置大概如次所示:

<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="/admin/**" access="ROLE_ADMIN" />

<security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN" />

</security:filter-security-metadata-source>

</property>

</bean>

filter-security-metadata-source 用来摆设其 securityMetadataSource 属性。intercept-url 用来摆设须要阻挡的 URL 与对应的权力联系。

ExceptionTranslationFilter

经过前方的引见咱们领会在 Spring Security 的 Filter 链表中 ExceptionTranslationFilter 就放在 FilterSecurityInterceptor 的前方。而 ExceptionTranslationFilter 是捕捉来自 FilterChain 的特殊,并对那些特殊做处置。ExceptionTranslationFilter 不妨捕捉来自 FilterChain 一切的特殊,然而它只会处置两类特殊,AuthenticationException 和 AccessDeniedException,其它的特殊它会连接抛出。即使捕捉到的是 AuthenticationException,那么将会运用其对应的 AuthenticationEntryPoint 的 commence()处置。即使捕捉的特殊是一个 AccessDeniedException,那么将视暂时考察的用户能否仍旧登录认证做各别的处置,即使未登录,则会运用关系的 AuthenticationEntryPoint 的 commence()本领举行处置,要不将运用关系的 AccessDeniedHandler 的 handle()本领举行处置。

AuthenticationEntryPoint 是在用户没有登录时用来启发用户举行登录认证的,在本质运用中应按照简直的认证体制采用对应的 AuthenticationEntryPoint。

AccessDeniedHandler 用来在用户仍旧登录了,然而考察了其自己没有权力的资源时做出对应的处置。ExceptionTranslationFilter 具有的 AccessDeniedHandler 默许是 AccessDeniedHandlerImpl,其会归来一个 403 缺点码到存户端。咱们不妨经过表露的摆设 AccessDeniedHandlerImpl,同声给其指定一个 errorPage 使其不妨归来对应的缺点页面。固然咱们也不妨实行本人的 AccessDeniedHandler。

<bean id="exceptionTranslationFilter"

class="org.springframework.security.web.access.ExceptionTranslationFilter">

<property name="authenticationEntryPoint">

<bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">

<property name="loginFormUrl" value="/login.jsp" />

</bean>

</property>

<property name="accessDeniedHandler">

<bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">

<property name="errorPage" value="/access_denied.jsp" />

</bean>

</property>

</bean>

在上述摆设中咱们指定了 AccessDeniedHandler 为 AccessDeniedHandlerImpl,同声为其指定了 errorPage,如许爆发 AccessDeniedException 后将转到对应的 errorPage 上。指定了 AuthenticationEntryPoint 为运用表单登录的 LoginUrlAuthenticationEntryPoint。其余,须要提防的是即使该 filter 是动作自设置 filter 介入到由 NameSpace 机动创造的 FilterChain 中时需把它放在前置的 ExceptionTranslationFilter 反面,要不特殊都将被内置的 ExceptionTranslationFilter 所捕捉。

<security:http>

<security:form-login login-page="/login.jsp"

username-parameter="username" password-parameter="password"

login-processing-url="/login.do" />

<!-- 退出登录时简略 session 对应的 cookie -->

<security:logout delete-cookies="JSESSIONID" />

<!-- 登录页面该当是不须要认证的 -->

<security:intercept-url pattern="/login*.jsp*"

access="IS_AUTHENTICATED_ANONYMOUSLY" />

<security:intercept-url pattern="/**" access="ROLE_USER" />

<security:custom-filter ref="exceptionTranslationFilter" after="EXCEPTION_TRANSLATION_FILTER"/>

</security:http>

在捕捉到 AuthenticationException 之后,挪用 AuthenticationEntryPoint 的 commence() 本领启发用户登录之前,ExceptionTranslationFilter 还做了一件事,那即是运用 RequestCache 将暂时 HttpServletRequest 的消息生存起来,及至于用户胜利登录后须要跳转到之前的页面时不妨获得到那些消息,而后连接之前的乞求,比方用户大概在未登录的情景下公布指摘,待用户提交指摘的功夫就会将包括指摘消息的暂时乞求生存起来,同声启发用户举行登录认证,待用户胜利登录后再运用从来的 request 包括的消息连接之前的乞求,即连接提交指摘,以是待用户登录胜利后咱们常常看到的是用户胜利提交了指摘之后的页面。Spring Security 默许运用的 RequestCache 是 HttpSessionRequestCache,其会将 HttpServletRequest 关系消息封装为一个 SavedRequest 生存在 HttpSession 中。

SecurityContextPersistenceFilter

SecurityContextPersistenceFilter 会在乞求发端时从摆设好的 SecurityContextRepository 中获得 SecurityContext,而后把它树立给 SecurityContextHolder。在乞求实行后将 SecurityContextHolder 持有的 SecurityContext 再生存到摆设好的 SecurityContextRepository,同声废除 SecurityContextHolder 所持有的 SecurityContext。在运用 NameSpace 时,Spring Security 默许会给 SecurityContextPersistenceFilter 的 SecurityContextRepository 树立一个 HttpSessionSecurityContextRepository,其会将 SecurityContext 生存在 HttpSession 中。其余 HttpSessionSecurityContextRepository 有一个很要害的属性 allowSessionCreation,默许为 true。如许须要把 SecurityContext 生存在 session 中时,即使不生存 session,不妨机动创造一个。也不妨把它树立为 false,如许在乞求中断后即使没有可用的 session 就不会生存 SecurityContext 到 session 了。SecurityContextRepository 再有一个空实行,NullSecurityContextRepository,即使在乞求实行后不想生存 SecurityContext 也不妨运用它。

这边再弥补证明一点干什么 SecurityContextPersistenceFilter 在乞求实行后须要废除 SecurityContextHolder 的 SecurityContext。SecurityContextHolder 在树立和生存 SecurityContext 都是运用的静态本领,简直操纵是由其所持有的 SecurityContextHolderStrategy 实行的。默许运用的是鉴于线程变量的实行,即 SecurityContext 是寄存在 ThreadLocal 内里的,如许各个独力的乞求都将具有本人的 SecurityContext。在乞求实行后废除 SecurityContextHolder 中的 SucurityContext 即是废除 ThreadLocal,Servlet 容器普遍都有本人的线程池,这不妨制止 Servlet 容器下一次散发线程时线程中还包括 SecurityContext 变量,进而惹起不需要的缺点。

底下是一个 SecurityContextPersistenceFilter 的大略摆设。

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

UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter 用来处置来自表单提交的认证。该表单必需供给对应的用户名和暗号,对应的参数名默许为 j_username 和 j_password。即使不想运用默许的参数名,不妨经过 UsernamePasswordAuthenticationFilter 的 usernameParameter 和 passwordParameter 举行指定。表单的提交路途默许是 “j_spring_security_check”,也不妨经过 UsernamePasswordAuthenticationFilter 的 filterProcessesUrl 举行指定。经过属性 postOnly 不妨指定只承诺登录表单举行 post 乞求,默许是 true。其里面再有登录胜利或波折保守行处置的 AuthenticationSuccessHandler 和 AuthenticationFailureHandler,那些都不妨按照需要做关系变换。其余,它还须要一个 AuthenticationManager 的援用举行认证,这个是没有默许摆设的。

<bean id="authenticationFilter"

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

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

<property name="usernameParameter" value="username"/>

<property name="passwordParameter" value="password"/>

<property name="filterProcessesUrl" value="/login.do" />

</bean>

即使要在 http 元素设置中运用上述 AuthenticationFilter 设置,那么完备的摆设该当一致于如次这格式。

<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

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/security

http://www.springframework.org/schema/security/spring-security-3.1.xsd">

<!-- entry-point-ref 指定登录进口 -->

<security:http entry-point-ref="authEntryPoint">

<security:logout delete-cookies="JSESSIONID" />

<security:intercept-url pattern="/login*.jsp*"

access="IS_AUTHENTICATED_ANONYMOUSLY" />

<security:intercept-url pattern="/**" access="ROLE_USER" />

<!-- 增添本人设置的 AuthenticationFilter 到 FilterChain 的 FORM_LOGIN_FILTER 场所 -->

<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>

</security:http>

<!-- AuthenticationEntryPoint,启发用户举行登录 -->

<bean id="authEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">

<property name="loginFormUrl" value="/login.jsp"/>

</bean>

<!-- 认证过滤器 -->

<bean id="authenticationFilter"

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

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

<property name="usernameParameter" value="username"/>

<property name="passwordParameter" value="password"/>

<property name="filterProcessesUrl" value="/login.do" />

</bean>

<security:authentication-manager alias="authenticationManager">

<security:authentication-provider

user-service-ref="userDetailsService">

<security:password-encoder hash="md5"

base64="true">

<security:salt-source user-property="username" />

</security:password-encoder>

</security:authentication-provider>

</security:authentication-manager>

<bean id="userDetailsService"

class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">

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

</bean>

</beans>

分享到 :

Leave a Reply

Your email address will not be published. Required fields are marked *