This is an automated email from the ASF dual-hosted git repository.

bdemers pushed a commit to branch 1.6.x
in repository https://gitbox.apache.org/repos/asf/shiro.git


The following commit(s) were added to refs/heads/1.6.x by this push:
     new dc194fc  Add a feature to allow for global filters
dc194fc is described below

commit dc194fc977ab6cfbf3c1ecb085e2bac5db14af6d
Author: Brian Demers <[email protected]>
AuthorDate: Tue Jul 7 21:06:35 2020 -0400

    Add a feature to allow for global filters
    
    Adds new filter to block invalid requests
---
 .../org/apache/shiro/guice/web/ShiroWebModule.java |  25 +++-
 .../apache/shiro/guice/web/ShiroWebModuleTest.java | 153 +++++++++++++++++++++
 .../autoconfigure/ShiroWebFilterConfiguration.java |   8 ++
 .../web/ConfiguredGlobalFiltersTest.groovy         | 104 ++++++++++++++
 .../web/DisabledGlobalFiltersTest.groovy           |  64 +++++++++
 .../web/ShiroWebSpringAutoConfigurationTest.groovy |  30 +++-
 .../ShiroWebAutoConfigurationTestApplication.java  |   4 +-
 .../shiro/spring/web/ShiroFilterFactoryBean.java   |  23 ++++
 .../web/config/AbstractShiroWebConfiguration.java  |   3 -
 .../AbstractShiroWebFilterConfiguration.java       |   9 +-
 .../web/config/ShiroWebFilterConfiguration.java    |   6 +
 .../config/ShiroWebFilterConfigurationTest.groovy  |   3 +-
 .../spring/web/ShiroFilterFactoryBeanTest.java     |   8 +-
 .../web/config/IniFilterChainResolverFactory.java  |  18 +++
 .../shiro/web/filter/InvalidRequestFilter.java     | 124 +++++++++++++++++
 .../apache/shiro/web/filter/mgt/DefaultFilter.java |   4 +-
 .../web/filter/mgt/DefaultFilterChainManager.java  |  37 ++++-
 .../shiro/web/filter/mgt/FilterChainManager.java   |  22 +++
 .../shiro/web/servlet/AbstractShiroFilter.java     |   1 +
 .../IniFilterChainResolverFactoryTest.groovy       |  26 ++++
 .../shiro/web/env/IniWebEnvironmentTest.groovy     |  69 ++++++++++
 .../web/filter/InvalidRequestFilterTest.groovy     | 106 ++++++++++++++
 .../mgt/DefaultFilterChainManagerTest.groovy       |  52 +++++++
 .../java/org/apache/shiro/web/env/FilterStub.java  |  32 +++--
 24 files changed, 900 insertions(+), 31 deletions(-)

diff --git 
a/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java 
b/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
index df95665..73ffc7b 100644
--- a/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
+++ b/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
@@ -30,6 +30,7 @@ import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.session.mgt.SessionManager;
 import org.apache.shiro.util.StringUtils;
 import org.apache.shiro.web.env.WebEnvironment;
+import org.apache.shiro.web.filter.InvalidRequestFilter;
 import org.apache.shiro.web.filter.PathMatchingFilter;
 import org.apache.shiro.web.filter.authc.AnonymousFilter;
 import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
@@ -87,7 +88,8 @@ public abstract class ShiroWebModule extends ShiroModule {
     public static final Key<SslFilter> SSL = Key.get(SslFilter.class);
     @SuppressWarnings({"UnusedDeclaration"})
     public static final Key<UserFilter> USER = Key.get(UserFilter.class);
-
+    @SuppressWarnings({"UnusedDeclaration"})
+    public static final Key<InvalidRequestFilter> INVALID_REQUEST = 
Key.get(InvalidRequestFilter.class);
 
     static final String NAME = "SHIRO";
 
@@ -124,6 +126,10 @@ public abstract class ShiroWebModule extends ShiroModule {
         };
     }
 
+    public List<FilterConfig<? extends Filter>> globalFilters() {
+        return Collections.singletonList(filterConfig(INVALID_REQUEST));
+    }
+
     @Override
     protected final void configureShiro() {
         bindBeanType(TypeLiteral.get(ServletContext.class), 
Key.get(ServletContext.class, Names.named(NAME)));
@@ -135,6 +141,12 @@ public abstract class ShiroWebModule extends ShiroModule {
 
         this.configureShiroWeb();
 
+        // add default matching route if not already set
+        if (!filterChains.containsKey("/**")) {
+            // no config, this will add only the global filters
+            this.addFilterChain("/**", new FilterConfig[0]);
+        }
+
         bind(FilterChainResolver.class).toProvider(new 
FilterChainResolverProvider(setupFilterChainConfigs()));
     }
 
@@ -153,8 +165,15 @@ public abstract class ShiroWebModule extends ShiroModule {
             // collect the keys used for this path
             List<Key<? extends Filter>> keysForPath = new ArrayList<Key<? 
extends Filter>>();
 
-            for (int i = 0; i < filterChain.getValue().length; i++) {
-                FilterConfig<? extends Filter> filterConfig = 
filterChain.getValue()[i];
+            List<FilterConfig<? extends Filter>> globalFilters = 
this.globalFilters();
+            FilterConfig<? extends Filter>[] pathFilters = 
filterChain.getValue();
+
+            // merge the global filters and the path specific filters
+            List<FilterConfig<? extends Filter>> filterConfigs = new 
ArrayList<>(globalFilters.size() + pathFilters.length);
+            filterConfigs.addAll(globalFilters);
+            filterConfigs.addAll(Arrays.asList(pathFilters));
+
+            for (FilterConfig<? extends Filter> filterConfig : filterConfigs) {
 
                 Key<? extends Filter> key = filterConfig.getKey();
                 String config = filterConfig.getConfigValue();
diff --git 
a/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
 
b/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
index 9931d01..6de5a75 100644
--- 
a/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
+++ 
b/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
@@ -24,6 +24,7 @@ import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.Provides;
 import com.google.inject.binder.AnnotatedBindingBuilder;
+import com.google.inject.name.Names;
 import org.apache.shiro.guice.ShiroModuleTest;
 import org.apache.shiro.env.Environment;
 import org.apache.shiro.mgt.SecurityManager;
@@ -31,6 +32,7 @@ import org.apache.shiro.realm.Realm;
 import org.apache.shiro.session.mgt.SessionManager;
 import org.apache.shiro.web.env.EnvironmentLoader;
 import org.apache.shiro.web.env.WebEnvironment;
+import org.apache.shiro.web.filter.InvalidRequestFilter;
 import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
 import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
 import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
@@ -55,7 +57,9 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import java.io.IOException;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
 
 import static org.easymock.EasyMock.*;
 import static org.junit.Assert.*;
@@ -212,29 +216,38 @@ public class ShiroWebModuleTest {
         FilterChain filterChain = simpleFilterChainResolver.getChain(request, 
null, null);
         assertThat(filterChain, instanceOf(SimpleFilterChain.class));
         Filter nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(InvalidRequestFilter.class));
+        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
         assertThat(nextFilter, instanceOf(FormAuthenticationFilter.class));
 
         // test the /test_custom_filter resource
         filterChain = simpleFilterChainResolver.getChain(request, null, null);
         assertThat(filterChain, instanceOf(SimpleFilterChain.class));
         nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(InvalidRequestFilter.class));
+        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
         assertThat(nextFilter, instanceOf(CustomFilter.class));
 
         // test the /test_authc_basic resource
         filterChain = simpleFilterChainResolver.getChain(request, null, null);
         assertThat(filterChain, instanceOf(SimpleFilterChain.class));
         nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(InvalidRequestFilter.class));
+        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
         assertThat(nextFilter, 
instanceOf(BasicHttpAuthenticationFilter.class));
 
         // test the /test_perms resource
         filterChain = simpleFilterChainResolver.getChain(request, null, null);
         assertThat(filterChain, instanceOf(SimpleFilterChain.class));
         nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+        assertThat(nextFilter, instanceOf(InvalidRequestFilter.class));
+        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
         assertThat(nextFilter, 
instanceOf(PermissionsAuthorizationFilter.class));
 
         // test the /multiple_configs resource
         filterChain = simpleFilterChainResolver.getChain(request, null, null);
         assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), 
instanceOf(InvalidRequestFilter.class));
         assertThat(getNextFilter((SimpleFilterChain) filterChain), 
instanceOf(FormAuthenticationFilter.class));
         assertThat(getNextFilter((SimpleFilterChain) filterChain), 
instanceOf(RolesAuthorizationFilter.class));
         assertThat(getNextFilter((SimpleFilterChain) filterChain), 
instanceOf(PermissionsAuthorizationFilter.class));
@@ -312,6 +325,146 @@ public class ShiroWebModuleTest {
         verify(servletContext, request);
     }
 
