2011-12-27 105 views
9

通道處理器的默認行爲是執行sendRedirect(使用302代碼臨時重定向)。我需要改變這種行爲,以便完成永久(301)重定向而不是302重定向。我試圖做到以下幾點:用Spring Security 3.0.5覆蓋ChannelProcessingFilter不起作用

public class MyChannelProcessingFilter extends ChannelProcessingFilter{ 
    //No implementation, I needed this to just make sure that a custom filter is created and I can configure it as a custom filter in the xml file. 
} 
  • 通過擴展AbstractRetryEntryPoint

    public class RetryWithHttpsEntryPoint extends org.springframework.security.web.access.channel.AbstractRetryEntryPoint { 
        private PortResolver portResolver = new PortResolverImpl(); 
        private final String scheme ="https://"; 
        /** The standard port for the scheme (80 for http, 443 for https) */ 
        private final int standardPort = 443; 
    
        public RetryWithHttpsEntryPoint() { 
         super("https://", 443); 
        } 
    
        @Override 
        public void commence(HttpServletRequest request, HttpServletResponse res) throws IOException, ServletException { 
         String queryString = request.getQueryString(); 
         String redirectUrl = request.getRequestURI() + ((queryString == null) ? "" : ("?" + queryString)); 
    
         Integer currentPort = new Integer(portResolver.getServerPort(request)); 
         Integer redirectPort = getMappedPort(currentPort); 
    
         if (redirectPort != null) { 
          boolean includePort = redirectPort.intValue() != standardPort; 
    
          redirectUrl = scheme + request.getServerName() + ((includePort) ? (":" + redirectPort) : "") + redirectUrl; 
         } 
    
         if (logger.isDebugEnabled()) { 
          logger.debug("Redirecting to: " + redirectUrl); 
         } 
    
         res.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); 
         res.setHeader("Location", redirectUrl); 
         res.setHeader("Connection", "close"); 
        } 
    
        protected Integer getMappedPort(Integer mapFromPort) { 
         return getPortMapper().lookupHttpsPort(mapFromPort); 
        } 
    } 
    
  • 配置相同的創建自定義的入口點:

    1. 通過擴展ChannelProcessingFilter創建自定義ChannelProcessingFilter在applicationContext-security.xml文件中。我把完整的XML文件,供您參考(刪除不需要的部分。如果您需要在其他地方不要讓我知道)

      <?xml version="1.0" encoding="UTF-8"?> 
      
      <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" 
          xmlns:util="http://www.springframework.org/schema/util" 
          xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
            http://www.springframework.org/schema/security 
            http://www.springframework.org/schema/security/spring-security-3.0.3.xsd 
            http://www.springframework.org/schema/util 
            http://www.springframework.org/schema/util/spring-util.xsd"> 
      
          <security:http auto-config="false" 
            entry-point-ref="authenticationProcessingFilterEntryPoint" 
            access-decision-manager-ref="accessDecisionManager" > 
           <security:intercept-url pattern="/activ8/protectedCheckEligibility.html**" access="user" requires-channel="https"/> 
           <security:intercept-url pattern="/siteMap.html" access="ROLE_ANONYMOUS,user,admin" requires-channel="http"/> 
           <security:intercept-url pattern="/privacyPolicy.html" access="ROLE_ANONYMOUS,user,admin" requires-channel="http"/> 
           <!-- other urls configured over here --> 
           <security:intercept-url pattern="/*.jsp" access="ROLE_ANONYMOUS,admin,user" requires-channel="https"/> 
           <security:intercept-url pattern="/**/*.html**" access="ROLE_ANONYMOUS,user,admin" requires-channel="https"/> 
           <security:intercept-url pattern="/fb_activities.html**" access="parent" />  
      
           <security:remember-me key="appfuseRocks" /> 
           <security:custom-filter position="SWITCH_USER_FILTER" ref="careSwitchUserProcessingFilter"/> 
           <security:custom-filter position="FORM_LOGIN_FILTER" ref="myCustomAuthenticationProcessingFilter"/> 
           <!-- configured the custom channel filter over here --> 
           <security:custom-filter position="CHANNEL_FILTER" ref="myChannelProcessingFilter"/> 
          </security:http> 
      
          <bean id="myChannelProcessingFilter" class="com.my.webapp.filter.myChannelProcessingFilter"> 
           <property name="channelDecisionManager" ref="channelDecisionManager" /> 
           <property name="securityMetadataSource"> 
            <security:filter-security-metadata-source path-type="ant"> 
             <security:intercept-url pattern="/**" access="REQUIRES_INSECURE_CHANNEL" /> 
            </security:filter-security-metadata-source> 
           </property> 
          </bean> 
      
          <bean id="channelDecisionManager" class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl"> 
           <property name="channelProcessors"> 
            <list> 
             <ref bean="secureChannelProcessor"/> 
            </list> 
           </property> 
          </bean> 
      
          <bean id="secureChannelProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor"> 
           <property name="entryPoint" ref="secureChannelEntryPoint"/> 
           <!-- <property name="portMapper" ref="portMapper" /> --> 
           <property name="secureKeyword" value="REQUIRES_SECURE_CHANNEL"/> 
          </bean> 
      
          <bean id="secureChannelEntryPoint" class="com.my.webapp.filter.RetryWithHttpsEntryPoint"/> 
      
          <!-- lot of other configuratons... removed --> 
      
      </beans> 
      

    我收到以下錯誤,當我嘗試運行我的雄貓:

     
    ERROR 2011-12-26 21:13:21,569 [ina].[localhost].[/]]: Exception sending context initialized event to listener instance of class com.kajeet.webapp.listener.StartupListener 
    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Filter beans '' and 'Root bean: class [org.springframework.security.web.access.channel.ChannelProcessingFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null' 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 and avoiding the use of . 
        Offending resource: ServletContext resource [/WEB-INF/applicationContext-security.xml] 
        at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68) 
        at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85) 
        at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:72) 
        at org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.checkFilterChainOrder(HttpSecurityBeanDefinitionParser.java:196) 
        at org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.parse(HttpSecurityBeanDefinitionParser.java:132) 
        at org.springframework.security.config.SecurityNamespaceHandler.parse(SecurityNamespaceHandler.java:86) 
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1335) 
        at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1325) 
        at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:135) 
        at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:93) 
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493) 
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390) 
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334) 
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302) 
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143) 
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178) 
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149) 
        at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:124) 
        at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:93) 
        at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130) 
        at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467) 
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397) 
        at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:276) 
        at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:197) 
        at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47) 
        at com.kajeet.webapp.listener.StartupListener.contextInitialized(StartupListener.java:51) 
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3764) 
        at org.apache.catalina.core.StandardContext.start(StandardContext.java:4216) 
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:760) 
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:740) 
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:544) 
        at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:920) 
        at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:883) 
        at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:492) 
        at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1138) 
        at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311) 
        at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:120) 
        at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1022) 
        at org.apache.catalina.core.StandardHost.start(StandardHost.java:736) 
        at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1014) 
        at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443) 
        at org.apache.catalina.core.StandardService.start(StandardService.java:448) 
        at org.apache.catalina.core.StandardServer.start(StandardServer.java:700) 
        at org.apache.catalina.startup.Catalina.start(Catalina.java:552) 
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
        at java.lang.reflect.Method.invoke(Method.java:597) 
        at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:295) 
        at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:433) 
    

    我也重寫了其他過濾器,它並沒有抱怨那些。這個應用程序以前運行得很好。我們有這個額外的要求,因此我添加了新的過濾器,並遇到這樣的錯誤。

    我試過的第二種方法就是在XML中配置默認​​的ChannelProcessingFilter,因爲在Spring 3.0中過濾器會自動調用,我的印象是我可以在XML文件中配置它們,Spring會自動加載它們,但它沒有:

    <?xml version="1.0" encoding="UTF-8"?> 
    
        <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" 
         xmlns:util="http://www.springframework.org/schema/util" 
         xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
           http://www.springframework.org/schema/security 
           http://www.springframework.org/schema/security/spring-security-3.0.3.xsd 
           http://www.springframework.org/schema/util 
           http://www.springframework.org/schema/util/spring-util.xsd"> 
    
         <security:http auto-config="false" 
           entry-point-ref="authenticationProcessingFilterEntryPoint" 
           access-decision-manager-ref="accessDecisionManager" > 
          <security:intercept-url pattern="/activ8/protectedCheckEligibility.html**" access="user" requires-channel="https"/> 
          <security:intercept-url pattern="/siteMap.html" access="ROLE_ANONYMOUS,user,admin" requires-channel="http"/> 
          <security:intercept-url pattern="/privacyPolicy.html" access="ROLE_ANONYMOUS,user,admin" requires-channel="http"/> 
          <!-- other urls configured over here --> 
          <security:intercept-url pattern="/*.jsp" access="ROLE_ANONYMOUS,admin,user" requires-channel="https"/> 
          <security:intercept-url pattern="/**/*.html**" access="ROLE_ANONYMOUS,user,admin" requires-channel="https"/> 
          <security:intercept-url pattern="/fb_activities.html**" access="parent" />  
    
          <security:remember-me key="appfuseRocks" /> 
          <security:custom-filter position="SWITCH_USER_FILTER" ref="careSwitchUserProcessingFilter"/> 
          <security:custom-filter position="FORM_LOGIN_FILTER" ref="myCustomAuthenticationProcessingFilter"/> 
         </security:http> 
    
         <bean id="channelDecisionManager" class="org.springframework.security.securechannel.ChannelDecisionManagerImpl"> 
          <property name="channelProcessors"> 
           <list> 
            <ref bean="secureChannelProcessor"/> 
            <ref bean="insecureChannelProcessor"/> 
           </list> 
          </property> 
         </bean> 
    
         <bean id="secureChannelProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor"/> 
         <bean id="insecureChannelProcessor" class="org.springframework.security.web.access.channel.InsecureChannelProcessor"/> 
    
         <!-- lot of other configuratons... removed --> 
    
        </beans> 
    

    任何幫助將肯定會讚賞。我不是Spring專家,但是我已經做了一些工作,一兩個指針肯定可以幫助我解決這個問題。謝謝你在前進

  • +0

    在進一步研究,我知道這不需要擴展ChannelProcessingFilter,我發現的問題是SecureChannelProce ssor啓動兩次,一次使用默認值,第二次使用屬於XML的自定義值。應用規則時,只需使用具有默認值的那個規則。好像我錯過了一個非常小的鏈接。 – 2011-12-27 16:53:11

    回答

    2

    解決方案:

    的問題是,我們不能兼得的安全性:HTTP還有myChannelProcessingFilter(我已經覆蓋了一個),以應對安全的訪問參數: intercept-url,因此我刪除了http標記,並在myChannelProcessingFilter中添加了我想要處理的訪問內容。解決它的XML如下

    <?xml version="1.0" encoding="UTF-8"?> 
    
    <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" 
        xmlns:util="http://www.springframework.org/schema/util" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
          http://www.springframework.org/schema/security 
          http://www.springframework.org/schema/security/spring-security-3.0.3.xsd 
          http://www.springframework.org/schema/util 
          http://www.springframework.org/schema/util/spring-util.xsd"> 
    
        <!-- 
         The http element responsible for creating a FilterChainProxy and the filter beans which it uses. 
         Common problems like incorrect filter ordering are no longer an issue as the filter positions are predefined. 
        --> 
        <security:http auto-config="false" 
          entry-point-ref="authenticationProcessingFilterEntryPoint" 
          access-decision-manager-ref="accessDecisionManager" > 
    
         <security:custom-filter position="CHANNEL_FILTER" ref="channelProcessingFilter"/> 
    
         <security:intercept-url pattern="/*.html*" access="ROLE_ANONYMOUS,admin,user" /> 
         <security:intercept-url pattern="/*.jsp" access="ROLE_ANONYMOUS,admin,user" /> 
         <security:intercept-url pattern="/**/*.html**" access="ROLE_ANONYMOUS,user,admin" /> 
    
    
        </security:http> 
    
        <bean id="channelProcessingFilter" class="org.springframework.security.web.access.channel.ChannelProcessingFilter"> 
         <property name="channelDecisionManager" ref="channelDecisionManager"/> 
         <property name="securityMetadataSource"> 
         <security:filter-security-metadata-source path-type="ant"> 
          <security:intercept-url pattern="/*.jsp**" access="REQUIRES_SECURE_CHANNEL" /> 
          <security:intercept-url pattern="/**/*.html**" access="REQUIRES_SECURE_CHANNEL" /> 
         </security:filter-security-metadata-source> 
         </property> 
        </bean> 
    
        <bean id="channelDecisionManager" class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl"> 
         <property name="channelProcessors"> 
          <list> 
           <ref bean="secureProcessor"/> 
           <ref bean="insecureProcessor"/> 
          </list> 
         </property> 
        </bean> 
    
        <bean id="secureProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor" > 
         <property name="entryPoint" ref="retryWithHttps"/> 
        </bean> 
    
        <bean id="insecureProcessor" class="org.springframework.security.web.access.channel.InsecureChannelProcessor"> 
         <property name="entryPoint" ref="retryWithHttp"/> 
        </bean> 
    
        <bean id="retryWithHttps" class="com.my.webapp.filter.RetryWithHttpsEntryPoint" /> 
        <bean id="retryWithHttp" class="com.my.webapp.filter.RetryWithHttpEntryPoint" /> 
    
    </beans> 
    
    0

    我發現了另一種方法來實現同樣的事情,代碼和複雜性更少。您可以簡單地使用BeanPostProcessor來獲取SecureChannelProcessorInsecureChannelProcessor,然後在其上設置您自己的入口點。那樣的話,你仍然可以在其他東西上使用默認值。

    了BeanPostProcessor:

    @Component 
    public class ChannelProcessorsPostProcessor implements BeanPostProcessor { 
    
        @Override 
        public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException { 
    
         if (bean instanceof SecureChannelProcessor) ((SecureChannelProcessor)bean).setEntryPoint(new MyEntryRetryPoint("https://", 443)); 
         else if (bean instanceof InsecureChannelProcessor) ((InsecureChannelProcessor)bean).setEntryPoint(new MyEntryRetryPoint("http://", 80)); 
    
         return bean; 
        } 
    
        @Override 
        public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException { 
    
         return bean; 
        } 
    } 
    
    0

    我認爲這是更好地寫出了重定向策略:

    @Component 
    public class PermanentRedirectStrategy implements RedirectStrategy { 
        private boolean contextRelative; 
    
        @Override 
        public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException { 
         response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); 
         response.setHeader("Location", response.encodeRedirectURL(calculateRedirectUrl(request.getContextPath(), url))); 
        } 
    
        /** 
        * Unfortunately DefaultRedirectStrategy.calculateRedirectUrl is private 
        * If this weren't the case, we could extend this class from DefaultRedirectStrategy 
        * to use its method directly without copying it 
        */ 
        private String calculateRedirectUrl(String contextPath, String url) { 
         if (!UrlUtils.isAbsoluteUrl(url)) { 
          if (contextRelative) { 
           return url; 
          } else { 
           return contextPath + url; 
          } 
         } 
    
         // Full URL, including http(s):// 
    
         if (!contextRelative) { 
          return url; 
         } 
    
         // Calculate the relative URL from the fully qualified URL, minus the last 
         // occurence of the scheme and base context 
         url = url.substring(url.lastIndexOf("://") + 3); // strip off scheme 
         url = url.substring(url.indexOf(contextPath) + contextPath.length()); 
    
         if (url.length() > 1 && url.charAt(0) == '/') { 
          url = url.substring(1); 
         } 
    
         return url; 
        } 
    } 
    

    ,然後將其設置爲現有的切入點:

    @Component 
    public class ChannelProcessorsPostProcessor implements BeanPostProcessor { 
        @Autowired 
        private RedirectStrategy permanentRedirectStrategy; 
    
        @Override 
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
         ChannelEntryPoint entryPoint = null; 
    
         if (bean instanceof SecureChannelProcessor) { 
          entryPoint = ((SecureChannelProcessor) bean).getEntryPoint(); 
         } else if (bean instanceof InsecureChannelProcessor) { 
          entryPoint = ((InsecureChannelProcessor) bean).getEntryPoint(); 
         } 
    
         if (entryPoint != null && AbstractRetryEntryPoint.class.isAssignableFrom(entryPoint.getClass())) { 
          ((AbstractRetryEntryPoint) entryPoint).setRedirectStrategy(permanentRedirectStrategy); 
         } 
    
         return bean; 
        } 
    
        @Override 
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
         return bean; 
        } 
    }