Spring Security 04

转至:Elim的博客http://elim.iteye.com/blog/2161648

Filter Porxy

DelegatingFilterProxy

DelegationFilterProxy不是spring security的入口,它其实是spring的一个代理类(org.springframework.web.filter.DelegatingFilterProxy),作用是将spring 与 spring security融合。
它内部代理的是spring scurity的FilterChainProxy(org.springframework.security.web.FilterChainProxy)代理类。真正执行工作的是是FilterChainProxy。FilterChainProxy中会加载
所有的spring security filter,然后调用每个Filter的doFilter方法。targetFilterLifecycle初始化为true时,会调用所有filer的init初始化方法
详情查看https://www.cnblogs.com/hzhuxin/archive/2011/12/19/2293730.html

<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 

  <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>  <!-- 默认是false -->
    </init-param>
 </filter>

<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>

 </filter-mapping>

FilterChainProxy

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。

Filter Chain中的Filer,按照先后顺序

  • 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时添加Filter到FilterChain是通过http元素下的custom-filter元素来定义的。定义custom-filter时需要我们通过ref属性指定其对应关联的是哪个Filter,此外还需要通过position、before或者after指定该Filter放置的位置。eg:position=”CAS_FILTER”就表示将定义的Filter放在CAS_FILTER对应的那个位置

Filter别名

别名 Filter类 对应元素或属性
CHANNEL_FILTER ChannelProcessingFilter http/intercept-url@requires-channe
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-managemen
EXCEPTION_TRANSLATION_FILTER ExceptionTranslationFilter http
FILTER_SECURITY_INTERCEPTOR FilterSecurityInterceptor http
SWITCH_USER_FILTER SwitchUserFilter

Spring Security定义好的核心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所捕获。

posted @ 2018-09-11 10:55  [烟火里的尘埃]  阅读(197)  评论(0编辑  收藏  举报