+    @Test
+    public void testDefaultPath() {
+
+        final ShiroModuleTest.MockRealm mockRealm = 
createMock(ShiroModuleTest.MockRealm.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+
+        
servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), 
EasyMock.anyObject());
+        
expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes();
+        expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
+        
expect(request.getAttribute("javax.servlet.include.path_info")).andReturn(null).anyTimes();
+        expect(request.getPathInfo()).andReturn(null).anyTimes();
+        
expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/test/foobar");
+        replay(servletContext, request);
+
+        Injector injector = Guice.createInjector(new 
ShiroWebModule(servletContext) {
+            @Override
+            protected void configureShiroWeb() {
+                bindRealm().to(ShiroModuleTest.MockRealm.class);
+                expose(FilterChainResolver.class);
+                // no paths configured
+            }
+
+            @Provides
+            public ShiroModuleTest.MockRealm createRealm() {
+                return mockRealm;
+            }
+        });
+
+        FilterChainResolver resolver = 
injector.getInstance(FilterChainResolver.class);
+        assertThat(resolver, instanceOf(SimpleFilterChainResolver.class));
+        SimpleFilterChainResolver simpleFilterChainResolver = 
(SimpleFilterChainResolver) resolver;
+
+        // test the /test_authc resource
+        FilterChain filterChain = simpleFilterChainResolver.getChain(request, 
null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), 
instanceOf(InvalidRequestFilter.class));
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), 
nullValue());
+
+        verify(servletContext, request);
+    }
+
+    @Test
+    public void testDisableGlobalFilters() {
+
+        final ShiroModuleTest.MockRealm mockRealm = 
createMock(ShiroModuleTest.MockRealm.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+
+        
servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), 
EasyMock.anyObject());
+        
expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes();
+        expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
+        
expect(request.getAttribute("javax.servlet.include.path_info")).andReturn(null).anyTimes();
+        expect(request.getPathInfo()).andReturn(null).anyTimes();
+        
expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/test/foobar");
+        replay(servletContext, request);
+
+        Injector injector = Guice.createInjector(new 
ShiroWebModule(servletContext) {
+            @Override
+            protected void configureShiroWeb() {
+                bindRealm().to(ShiroModuleTest.MockRealm.class);
+                expose(FilterChainResolver.class);
+                this.addFilterChain("/**", filterConfig(AUTHC));
+            }
+
+            @Override
+            public List<FilterConfig<? extends Filter>> globalFilters() {
+                return Collections.emptyList();
+            }
+
+            @Provides
+            public ShiroModuleTest.MockRealm createRealm() {
+                return mockRealm;
+            }
+        });
+
+        FilterChainResolver resolver = 
injector.getInstance(FilterChainResolver.class);
+        assertThat(resolver, instanceOf(SimpleFilterChainResolver.class));
+        SimpleFilterChainResolver simpleFilterChainResolver = 
(SimpleFilterChainResolver) resolver;
+
+        // test the /test_authc resource
+        FilterChain filterChain = simpleFilterChainResolver.getChain(request, 
null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), 
instanceOf(FormAuthenticationFilter.class));
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), 
nullValue());
+
+        verify(servletContext, request);
+    }
+
+    @Test
+    public void testChangeInvalidFilterConfig() {
+
+        final ShiroModuleTest.MockRealm mockRealm = 
createMock(ShiroModuleTest.MockRealm.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+
+        
servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), 
EasyMock.anyObject());
+        
expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes();
+        expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
+        
expect(request.getAttribute("javax.servlet.include.path_info")).andReturn(null).anyTimes();
+        expect(request.getPathInfo()).andReturn(null).anyTimes();
+        
expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/test/foobar");
+        replay(servletContext, request);
+
+        Injector injector = Guice.createInjector(new 
ShiroWebModule(servletContext) {
+            @Override
+            protected void configureShiroWeb() {
+
+                
bindConstant().annotatedWith(Names.named("shiro.blockBackslash")).to(false);
+
+                bindRealm().to(ShiroModuleTest.MockRealm.class);
+                expose(FilterChainResolver.class);
+                this.addFilterChain("/**", filterConfig(AUTHC));
+            }
+
+            @Provides
+            public ShiroModuleTest.MockRealm createRealm() {
+                return mockRealm;
+            }
+        });
+
+        FilterChainResolver resolver = 
injector.getInstance(FilterChainResolver.class);
+        assertThat(resolver, instanceOf(SimpleFilterChainResolver.class));
+        SimpleFilterChainResolver simpleFilterChainResolver = 
(SimpleFilterChainResolver) resolver;
+
+        // test the /test_authc resource
+        FilterChain filterChain = simpleFilterChainResolver.getChain(request, 
null, null);
+        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+
+        Filter invalidRequestFilter = getNextFilter((SimpleFilterChain) 
filterChain);
+        assertThat(invalidRequestFilter, 
instanceOf(InvalidRequestFilter.class));
+        assertFalse("Expected 'blockBackslash' to be false", 
((InvalidRequestFilter) invalidRequestFilter).isBlockBackslash());
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), 
instanceOf(FormAuthenticationFilter.class));
+        assertThat(getNextFilter((SimpleFilterChain) filterChain), 
nullValue());
+
+        verify(servletContext, request);
+    }
+
     private Filter getNextFilter(SimpleFilterChain filterChain) {
 
         Iterator<? extends Filter> filters = filterChain.getFilters();
diff --git 
a/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebFilterConfiguration.java
 
b/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebFilterConfiguration.java
index 69356d6..9a60a0c 100644
--- 
a/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebFilterConfiguration.java
+++ 
b/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebFilterConfiguration.java
@@ -28,6 +28,8 @@ import 
org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
+import java.util.List;
+
 /**
  * @since 1.4.0
  */
@@ -53,4 +55,10 @@ public class ShiroWebFilterConfiguration extends 
AbstractShiroWebFilterConfigura
 
         return filterRegistrationBean;
     }
+
+    @Bean(name = "globalFilters")
+    @ConditionalOnMissingBean
+    protected List<String> globalFilters() {
+        return super.globalFilters();
+    }
 }
diff --git 
a/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/ConfiguredGlobalFiltersTest.groovy
 
