- 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);
-
上述代码返回的过滤链已经是经过OrderDecorator类包装的带有过滤链order顺序属性的集合。unorderedFilterChain.addAll(httpBldr.getFilters()); unorderedFilterChain.addAll(authBldr.getFilters()); unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
-
OrderComparator就是过滤链的排序比较器了,比较算法部分代码:Collections.sort(unorderedFilterChain, new OrderComparator());
检查自定义过滤链当指定position属性时有可能做成的位置冲突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; }
-
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 功能。
Spring Security 自定义过滤链filters
最新推荐文章于 2024-02-24 12:00:00 发布