Spring Security 自定义过滤链filters

  • spring security过滤链filter的顺序参考, 查看org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.createFilterChain代码:
            //定义未排序filter集合。该集合中的对象为OrderDecorator实例。 
            List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();
            //添加http、认证相关的filter集合
            unorderedFilterChain.addAll(httpBldr.getFilters());
            unorderedFilterChain.addAll(authBldr.getFilters());
            unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
            //根据排序规则进行排序
            Collections.sort(unorderedFilterChain, new OrderComparator());
            //检查每个filter与前一个filter的位置是否相同  
            //这里的检查主要是防止自定义filter直接配置position属性,造成与默认的filter产生order冲突
            checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element));
    
            // The list of filter beans
            List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();
            //重新定义filterChain,把经过排序的filter依次添加到filterChain集合中
            for (OrderDecorator od : unorderedFilterChain) {
                filterChain.add(od.bean);
            }
    
            return createSecurityFilterChainBean(element, pc, filterChain);
     
  •         unorderedFilterChain.addAll(httpBldr.getFilters());
            unorderedFilterChain.addAll(authBldr.getFilters());
            unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
     上述代码返回的过滤链已经是经过OrderDecorator类包装的带有过滤链order顺序属性的集合。
  • Collections.sort(unorderedFilterChain, new OrderComparator());
     OrderComparator就是过滤链的排序比较器了,比较算法部分代码:
    public int compare(Object o1, Object o2) {
    		boolean p1 = (o1 instanceof PriorityOrdered);
    		boolean p2 = (o2 instanceof PriorityOrdered);
    		if (p1 && !p2) {
    			return -1;
    		}
    		else if (p2 && !p1) {
    			return 1;
    		}
    
    		// Direct evaluation instead of Integer.compareTo to avoid unnecessary object creation.
    		int i1 = getOrder(o1);
    		int i2 = getOrder(o2);
    		return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
    	}
     检查自定义过滤链当指定position属性时有可能做成的位置冲突
  • private void checkFilterChainOrder(List<OrderDecorator> filters, ParserContext pc, Object source) {
            logger.info("Checking sorted filter chain: " + filters);
    
            for(int i=0; i < filters.size(); i++) {
                OrderDecorator filter = filters.get(i);
    
                if (i > 0) {
                    OrderDecorator previous = filters.get(i-1);
                    if (filter.getOrder() == previous.getOrder()) {
                        pc.getReaderContext().error("Filter beans '" + filter.bean + "' and '" +
                                previous.bean + "' have the same 'order' value. When using custom filters, " +
                                        "please make sure the positions do not conflict with default filters. " +
                                        "Alternatively you can disable the default filters by removing the corresponding " +
                                        "child elements from <http> and avoiding the use of <http auto-config='true'>.", source);
                    }
                }
            }
        }
     
  • Spring Security用枚举类型org.springframework.security.config.http.SecurityFilters来维护filter的顺序,再用org.springframework.security.config.http.OrderDecorator来包装filter及filter的顺序,再由org.springframework.core.OrderComparator比较filter的先后顺序。
  • 要自定义Spring Security的过滤链就必须了解filter的先后顺序,否者Spring Security不会正常运作。
  • 在web.xml添加spring security过滤器代理
    <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添加session 事件发布
    <listener>
            <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
        </listener>
     
  • spring security 配置文件如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:security="http://www.springframework.org/schema/security"
           xmlns="http://www.springframework.org/schema/beans"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security.xsd">
        <bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
            <security:filter-chain-map request-matcher="ant">
                <security:filter-chain pattern="/**" filters="
                    securityContextPersistenceFilter,
                    currentSessionFilter,
                    asyncManagerIntegrationFilter,
                    logoutFilter,
                    usernamePasswordAuthenticationFilter,
                    basicAuthenticationFilter,
                    requestCacheAwareFilter,
                    contextHolderAwareRequestFilter,
                    rememberMeAuthenticationFilter,
                    anonymousAuthenticationFilter,
                    sessionManagementFilter,
                    exceptionTranslationFilter,
                    filterSecurityInterceptor
                "/>
            </security:filter-chain-map>
        </bean>
        <bean id="securityContextPersistenceFilter"
              class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
            <constructor-arg ref="securityContextRepository"/>
            <property name="forceEagerSessionCreation" value="false"/>
        </bean>
        <bean id="securityContextRepository"
              class="org.springframework.security.web.context.HttpSessionSecurityContextRepository">
            <property name="allowSessionCreation" value="true"/>
        </bean>
        <bean id="usernamePasswordAuthenticationFilter"
              class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
            <property name="authenticationManager" ref="customAuthenticationManager"/>
            <property name="usernameParameter" value="username"/>
            <property name="passwordParameter" value="password"/>
            <property name="rememberMeServices" ref="enhancedPersistentTokenBasedRememberMeServices"/>
            <property name="sessionAuthenticationStrategy" ref="compositeSessionAuthenticationStrategy"/>
            <property name="authenticationSuccessHandler" ref="savedRequestAwareAuthenticationSuccessHandler" />
            <property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler" />
            <property name="requiresAuthenticationRequestMatcher" ref="filterProcessUrlRequestMatcher" />
            <property name="allowSessionCreation" value="true"/>
        </bean>
        <bean id="savedRequestAwareAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler" >
            <property name="requestCache" ref="httpSessionRequestCache" />
            <property name="defaultTargetUrl" value="/home.html" />
        </bean>
        <bean id="anonymousAuthenticationFilter"
              class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
            <constructor-arg value="BF93JFJ091N00Q7HF"/>
        </bean>
        <bean id="filterSecurityInterceptor"
              class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
            <property name="authenticationManager" ref="customAuthenticationManager"/>
            <property name="accessDecisionManager" ref="affirmativeBased"/>
            <property name="securityMetadataSource">
                <security:filter-security-metadata-source use-expressions="true">
                    <security:intercept-url pattern="/css/*" access="permitAll"/>
                    <security:intercept-url pattern="/js/*" access="permitAll"/>
                    <security:intercept-url pattern="/fonts/*" access="permitAll"/>
                    <security:intercept-url pattern="/signup.html*" access="permitAll"/>
                    <security:intercept-url pattern="/login.html*" access="permitAll"/>
                    <security:intercept-url pattern="/category.html*" access="permitAll"/>
                    <security:intercept-url pattern="/category.html*" access="permitAll"/>
                    <security:intercept-url pattern="/*" access="hasRole('ROLE_USER')"/>
    
                </security:filter-security-metadata-source>
            </property>
        </bean>
        <bean id="affirmativeBased" class="org.springframework.security.access.vote.AffirmativeBased">
            <constructor-arg type="java.util.List">
                <list>
                    <ref bean="expressionVoter"/>
                    <ref bean="roleVoter"/>
                    <ref bean="authenticatedVoter"/>
                </list>
            </constructor-arg>
        </bean>
        <bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
        <bean id="authenticatedVoter" class="org.springframework.security.access.vote.AuthenticatedVoter"/>
        <bean id="daoAuthenticationProvider"
              class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
            <property name="userDetailsService" ref="userDao"/>
            <property name="passwordEncoder" ref="passwordEncoder"/>
        </bean>
        <bean id="anonymousAuthenticationProvider"
              class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
            <constructor-arg type="java.lang.String" value="BF93JFJ091N00Q7HF"/>
        </bean>
        <bean id="customAuthenticationManager" class="org.springframework.security.authentication.ProviderManager">
            <constructor-arg type="java.util.List">
                <list>
                    <ref bean="daoAuthenticationProvider"/>
                    <ref bean="anonymousAuthenticationProvider"/>
                    <ref bean="rememberMeAuthenticationProvider"/>
                </list>
            </constructor-arg>
            <property name="authenticationEventPublisher" ref="defaultAuthenticationEventPublisher" />
        </bean>
        <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
            <constructor-arg type="java.lang.String" value="/"/>
            <constructor-arg>
                <array>
                    <ref bean="logoutHandler"/>
                    <ref bean="enhancedPersistentTokenBasedRememberMeServices"/>
                </array>
            </constructor-arg>
            <property name="logoutRequestMatcher" ref="logoutFilterProcessUrlRequestMatcher" />
        </bean>
    
    
        <bean id="rememberMeAuthenticationFilter"
              class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
            <constructor-arg ref="customAuthenticationManager"/>
            <constructor-arg ref="enhancedPersistentTokenBasedRememberMeServices"/>
        </bean>
        <bean id="enhancedPersistentTokenBasedRememberMeServices"
              class="com.aasonwu.mycompany.EnhancedPersistentTokenBasedRememberMeServices">
            <constructor-arg type="java.lang.String"
                             value="BoSk70Yar38~veg91DoCKs=sLaIn!metE55bURgs71rug;ILEa=Ikon79sept+ree$Fuel99baKER;wOe43JackS=TinS79babA73tiLmibs10bIsE*"/>
            <constructor-arg type="org.springframework.security.core.userdetails.UserDetailsService"
                             ref="userDao"/>
            <constructor-arg
                    type="org.springframework.security.web.authentication.rememberme.PersistentTokenRepository"
                    ref="jdbcTokenRepository"/>
            <property name="cookieName" value="MYCOMPANY_REMEMBER_ME"/>
            <property name="parameter" value="remember_me"/>
        </bean>
    
        <bean id="jdbcTokenRepository"
              class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <bean id="rememberMeAuthenticationProvider"
              class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
            <constructor-arg
                    value="BoSk70Yar38~veg91DoCKs=sLaIn!metE55bURgs71rug;ILEa=Ikon79sept+ree$Fuel99baKER;wOe43JackS=TinS79babA73tiLmibs10bIsE*"/>
        </bean>
    
        <bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
            <constructor-arg ref="loginUrlAuthenticationEntryPoint"/>
            <constructor-arg ref="httpSessionRequestCache"/>
            <property name="accessDeniedHandler" ref="accessDeniedHandler"/>
        </bean>
    
        <bean id="loginUrlAuthenticationEntryPoint"
              class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
            <constructor-arg value="/login.html"/>
        </bean>
    
        <bean id="accessDeniedHandler" class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
            <property name="errorPage" value="/accessDenied.html"/>
        </bean>
    
        <bean id="expressionHandler"
              class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>
    
        <bean id="expressionVoter" class="org.springframework.security.web.access.expression.WebExpressionVoter">
            <property name="expressionHandler" ref="expressionHandler"/>
        </bean>
        <!--session manager-->
        <bean id="currentSessionFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
            <constructor-arg type="org.springframework.security.core.session.SessionRegistry" ref="sessionRegistry"/>
            <constructor-arg type="java.lang.String" value="/login.html"/>
            <property name="logoutHandlers" ref="logoutHandler"/>
        </bean>
        <bean id="logoutHandler"
              class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
            <property name="invalidateHttpSession" value="true"/>
            <property name="clearAuthentication" value="true"/>
        </bean>
        <bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
            <constructor-arg ref="securityContextRepository"/>
            <constructor-arg ref="compositeSessionAuthenticationStrategy"/>
            <property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"/>
            <!--<property name="invalidSessionStrategy" ref="simpleRedirectInvalidSessionStrategy" />-->
        </bean>
        <bean id="contextHolderAwareRequestFilter"
              class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter">
            <property name="authenticationManager" ref="customAuthenticationManager"/>
        </bean>
    
        <bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
        <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
    
        <bean id="sessionFixationProtectionStrategy"
              class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
            <property name="migrateSessionAttributes" value="true"/>
        </bean>
    
        <bean id="compositeSessionAuthenticationStrategy"
              class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
            <constructor-arg>
                <list>
                    <ref bean="sessionControlAuthenticationStrategy"/>
                    <ref bean="sessionFixationProtectionStrategy"/>
                    <ref bean="registerSessionAuthenticationStrategy"/>
                </list>
            </constructor-arg>
        </bean>
        <bean id="sessionControlAuthenticationStrategy"
              class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
            <constructor-arg ref="sessionRegistry"/>
            <property name="maximumSessions" value="1"/>
            <!--<property name="exceptionIfMaximumExceeded" value="false" />-->
        </bean>
        <bean id="registerSessionAuthenticationStrategy"
              class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
            <constructor-arg ref="sessionRegistry"/>
        </bean>
        <bean id="simpleUrlAuthenticationFailureHandler"
              class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
            <constructor-arg value="/login.html"/>
            <property name="allowSessionCreation" value="true" />
        </bean>
        <bean id="simpleRedirectInvalidSessionStrategy"
              class="org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy">
            <constructor-arg value="/accessDenied.html"/>
        </bean>
        <bean id="asyncManagerIntegrationFilter"
              class="org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter"/>
        <bean id="basicAuthenticationFilter"
              class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
            <constructor-arg ref="customAuthenticationManager"/>
            <constructor-arg ref="basicAuthenticationEntryPoint"/>
        </bean>
        <bean id="basicAuthenticationEntryPoint"
              class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
            <property name="realmName" value="Spring Security Application"/>
        </bean>
    
        <bean id="httpSessionRequestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
            <property name="createSessionAllowed" value="true"/>
        </bean>
        <bean id="requestCacheAwareFilter" class="org.springframework.security.web.savedrequest.RequestCacheAwareFilter">
            <constructor-arg ref="httpSessionRequestCache"/>
        </bean>
        <bean id="defaultAuthenticationEventPublisher" class="org.springframework.security.authentication.DefaultAuthenticationEventPublisher" />
    
        <bean id="filterProcessUrlRequestMatcher" class="org.springframework.security.web.authentication.logout.LogoutFilter$FilterProcessUrlRequestMatcher">
            <constructor-arg value="/login" />
        </bean>
    
        <bean id="logoutFilterProcessUrlRequestMatcher" class="org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter$FilterProcessUrlRequestMatcher">
            <constructor-arg value="/logout"/>
        </bean>
    
        <security:global-method-security pre-post-annotations="enabled" />
    </beans>
     
  • 这个配置接近使用命名空间配置上下文的环境,并实现了自定义的userDetailServics, remember me 功能。

猜你喜欢

转载自aasonwu.iteye.com/blog/2066991