b/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/ConfiguredGlobalFiltersTest.groovy
new file mode 100644
index 0000000..34c89a4
--- /dev/null
+++ 
b/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/ConfiguredGlobalFiltersTest.groovy
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.spring.boot.autoconfigure.web
+
+import 
org.apache.shiro.spring.boot.autoconfigure.web.application.ShiroWebAutoConfigurationTestApplication
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean
+import org.apache.shiro.spring.web.config.AbstractShiroWebFilterConfiguration
+import org.apache.shiro.web.filter.InvalidRequestFilter
+import org.apache.shiro.web.filter.authz.PortFilter
+import org.apache.shiro.web.filter.mgt.DefaultFilter
+import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager
+import org.apache.shiro.web.filter.mgt.NamedFilterList
+import org.apache.shiro.web.servlet.AbstractShiroFilter
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.test.context.junit4.SpringRunner
+
+import static org.hamcrest.MatcherAssert.assertThat
+import static org.hamcrest.Matchers.*
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = [ShiroWebAutoConfigurationTestApplication, Config])
+
+class ConfiguredGlobalFiltersTest {
+
+    @Configuration
+    static class Config extends AbstractShiroWebFilterConfiguration {
+
+        @Bean
+        List<String> globalFilters() {
+            return [DefaultFilter.invalidRequest.name(), 
DefaultFilter.port.name()]
+        }
+
+        @Bean
+        @Override
+        ShiroFilterFactoryBean shiroFilterFactoryBean() {
+            ShiroFilterFactoryBean bean = super.shiroFilterFactoryBean()
+            InvalidRequestFilter invalidRequestFilter = new 
InvalidRequestFilter()
+            invalidRequestFilter.setBlockBackslash(false)
+            PortFilter portFilter = new PortFilter()
+            portFilter.setPort(9999)
+            bean.getFilters().put("invalidRequest", invalidRequestFilter)
+            bean.getFilters().put("port", portFilter)
+            return bean
+        }
+    }
+
+    @Autowired
+    private AbstractShiroFilter shiroFilter
+
+    @Test
+    void testGlobalFiltersConfigured() {
+        // make sure global chains are configured
+        assertThat shiroFilter.filterChainResolver.filterChainManager, 
instanceOf(DefaultFilterChainManager)
+        DefaultFilterChainManager filterChainManager = 
shiroFilter.filterChainResolver.filterChainManager
+
+        // default config set
+        assertThat filterChainManager.globalFilterNames, 
contains(DefaultFilter.invalidRequest.name(),
+                                                                  
DefaultFilter.port.name())
+        // default route configured
+        NamedFilterList allChain = filterChainManager.getChain("/**")
+        assertThat allChain, contains(
+                instanceOf(DefaultFilter.invalidRequest.filterClass),
+                instanceOf(DefaultFilter.port.filterClass))
+
+        InvalidRequestFilter invalidRequest = allChain.get(0)
+        assertThat "Expected invalidRequest.blockBackslash to be false", 
!invalidRequest.isBlockBackslash()
+        PortFilter portFilter = allChain.get(1) // an ugly line, but we want 
to make sure that we can override the filters
+        // defined in Shiro's DefaultFilter
+        assertThat portFilter.port, equalTo(9999)
+
+        // configured routes also contain global filters
+        NamedFilterList loginChain = filterChainManager.getChain("/login.html")
+        assertThat loginChain, contains(
+                instanceOf(DefaultFilter.invalidRequest.filterClass),
+                instanceOf(DefaultFilter.port.filterClass),
+                instanceOf(DefaultFilter.authc.filterClass)) // configured in 
ShiroWebAutoConfigurationTestApplication
+
+        assertThat loginChain.get(0), sameInstance(invalidRequest)
+        assertThat loginChain.get(1), sameInstance(portFilter)
+
+
+    }
+}
diff --git 
a/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/DisabledGlobalFiltersTest.groovy
 
b/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/DisabledGlobalFiltersTest.groovy
new file mode 100644
index 0000000..983b666
--- /dev/null
+++ 
b/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/DisabledGlobalFiltersTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.spring.boot.autoconfigure.web;
+
+import 
org.apache.shiro.spring.boot.autoconfigure.web.application.ShiroWebAutoConfigurationTestApplication
+import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager
+import org.apache.shiro.web.servlet.AbstractShiroFilter
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.test.context.junit4.SpringRunner
+
+import static org.hamcrest.MatcherAssert.assertThat
+import static org.hamcrest.Matchers.equalTo
+import static org.hamcrest.Matchers.instanceOf
+import static org.hamcrest.Matchers.nullValue
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = [ShiroWebAutoConfigurationTestApplication, Config])
+class DisabledGlobalFiltersTest {
+
+    @Configuration
+    static class Config {
+
+        @Bean
+        List<String> globalFilters() {
+            return []
+        }
+    }
+
+    @Autowired
+    private AbstractShiroFilter shiroFilter
+
+    @Test
+    void testGlobalFiltersDisabled() {
+        // make sure global chains are configured
+        assertThat shiroFilter.filterChainResolver.filterChainManager, 
instanceOf(DefaultFilterChainManager)
+        DefaultFilterChainManager filterChainManager = 
shiroFilter.filterChainResolver.filterChainManager
+
+        // default config set
+        assertThat filterChainManager.globalFilterNames, equalTo([])
+        // default route configured
+        assertThat filterChainManager.getChain("/**"), nullValue()
+    }
+}
diff --git 
a/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/ShiroWebSpringAutoConfigurationTest.groovy
 
b/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/ShiroWebSpringAutoConfigurationTest.groovy
index e69d052..ad19589 100644
--- 
a/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/ShiroWebSpringAutoConfigurationTest.groovy
+++ 
b/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/ShiroWebSpringAutoConfigurationTest.groovy
@@ -25,20 +25,26 @@ import 
org.apache.shiro.spring.boot.autoconfigure.web.application.ShiroWebAutoCo
 import org.apache.shiro.event.EventBus
 import org.apache.shiro.mgt.DefaultSecurityManager
 import org.apache.shiro.mgt.SecurityManager
+import org.apache.shiro.web.filter.mgt.DefaultFilter
+import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager
 import org.apache.shiro.web.mgt.WebSecurityManager
+import org.apache.shiro.web.servlet.AbstractShiroFilter
 import org.junit.Test
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests
 
-import static org.hamcrest.MatcherAssert.assertThat
+import static org.hamcrest.Matchers.contains
+import static org.hamcrest.Matchers.equalTo
 import static org.hamcrest.Matchers.instanceOf
 import static org.junit.Assert.*
+import static org.hamcrest.MatcherAssert.assertThat
+
 /**
  * @since 1.4.0
  */
 @SpringBootTest(classes = [ShiroWebAutoConfigurationTestApplication])
