2011-12-27 12 views
9

El comportamiento predeterminado de los Channel Processors es hacer un sendRedirect (que se redirecciona temporalmente con el código 302). Necesito cambiar este comportamiento para que se realice una redirección permanente (301) en lugar de 302. Traté de hacer lo siguiente:Anular ChannelProcessingFilter con Spring Security 3.0.5 no funciona

  1. Crear una ChannelProcessingFilter personalizada mediante la extensión de la 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. 
    } 
    
  2. Crear una costumbre EntryPoint extendiendo el 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); 
        } 
    } 
    
  3. configurar los mismos en el archivo applicationContext-security.xml. Estoy poniendo el archivo XML completo para su referencia (eliminación de las partes que no son necesarios. Si necesita las otras partes hágamelo saber)

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

estoy recibiendo siguientes errores cuando trato de ejecutar mi tomcat:

 
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) 

También he reemplazado a otros filtros y no se queja de eso. Esta aplicación funcionaba perfectamente bien antes. Teníamos este requisito adicional y, por lo tanto, agregué el nuevo filtro y me encontré con dichos errores.

El segundo enfoque que probé es simplemente configurar el ChannelProcessingFilter predeterminado en el XML, ya que en Spring 3.0 los filtros se llaman automáticamente, tuve la impresión de que puedo configurarlos en un archivo XML y Spring los cargará automáticamente, pero no:

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

Cualquier ayuda será definitivamente apreciada. No soy un profesional de Spring, pero he trabajado un poco, un puntero o dos definitivamente pueden ayudarme a resolverlo. Gracias de antemano

+0

En estudios posteriores , Llegué a saber que esto no requiere extender ChannelProcessingFilter, el problema que encontré fue que SecureChannelProce ssor se initala dos veces, una vez con los valores predeterminados y la segunda vez con valores personalizados que forman parte del XML. Al aplicar reglas, solo usa la que tiene valores predeterminados. Parece que me falta un enlace muy pequeño en alguna parte. –

Respuesta

2

Solución:

El problema es que no podemos tener tanto la seguridad: http, así como la myChannelProcessingFilter (la que había anulado) para lidiar con el argumento de acceso de la seguridad: intercept-url, por lo tanto, eliminé la etiqueta http y agregué el elemento de acceso en myChannelProcessingFilter, donde quería que se procesara. El XML que lo resolvió es el siguiente

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

Encontré otra manera de lograr lo mismo con mucho menos código y complejidad. Simplemente puede usar un BeanPostProcessor para obtener SecureChannelProcessor y InsecureChannelProcessor y luego establecer su propio punto de entrada en ellos. De esta forma, aún puede usar los valores predeterminados en todo lo demás.

El 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

Creo que es mejor escribir una estrategia de redireccionamiento:

@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; 
    } 
} 

, y luego poner al punto de entrada existente:

@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; 
    } 
}