Переопределение ChannelProcessingFilter в Spring Security 3.0.5 не работает - PullRequest
9 голосов
/ 27 декабря 2011

Поведение по умолчанию для процессоров канала - это сделать sendRedirect (который является временным перенаправлением с кодом 302). Мне нужно изменить это поведение таким образом, чтобы вместо перенаправления 302 выполнялось перенаправление (301). Я пытался сделать следующее:

  1. Создайте пользовательский ChannelProcessingFilter, расширив 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. Создание пользовательской EntryPoint путем расширения 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. Настройте то же самое в файле 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)

Я также переопределил другие фильтры, и он не жалуется на них. Это приложение работало отлично раньше. У нас было это дополнительное требование, и поэтому я добавил новый фильтр и столкнулся с такими ошибками.

Второй подход, который я попробовал, - это просто настроить ChannelProcessingFilter по умолчанию в XML, поскольку в 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, но я проделал некоторую работу над этим, указатель или два могут определенно помочь мне решить эту проблему. Заранее спасибо

Ответы [ 3 ]

2 голосов
/ 19 января 2012

Решение:

Проблема в том, что у нас не может быть безопасности: 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 голосов
/ 29 января 2015

Я думаю, что лучше написать стратегию перенаправления:

@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;
    }
}
0 голосов
/ 11 января 2014

Я нашел другой способ добиться того же, с гораздо меньшим количеством кода и сложностью.Вы можете просто использовать BeanPostProcessor, чтобы получить SecureChannelProcessor и InsecureChannelProcessor, а затем установить для них собственную точку входа.Таким образом, вы все равно можете использовать значения по умолчанию для всего остального.

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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...