-public class ShiroWebSpringAutoConfigurationTest extends 
AbstractJUnit4SpringContextTests {
+class ShiroWebSpringAutoConfigurationTest extends 
AbstractJUnit4SpringContextTests {
 
     @Autowired
     private SecurityManager securityManager
@@ -52,8 +58,11 @@ public class ShiroWebSpringAutoConfigurationTest extends 
AbstractJUnit4SpringCon
     @Autowired
     private SubscribedListener subscribedListener
 
+    @Autowired
+    private AbstractShiroFilter shiroFilter
+
     @Test
-    public void testMinimalConfiguration() {
+    void testMinimalConfiguration() {
 
         // first do a quick check of the injected objects
         assertNotNull securityManager
@@ -61,11 +70,22 @@ public class ShiroWebSpringAutoConfigurationTest extends 
AbstractJUnit4SpringCon
 
         assertNotNull eventBusAwareObject
         assertNotNull eventBus
+        assertNotNull shiroFilter
         assertTrue(eventBus.registry.containsKey(subscribedListener))
         assertSame(eventBusAwareObject.getEventBus(), eventBus)
         assertSame(((DefaultSecurityManager)securityManager).getEventBus(), 
eventBus)
 
-    }
-
+        // make sure global chains are configured
+        assertThat shiroFilter.filterChainResolver.filterChainManager, 
instanceOf(DefaultFilterChainManager)
+        DefaultFilterChainManager filterChainManager = 
shiroFilter.filterChainResolver.filterChainManager
 
+        // default config set
+        assertThat filterChainManager.globalFilterNames, 
equalTo([DefaultFilter.invalidRequest.name()])
+        // default route configured
+        assertThat filterChainManager.getChain("/**"), 
contains(instanceOf(DefaultFilter.invalidRequest.filterClass))
+        // configured routes also contain global filters
+        assertThat filterChainManager.getChain("/login.html"), contains(
+                instanceOf(DefaultFilter.invalidRequest.filterClass),
+                instanceOf(DefaultFilter.authc.filterClass)) // configured in 
ShiroWebAutoConfigurationTestApplication
+    }
 }
diff --git 
a/support/spring-boot/spring-boot-web-starter/src/test/java/org/apache/shiro/spring/boot/autoconfigure/web/application/ShiroWebAutoConfigurationTestApplication.java
 
b/support/spring-boot/spring-boot-web-starter/src/test/java/org/apache/shiro/spring/boot/autoconfigure/web/application/ShiroWebAutoConfigurationTestApplication.java
index c1fdc43..8e778bb 100644
--- 
a/support/spring-boot/spring-boot-web-starter/src/test/java/org/apache/shiro/spring/boot/autoconfigure/web/application/ShiroWebAutoConfigurationTestApplication.java
+++ 
b/support/spring-boot/spring-boot-web-starter/src/test/java/org/apache/shiro/spring/boot/autoconfigure/web/application/ShiroWebAutoConfigurationTestApplication.java
@@ -55,7 +55,9 @@ public class ShiroWebAutoConfigurationTestApplication {
 
     @Bean
     ShiroFilterChainDefinition shiroFilterChainDefinition() {
-        return new DefaultShiroFilterChainDefinition();
+        DefaultShiroFilterChainDefinition chainDefinition = new 
DefaultShiroFilterChainDefinition();
+        chainDefinition.addPathDefinition("/login.html", "authc");
+        return chainDefinition;
     }
 
     @Bean
diff --git 
a/support/spring/src/main/java/org/apache/shiro/spring/web/ShiroFilterFactoryBean.java
 
b/support/spring/src/main/java/org/apache/shiro/spring/web/ShiroFilterFactoryBean.java
index 5f006a1..1065a63 100644
--- 
a/support/spring/src/main/java/org/apache/shiro/spring/web/ShiroFilterFactoryBean.java
+++ 
b/support/spring/src/main/java/org/apache/shiro/spring/web/ShiroFilterFactoryBean.java
@@ -25,8 +25,10 @@ import org.apache.shiro.util.Nameable;
 import org.apache.shiro.util.StringUtils;
 import org.apache.shiro.web.config.IniFilterChainResolverFactory;
 import org.apache.shiro.web.filter.AccessControlFilter;
+import org.apache.shiro.web.filter.InvalidRequestFilter;
 import org.apache.shiro.web.filter.authc.AuthenticationFilter;
 import org.apache.shiro.web.filter.authz.AuthorizationFilter;
+import org.apache.shiro.web.filter.mgt.DefaultFilter;
 import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
 import org.apache.shiro.web.filter.mgt.FilterChainManager;
 import org.apache.shiro.web.filter.mgt.FilterChainResolver;
@@ -41,7 +43,9 @@ import org.springframework.beans.factory.FactoryBean;
 import org.springframework.beans.factory.config.BeanPostProcessor;
 
 import javax.servlet.Filter;
+import java.util.ArrayList;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -121,6 +125,8 @@ public class ShiroFilterFactoryBean implements FactoryBean, 
BeanPostProcessor {
 
     private Map<String, Filter> filters;
 
+    private List<String> globalFilters;
+
     private Map<String, String> filterChainDefinitionMap; 
//urlPathExpression_to_comma-delimited-filter-chain-definition
 
     private String loginUrl;
@@ -131,6 +137,8 @@ public class ShiroFilterFactoryBean implements FactoryBean, 
BeanPostProcessor {
 
     public ShiroFilterFactoryBean() {
         this.filters = new LinkedHashMap<String, Filter>();
+        this.globalFilters = new ArrayList<>();
+        this.globalFilters.add(DefaultFilter.invalidRequest.name());
         this.filterChainDefinitionMap = new LinkedHashMap<String, String>(); 
//order matters!
     }
 
@@ -332,6 +340,14 @@ public class ShiroFilterFactoryBean implements 
FactoryBean, BeanPostProcessor {
     }
 
     /**
+     * Sets the list of filters that will be executed against every request.  
Defaults to the {@link InvalidRequestFilter} which will block known invalid 
request attacks.
+     * @param globalFilters the list of filters to execute before specific 
path filters.
+     */
+    public void setGlobalFilters(List<String> globalFilters) {
+        this.globalFilters = globalFilters;
+    }
+
+    /**
      * Lazily creates and returns a {@link AbstractShiroFilter} concrete 
instance via the
      * {@link #createInstance} method.
      *
@@ -388,6 +404,9 @@ public class ShiroFilterFactoryBean implements FactoryBean, 
BeanPostProcessor {
             }
         }
 
+        // set the global filters
+        manager.setGlobalFilters(this.globalFilters);
+
         //build up the chains:
         Map<String, String> chains = getFilterChainDefinitionMap();
         if (!CollectionUtils.isEmpty(chains)) {
@@ -398,6 +417,9 @@ public class ShiroFilterFactoryBean implements FactoryBean, 
BeanPostProcessor {
             }
         }
 
+        // create the default chain, to match anything the path matching would 
have missed
+        manager.createDefaultChain("/**"); // TODO this assumes ANT path 
matching, which might be OK here
+
         return manager;
     }
 
@@ -533,6 +555,7 @@ public class ShiroFilterFactoryBean implements FactoryBean, 
BeanPostProcessor {
                 throw new IllegalArgumentException("WebSecurityManager 
property cannot be null.");
             }
             setSecurityManager(webSecurityManager);
+
             if (resolver != null) {
                 setFilterChainResolver(resolver);
             }
diff --git 
a/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java
 
b/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java
index 6e2cad3..414d7b1 100644
--- 
a/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java
+++ 
b/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java
@@ -18,13 +18,10 @@
  */
 package org.apache.shiro.spring.web.config;
 
-import org.apache.shiro.config.Ini;
 import org.apache.shiro.mgt.RememberMeManager;
 import org.apache.shiro.mgt.SessionStorageEvaluator;
 import org.apache.shiro.mgt.SessionsSecurityManager;
 import org.apache.shiro.mgt.SubjectFactory;
-import org.apache.shiro.realm.Realm;
-import org.apache.shiro.realm.text.IniRealm;
 import org.apache.shiro.session.mgt.SessionManager;
 import org.apache.shiro.spring.config.AbstractShiroConfiguration;
 import org.apache.shiro.web.mgt.CookieRememberMeManager;
diff --git 
a/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebFilterConfiguration.java
 
b/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebFilterConfiguration.java
index e15d50d..11b961e 100644
--- 
a/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebFilterConfiguration.java
+++ 
b/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebFilterConfiguration.java
@@ -18,13 +18,15 @@
  */
 package org.apache.shiro.spring.web.config;
 
-
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.filter.mgt.DefaultFilter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 
 import javax.servlet.Filter;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -50,6 +52,10 @@ public class AbstractShiroWebFilterConfiguration {
     @Value("#{ @environment['shiro.unauthorizedUrl'] ?: null }")
     protected String unauthorizedUrl;
 
+    protected List<String> globalFilters() {
+        return Collections.singletonList(DefaultFilter.invalidRequest.name());
+    }
+
     protected ShiroFilterFactoryBean shiroFilterFactoryBean() {
         ShiroFilterFactoryBean filterFactoryBean = new 
ShiroFilterFactoryBean();
 
@@ -58,6 +64,7 @@ public class AbstractShiroWebFilterConfiguration {
         filterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
 
         filterFactoryBean.setSecurityManager(securityManager);
+        filterFactoryBean.setGlobalFilters(globalFilters());
         
filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition.getFilterChainMap());
         filterFactoryBean.setFilters(filterMap);
 
diff --git 
a/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebFilterConfiguration.java
 
b/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebFilterConfiguration.java
index 72a4ecb..0ddcab1 100644
--- 
a/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebFilterConfiguration.java
+++ 
b/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebFilterConfiguration.java
@@ -22,6 +22,8 @@ import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
+import java.util.List;
+
 /**
  * @since 1.4.0
  */
@@ -34,4 +36,8 @@ public class ShiroWebFilterConfiguration extends 
AbstractShiroWebFilterConfigura
         return super.shiroFilterFactoryBean();
     }
 
+    @Bean(name = "globalFilters")
+    protected List<String> globalFilters() {
+        return super.globalFilters();
+    }
 }
diff --git 
a/support/spring/src/test/groovy/org/apache/shiro/spring/config/ShiroWebFilterConfigurationTest.groovy
 
b/support/spring/src/test/groovy/org/apache/shiro/spring/config/ShiroWebFilterConfigurationTest.groovy
index 68870dd..bd7e25c 100644
--- 
a/support/spring/src/test/groovy/org/apache/shiro/spring/config/ShiroWebFilterConfigurationTest.groovy
+++ 
b/support/spring/src/test/groovy/org/apache/shiro/spring/config/ShiroWebFilterConfigurationTest.groovy
@@ -24,6 +24,7 @@ import org.apache.shiro.spring.web.ShiroFilterFactoryBean
 import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition
 import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition
 import org.apache.shiro.spring.web.config.ShiroWebFilterConfiguration
+import org.apache.shiro.web.filter.InvalidRequestFilter
 import org.apache.shiro.web.filter.mgt.FilterChainManager
 import org.junit.Test
 import org.springframework.beans.factory.annotation.Autowired
@@ -66,7 +67,7 @@ class ShiroWebFilterConfigurationTest extends 
AbstractJUnit4SpringContextTests {
         // create the filter chain manager
         FilterChainManager filterChainManager = 
shiroFilterFactoryBean.createFilterChainManager()
         // lookup the chain by name
-        assertThat filterChainManager.getChain("/test-me"), 
contains(instanceOf(ExpectedTestFilter))
+        assertThat filterChainManager.getChain("/test-me"), 
contains(instanceOf(InvalidRequestFilter), instanceOf(ExpectedTestFilter))
     }
 
     @Configuration
diff --git 
a/support/spring/src/test/java/org/apache/shiro/spring/web/ShiroFilterFactoryBeanTest.java
 
b/support/spring/src/test/java/org/apache/shiro/spring/web/ShiroFilterFactoryBeanTest.java
index b2ba586..603d1c4 100644
--- 
a/support/spring/src/test/java/org/apache/shiro/spring/web/ShiroFilterFactoryBeanTest.java
+++ 
b/support/spring/src/test/java/org/apache/shiro/spring/web/ShiroFilterFactoryBeanTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.shiro.spring.web;
 
+import org.apache.shiro.web.filter.InvalidRequestFilter;
 import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
 import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
 import org.apache.shiro.web.filter.mgt.NamedFilterList;
@@ -55,11 +56,12 @@ public class ShiroFilterFactoryBeanTest {
         DefaultFilterChainManager fcManager = (DefaultFilterChainManager) 
resolver.getFilterChainManager();
         NamedFilterList chain = fcManager.getChain("/test");
         assertNotNull(chain);
-        assertEquals(chain.size(), 2);
+        assertEquals(chain.size(), 3);
         Filter[] filters = new Filter[chain.size()];
         filters = chain.toArray(filters);
-        assertTrue(filters[0] instanceof DummyFilter);
-        assertTrue(filters[1] instanceof FormAuthenticationFilter);
+        assertTrue(filters[0] instanceof InvalidRequestFilter); // global 
filter
+        assertTrue(filters[1] instanceof DummyFilter);
+        assertTrue(filters[2] instanceof FormAuthenticationFilter);
     }
 
     /**
diff --git 
a/web/src/main/java/org/apache/shiro/web/config/IniFilterChainResolverFactory.java
 
b/web/src/main/java/org/apache/shiro/web/config/IniFilterChainResolverFactory.java
index fb9f822..7118164 100644
--- 
a/web/src/main/java/org/apache/shiro/web/config/IniFilterChainResolverFactory.java
+++ 
b/web/src/main/java/org/apache/shiro/web/config/IniFilterChainResolverFactory.java
@@ -24,6 +24,7 @@ import org.apache.shiro.config.IniSecurityManagerFactory;
 import org.apache.shiro.config.ReflectionBuilder;
 import org.apache.shiro.util.CollectionUtils;
 import org.apache.shiro.util.Factory;
+import org.apache.shiro.web.filter.mgt.DefaultFilter;
 import org.apache.shiro.web.filter.mgt.FilterChainManager;
 import org.apache.shiro.web.filter.mgt.FilterChainResolver;
 import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
@@ -32,7 +33,9 @@ import org.slf4j.LoggerFactory;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterConfig;
+import java.util.Collections;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -49,6 +52,8 @@ public class IniFilterChainResolverFactory extends 
IniFactorySupport<FilterChain
 
     private FilterConfig filterConfig;
 
+    private List<String> globalFilters = 
Collections.singletonList(DefaultFilter.invalidRequest.name());
+
     public IniFilterChainResolverFactory() {
         super();
     }
@@ -70,6 +75,14 @@ public class IniFilterChainResolverFactory extends 
IniFactorySupport<FilterChain
         this.filterConfig = filterConfig;
     }
 
+    public List<String> getGlobalFilters() {
+        return globalFilters;
+    }
+
+    public void setGlobalFilters(List<String> globalFilters) {
+        this.globalFilters = globalFilters;
+    }
+
     protected FilterChainResolver createInstance(Ini ini) {
         FilterChainResolver filterChainResolver = createDefaultInstance();
         if (filterChainResolver instanceof PathMatchingFilterChainResolver) {
@@ -121,9 +134,14 @@ public class IniFilterChainResolverFactory extends 
IniFactorySupport<FilterChain
         //add the filters to the manager:
         registerFilters(filters, manager);
 
+        manager.setGlobalFilters(getGlobalFilters());
+
         //urls section:
         section = ini.getSection(URLS);
         createChains(section, manager);
+
+        // create the default chain, to match anything the path matching would 
have missed
+        manager.createDefaultChain("/**"); // TODO this assumes ANT path 
matching
     }
 
     protected void registerFilters(Map<String, Filter> filters, 
FilterChainManager manager) {
diff --git 
a/web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java 
b/web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java
new file mode 100644
index 0000000..3d229e9
--- /dev/null
+++ b/web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.shiro.web.filter;
+
+import org.apache.shiro.web.util.WebUtils;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A request filter that blocks malicious requests. Invalid request will 
respond with a 400 response code.
+ * <p>
+ * This filter checks and blocks the request if the following characters are 
found in the request URI:
+ * <ul>
+ *     <li>Semicolon - can be disabled by setting {@code blockSemicolon = 
false}</li>
+ *     <li>Backslash - can be disabled by setting {@code blockBackslash = 
false}</li>
+ *     <li>Non-ASCII characters - can be disabled by setting {@code 
blockNonAscii = false}, the ability to disable this check will be removed in 
future version.</li>
+ * </ul>
+ *
+ * @see <a 
href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/firewall/StrictHttpFirewall.html";>This
 class was inspired by Spring Security StrictHttpFirewall</a>
+ * @since 1.6
+ */
+public class InvalidRequestFilter extends AccessControlFilter {
+
+    private static final List<String> SEMICOLON = 
Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B"));
+
+    private static final List<String> BACKSLASH = 
Collections.unmodifiableList(Arrays.asList("\\", "%5c", "%5C"));
+
+    private boolean blockSemicolon = true;
+
+    private boolean blockBackslash = true;
+
+    private boolean blockNonAscii = true;
+
+    @Override
+    protected boolean isAccessAllowed(ServletRequest request, ServletResponse 
response, Object mappedValue) throws Exception {
+        String uri = WebUtils.toHttp(request).getRequestURI();
+        return !containsSemicolon(uri)
+            && !containsBackslash(uri)
+            && !containsNonAsciiCharacters(uri);
+    }
+
+    @Override
+    protected boolean onAccessDenied(ServletRequest request, ServletResponse 
response) throws Exception {
+        WebUtils.toHttp(response).sendError(400, "Invalid request");
+        return false;
+    }
+
+    private boolean containsSemicolon(String uri) {
+        if (isBlockSemicolon()) {
+            return SEMICOLON.stream().anyMatch(uri::contains);
+        }
+        return false;
+    }
+
+    private boolean containsBackslash(String uri) {
+        if (isBlockBackslash()) {
+            return BACKSLASH.stream().anyMatch(uri::contains);
+        }
+        return false;
+    }
+
+    private boolean containsNonAsciiCharacters(String uri) {
+        if (isBlockNonAscii()) {
+            return !containsOnlyPrintableAsciiCharacters(uri);
+        }
+        return false;
+    }
+
+    private static boolean containsOnlyPrintableAsciiCharacters(String uri) {
+        int length = uri.length();
+        for (int i = 0; i < length; i++) {
+            char c = uri.charAt(i);
+            if (c < '\u0020' || c > '\u007e') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public boolean isBlockSemicolon() {
+        return blockSemicolon;
+    }
+
+    public void setBlockSemicolon(boolean blockSemicolon) {
+        this.blockSemicolon = blockSemicolon;
+    }
+
+    public boolean isBlockBackslash() {
+        return blockBackslash;
+    }
+
+    public void setBlockBackslash(boolean blockBackslash) {
+        this.blockBackslash = blockBackslash;
+    }
+
+    public boolean isBlockNonAscii() {
+        return blockNonAscii;
+    }
+
+    public void setBlockNonAscii(boolean blockNonAscii) {
+        this.blockNonAscii = blockNonAscii;
+    }
+}
diff --git 
a/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilter.java 
b/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilter.java
index 036f62f..be77f06 100644
--- a/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilter.java
+++ b/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilter.java
@@ -19,6 +19,7 @@
 package org.apache.shiro.web.filter.mgt;
 
 import org.apache.shiro.util.ClassUtils;
+import org.apache.shiro.web.filter.InvalidRequestFilter;
 import org.apache.shiro.web.filter.authc.*;
 import org.apache.shiro.web.filter.authz.*;
 import org.apache.shiro.web.filter.session.NoSessionCreationFilter;
@@ -48,7 +49,8 @@ public enum DefaultFilter {
     rest(HttpMethodPermissionFilter.class),
     roles(RolesAuthorizationFilter.class),
     ssl(SslFilter.class),
-    user(UserFilter.class);
+    user(UserFilter.class),
+    invalidRequest(InvalidRequestFilter.class);
 
     private final Class<? extends Filter> filterClass;
 
diff --git 
a/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java
 
b/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java
index a880108..f289a79 100644
--- 
a/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java
+++ 
b/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java
@@ -30,8 +30,10 @@ import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -52,17 +54,21 @@ public class DefaultFilterChainManager implements 
FilterChainManager {
 
     private Map<String, Filter> filters; //pool of filters available for 
creating chains
 
+    private List<String> globalFilterNames; // list of filters to prepend to 
every chain
+
     private Map<String, NamedFilterList> filterChains; //key: chain name, 
value: chain
 
     public DefaultFilterChainManager() {
         this.filters = new LinkedHashMap<String, Filter>();
         this.filterChains = new LinkedHashMap<String, NamedFilterList>();
+        this.globalFilterNames = new ArrayList<>();
         addDefaultFilters(false);
     }
 
     public DefaultFilterChainManager(FilterConfig filterConfig) {
         this.filters = new LinkedHashMap<String, Filter>();
         this.filterChains = new LinkedHashMap<String, NamedFilterList>();
+        this.globalFilterNames = new ArrayList<>();
         setFilterConfig(filterConfig);
         addDefaultFilters(true);
     }
@@ -115,6 +121,15 @@ public class DefaultFilterChainManager implements 
FilterChainManager {
         addFilter(name, filter, init, true);
     }
 
+    public void createDefaultChain(String chainName) {
+        // only create the defaultChain if we don't have a chain with this 
name already
+        // (the global filters will already be in that chain)
+        if (!getChainNames().contains(chainName) && 
!CollectionUtils.isEmpty(globalFilterNames)) {
+            // add each of global filters
+            globalFilterNames.stream().forEach(filterName -> 
addToChain(chainName, filterName));
+        }
+    }
+
     public void createChain(String chainName, String chainDefinition) {
         if (!StringUtils.hasText(chainName)) {
             throw new NullPointerException("chainName cannot be null or 
empty.");
@@ -124,7 +139,12 @@ public class DefaultFilterChainManager implements 
FilterChainManager {
         }
 
         if (log.isDebugEnabled()) {
-            log.debug("Creating chain [" + chainName + "] from String 
definition [" + chainDefinition + "]");
+            log.debug("Creating chain [" + chainName + "] with global filters 
" + globalFilterNames + " and from String definition [" + chainDefinition + 
"]");
+        }
+
+        // first add each of global filters
+        if (!CollectionUtils.isEmpty(globalFilterNames)) {
+            globalFilterNames.stream().forEach(filterName -> 
addToChain(chainName, filterName));
         }
 
         //parse the value by tokenizing it to get the resulting 
filter-specific config entries
@@ -273,6 +293,21 @@ public class DefaultFilterChainManager implements 
FilterChainManager {
         chain.add(filter);
     }
 
+    public void setGlobalFilters(List<String> globalFilterNames) throws 
ConfigurationException {
+        // validate each filter name
+        if (!CollectionUtils.isEmpty(globalFilterNames)) {
+            for (String filterName : globalFilterNames) {
+                Filter filter = filters.get(filterName);
+                if (filter == null) {
+                    throw new ConfigurationException("There is no filter with 
name '" + filterName +
+                                                     "' to apply to the global 
filters in the pool of available Filters.  Ensure a " +
+                                                     "filter with that 
name/path has first been registered with the addFilter method(s).");
+                }
+                this.globalFilterNames.add(filterName);
+            }
+        }
+    }
+
     protected void applyChainConfig(String chainName, Filter filter, String 
chainSpecificFilterConfig) {
         if (log.isDebugEnabled()) {
             log.debug("Attempting to apply path [" + chainName + "] to filter 
[" + filter + "] " +
diff --git 
a/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java 
b/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java
index ebbc716..268c723 100644
--- a/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java
+++ b/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java
@@ -22,6 +22,7 @@ import org.apache.shiro.config.ConfigurationException;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -165,6 +166,14 @@ public interface FilterChainManager {
     void createChain(String chainName, String chainDefinition);
 
     /**
+     * Creates a chain that should match any non-matched request paths, 
typically {@code /**} assuming an {@link AntPathMatcher} I used.
+     * @param chainName The name of the chain to create, likely {@code /**}.
+     * @since 1.6
+     * @see org.apache.shiro.lang.util.AntPathMatcher AntPathMatcher
+     */
+    void createDefaultChain(String chainName);
+
+    /**
      * Adds (appends) a filter to the filter chain identified by the given 
{@code chainName}.  If there is no chain
      * with the given name, a new one is created and the filter will be the 
first in the chain.
      *
@@ -195,4 +204,17 @@ public interface FilterChainManager {
      *                                  interface).
      */
     void addToChain(String chainName, String filterName, String 
chainSpecificFilterConfig) throws ConfigurationException;
+
+    /**
+     * Configures the set of named filters that will match all paths.  These 
filters will match BEFORE explicitly
+     * configured filter chains i.e. by calling {@link #createChain(String, 
String)}, {@link #addToChain(String, String)}, etc.
+     * <br>
+     * <strong>Filters configured in this list wll apply to ALL 
requests.</strong>
+     *
+     * @param globalFilterNames         the list of filter names to match ALL 
paths.
+     * @throws ConfigurationException   if one of the filter names is invalid 
and cannot be loaded from the set of
+     *                                  configured filters {@link 
#getFilters()}}.
+     * @since 1.6
+     */
+    void setGlobalFilters(List<String> globalFilterNames) throws 
ConfigurationException;
 }
diff --git 
a/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java 
b/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java
index 8f0d5bb..7e2ed55 100644
--- a/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java
+++ b/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java
@@ -404,6 +404,7 @@ public abstract class AbstractShiroFilter extends 
OncePerRequestFilter {
      * @since 1.0
      */
     protected FilterChain getExecutionChain(ServletRequest request, 
ServletResponse response, FilterChain origChain) {
+
         FilterChain chain = origChain;
 
         FilterChainResolver resolver = getFilterChainResolver();
diff --git 
a/web/src/test/groovy/org/apache/shiro/web/config/IniFilterChainResolverFactoryTest.groovy
 
b/web/src/test/groovy/org/apache/shiro/web/config/IniFilterChainResolverFactoryTest.groovy
index 6a4e276..ba9c57f 100644
--- 
a/web/src/test/groovy/org/apache/shiro/web/config/IniFilterChainResolverFactoryTest.groovy
+++ 
b/web/src/test/groovy/org/apache/shiro/web/config/IniFilterChainResolverFactoryTest.groovy
@@ -18,6 +18,10 @@
  */
 package org.apache.shiro.web.config
 
+import org.apache.shiro.web.filter.InvalidRequestFilter
+import org.apache.shiro.web.filter.mgt.DefaultFilter
+import org.hamcrest.Matchers
+
 import javax.servlet.Filter
 import javax.servlet.FilterConfig
 import javax.servlet.ServletContext
@@ -30,6 +34,7 @@ import org.junit.Test
 
 import static org.easymock.EasyMock.*
 import static org.junit.Assert.*
+import static org.hamcrest.MatcherAssert.assertThat
 
 /**
  * Unit tests for the {@link IniFilterChainResolverFactory} implementation.
@@ -57,6 +62,7 @@ class IniFilterChainResolverFactoryTest {
         assertNull factory.filterConfig
         factory.filterConfig = null
         assertNull factory.filterConfig
+        assertThat factory.globalFilters, 
Matchers.contains(DefaultFilter.invalidRequest.name())
     }
 
     @Test
@@ -74,6 +80,7 @@ class IniFilterChainResolverFactoryTest {
         factory = new IniFilterChainResolverFactory(ini)
         FilterChainResolver resolver = factory.getInstance()
         assertNotNull resolver
+        assertThat resolver.filterChainManager.globalFilterNames, 
Matchers.contains(DefaultFilter.invalidRequest.name())
     }
 
     @Test
@@ -173,4 +180,23 @@ class IniFilterChainResolverFactoryTest {
         assertTrue extractedFilters['filtersSectionFilter'] instanceof 
SslFilter
         assertTrue extractedFilters['mainSectionFilter'] instanceof 
FormAuthenticationFilter
     }
+
+    @Test
+    void testConfigureInvalidRequestFilter() {
+        Ini ini = new Ini()
+        ini.load("""
+        [main]
+        invalidRequest.blockBackslash = false
+        [urls]
+        /index.html = anon
+        """)
+        factory = new IniFilterChainResolverFactory(ini)
+        FilterChainResolver resolver = factory.getInstance()
+        assertNotNull resolver
+
+        def invalidRequestFilter = 
resolver.filterChainManager.getChain("/index.html").get(0) // this will be the 
invalidRequest filter
+
+        assertThat(invalidRequestFilter, 
Matchers.instanceOf(InvalidRequestFilter))
+        assertThat("blockSemicolon should be faluse", 
invalidRequestFilter.blockBackslash)
+    }
 }
diff --git 
a/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy 
b/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy
index 7dcb40b..cee3cfd 100644
--- a/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy
+++ b/web/src/test/groovy/org/apache/shiro/web/env/IniWebEnvironmentTest.groovy
@@ -22,8 +22,11 @@ import org.apache.shiro.config.CompositeBean
 import org.apache.shiro.config.Ini
 import org.apache.shiro.config.SimpleBean
 import org.apache.shiro.web.filter.mgt.DefaultFilter
+import org.apache.shiro.web.filter.mgt.FilterChainManager
+import org.hamcrest.Matchers
 import org.junit.Test
 
+import static org.hamcrest.MatcherAssert.assertThat
 import static org.junit.Assert.*
 
 /**
@@ -94,4 +97,70 @@ class IniWebEnvironmentTest {
 
         assertSame(compositeBean.simpleBean, simpleBean)
     }
+
+    @Test
+    void testDisableGlobalFilters() {
+        Ini ini = new Ini()
+        ini.load("""
+        [main]
+        filterChainResolver.globalFilters = null
+
+        [urls]
+        /index.html = anon
+        """)
+
+        def env = new IniWebEnvironment(ini:  ini)
+        env.init()
+        assertThat 
env.getFilterChainResolver().filterChainManager.globalFilterNames, 
Matchers.empty()
+    }
+
+    @Test
+    void testDefaultGlobalFilters() {
+        Ini ini = new Ini()
+        ini.load("""
+        [main]
+
+        [urls]
+        /index.html = anon
+        """)
+
+        def env = new IniWebEnvironment(ini:  ini)
+        env.init()
+        def resolver =  env.getFilterChainResolver()
+        FilterChainManager manager = resolver.filterChainManager
+        assertThat manager.globalFilterNames, 
Matchers.contains(DefaultFilter.invalidRequest.name())
+
+        assertThat manager.getChain("/index.html"), Matchers.contains(
+                Matchers.instanceOf(DefaultFilter.invalidRequest.filterClass),
+                Matchers.instanceOf(DefaultFilter.anon.filterClass))
+    }
+
+    @Test
+    void testCustomGlobalFilters() {
+        Ini ini = new Ini()
+        ini.load("""
+        [main]
+        stub = org.apache.shiro.web.env.FilterStub
+        filterChainResolver.globalFilters = port,invalidRequest,stub
+
+        [urls]
+        /index.html = authc
+        """)
+
+        def env = new IniWebEnvironment(ini:  ini)
+        env.init()
+        def resolver =  env.getFilterChainResolver()
+        FilterChainManager manager = resolver.filterChainManager
+        assertThat manager.globalFilterNames, Matchers.contains(
+                DefaultFilter.port.name(),
+                DefaultFilter.invalidRequest.name(),
+                "stub"
+        )
+
+        assertThat manager.getChain("/index.html"), Matchers.contains(
+                Matchers.instanceOf(DefaultFilter.port.filterClass),
+                Matchers.instanceOf(DefaultFilter.invalidRequest.filterClass),
+                Matchers.instanceOf(FilterStub),
+                Matchers.instanceOf(DefaultFilter.authc.filterClass))
+    }
 }
diff --git 
a/web/src/test/groovy/org/apache/shiro/web/filter/InvalidRequestFilterTest.groovy
 
b/web/src/test/groovy/org/apache/shiro/web/filter/InvalidRequestFilterTest.groovy
new file mode 100644
index 0000000..8d0b1c0
--- /dev/null
+++ 
b/web/src/test/groovy/org/apache/shiro/web/filter/InvalidRequestFilterTest.groovy
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.shiro.web.filter
+
+import org.junit.Test
+
+import javax.servlet.http.HttpServletRequest
+
+import static org.easymock.EasyMock.expect
+import static org.easymock.EasyMock.mock
+import static org.easymock.EasyMock.replay
+import static org.hamcrest.MatcherAssert.assertThat
+
+class InvalidRequestFilterTest {
+
+    @Test
+    void defaultConfig() {
+        InvalidRequestFilter filter = new InvalidRequestFilter()
+        assertThat "filter.blockBackslash expected to be true", 
filter.isBlockBackslash()
+        assertThat "filter.blockNonAscii expected to be true", 
filter.isBlockNonAscii()
+        assertThat "filter.blockSemicolon expected to be true", 
filter.isBlockSemicolon()
+    }
+
+    @Test
+    void testFilterBlocks() {
+        InvalidRequestFilter filter = new InvalidRequestFilter()
+        assertPathBlocked(filter, "/\\something")
+        assertPathBlocked(filter, "/%5csomething")
+        assertPathBlocked(filter, "/%5Csomething")
+        assertPathBlocked(filter, "/;something")
+        assertPathBlocked(filter, "/%3bsomething")
+        assertPathBlocked(filter, "/%3Bsomething")
+        assertPathBlocked(filter, "/\u0019something")
+    }
+
+    @Test
+    void testFilterAllowsBackslash() {
+        InvalidRequestFilter filter = new InvalidRequestFilter()
+        filter.setBlockBackslash(false)
+        assertPathAllowed(filter, "/\\something")
+        assertPathAllowed(filter, "/%5csomething")
+        assertPathAllowed(filter, "/%5Csomething")
+        assertPathBlocked(filter, "/;something")
+        assertPathBlocked(filter, "/%3bsomething")
+        assertPathBlocked(filter, "/%3Bsomething")
+        assertPathBlocked(filter, "/\u0019something")
+    }
+
+    @Test
+    void testFilterAllowsNonAscii() {
+        InvalidRequestFilter filter = new InvalidRequestFilter()
+        filter.setBlockNonAscii(false)
+        assertPathBlocked(filter, "/\\something")
+        assertPathBlocked(filter, "/%5csomething")
+        assertPathBlocked(filter, "/%5Csomething")
+        assertPathBlocked(filter, "/;something")
+        assertPathBlocked(filter, "/%3bsomething")
+        assertPathBlocked(filter, "/%3Bsomething")
+        assertPathAllowed(filter, "/\u0019something")
+    }
+    @Test
+    void testFilterAllowsSemicolon() {
+        InvalidRequestFilter filter = new InvalidRequestFilter()
+        filter.setBlockSemicolon(false)
+        assertPathBlocked(filter, "/\\something")
+        assertPathBlocked(filter, "/%5csomething")
+        assertPathBlocked(filter, "/%5Csomething")
+        assertPathAllowed(filter, "/;something")
+        assertPathAllowed(filter, "/%3bsomething")
+        assertPathAllowed(filter, "/%3Bsomething")
+        assertPathBlocked(filter, "/\u0019something")
+    }
+
+
+    static void assertPathBlocked(InvalidRequestFilter filter, String path) {
+        assertThat "Expected path '${path}', to be blocked", 
!filter.isAccessAllowed(mockRequest(path), null, null)
+    }
+
+    static void assertPathAllowed(InvalidRequestFilter filter, String path) {
+        assertThat "Expected path '${path}', to be allowed", 
filter.isAccessAllowed(mockRequest(path), null, null)
+    }
+
+    static HttpServletRequest mockRequest(String path) {
+        HttpServletRequest request = mock(HttpServletRequest)
+        expect(request.getRequestURI()).andReturn(path)
+        replay(request)
+        return request
+    }
+}
diff --git 
a/web/src/test/groovy/org/apache/shiro/web/filter/mgt/DefaultFilterChainManagerTest.groovy
 
b/web/src/test/groovy/org/apache/shiro/web/filter/mgt/DefaultFilterChainManagerTest.groovy
index 364300b..8733588 100644
--- 
a/web/src/test/groovy/org/apache/shiro/web/filter/mgt/DefaultFilterChainManagerTest.groovy
+++ 
b/web/src/test/groovy/org/apache/shiro/web/filter/mgt/DefaultFilterChainManagerTest.groovy
@@ -21,6 +21,7 @@ package org.apache.shiro.web.filter.mgt
 import org.apache.shiro.config.ConfigurationException
 import org.apache.shiro.web.filter.authz.SslFilter
 import org.apache.shiro.web.servlet.ShiroFilter
+import org.hamcrest.Matchers
 
 import javax.servlet.Filter
 import javax.servlet.FilterChain
@@ -31,6 +32,7 @@ import org.junit.Test
 
 import static org.easymock.EasyMock.*
 import static org.junit.Assert.*
+import static org.hamcrest.MatcherAssert.assertThat
 
 /**
  * Unit tests for the {@link DefaultFilterChainManager} implementation.
@@ -210,6 +212,56 @@ class DefaultFilterChainManagerTest {
         assertEquals(DefaultFilter.perms.getFilterClass(), filter.getClass());
     }
 
+    @Test
+    void testWithGlobalFilters() {
+        DefaultFilterChainManager manager = new DefaultFilterChainManager()
+        manager.setGlobalFilters(["invalidRequest", "port"])
+        assertThat(manager.filterChains, Matchers.anEmptyMap())
+
+        // add a chain
+        manager.createChain("test", "authc, roles[manager], 
perms[\"user:read,write:12345\"")
+
+        assertThat(manager.getChain("test"), Matchers.contains(
+                
Matchers.instanceOf(DefaultFilter.invalidRequest.getFilterClass()),
+                Matchers.instanceOf(DefaultFilter.port.getFilterClass()),
+                Matchers.instanceOf(DefaultFilter.authc.getFilterClass()),
+                Matchers.instanceOf(DefaultFilter.roles.getFilterClass()),
+                Matchers.instanceOf(DefaultFilter.perms.getFilterClass())
+        ))
+
+        // the  "default" chain doesn't exist until it is created
+        assertThat(manager.getChain("/**"), Matchers.nullValue())
+        // create it
+        manager.createDefaultChain("/**")
+        // verify it
+        assertThat(manager.getChain("/**"), Matchers.contains(
+                
Matchers.instanceOf(DefaultFilter.invalidRequest.getFilterClass()),
+                Matchers.instanceOf(DefaultFilter.port.getFilterClass())
+        ))
+    }
+
+    @Test
+    void addDefaultChainWithSameName() {
+
+        DefaultFilterChainManager manager = new DefaultFilterChainManager()
+        manager.setGlobalFilters(["invalidRequest", "port"])
+
+        // create a chain
+        manager.createChain("test", "authc")
+
+        // create the default chain with the same name
+        manager.createDefaultChain("test")
+
+        // since the "default" chain was created with the same name as an 
existing chain, we could end up adding the
+        // global filters to the chain twice, test to verify it is only once
+        assertThat(manager.getChain("test"), Matchers.contains(
+                
Matchers.instanceOf(DefaultFilter.invalidRequest.getFilterClass()),
+                Matchers.instanceOf(DefaultFilter.port.getFilterClass()),
+                Matchers.instanceOf(DefaultFilter.authc.getFilterClass())
+        ))
+
+    }
+
     /**
      * Helps assert <a 
href="https://issues.apache.org/jira/browse/SHIRO-429";>SHIRO-429</a>
      * @since 1.2.2
diff --git 
a/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebFilterConfiguration.java
 b/web/src/test/java/org/apache/shiro/web/env/FilterStub.java
similarity index 58%
copy from 
support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebFilterConfiguration.java
copy to web/src/test/java/org/apache/shiro/web/env/FilterStub.java
index 72a4ecb..1330974 100644
--- 
a/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebFilterConfiguration.java
+++ b/web/src/test/java/org/apache/shiro/web/env/FilterStub.java
@@ -16,22 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.shiro.spring.web.config;
+package org.apache.shiro.web.env;
 
-import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
 
-/**
- * @since 1.4.0
- */
-@Configuration
-public class ShiroWebFilterConfiguration extends 
AbstractShiroWebFilterConfiguration {
+public class FilterStub implements Filter {
 
-    @Bean
     @Override
-    protected ShiroFilterFactoryBean shiroFilterFactoryBean() {
-        return super.shiroFilterFactoryBean();
+    public void init(FilterConfig filterConfig) throws ServletException {
+
     }
 
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException {
+
+    }
+
+    @Override
+    public void destroy() {
+
+    }
 }

Reply via email to