Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions samples/web/src/main/webapp/WEB-INF/shiro.ini
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,6 @@ goodguy = winnebago:drive:eagle5
/logout = logout
/account/** = authc
/remoting/** = authc, roles[b2bClient], perms["remote:invoke:lan,wan"]
/favicon.ico = anon
/style.css = anon
/** = authc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.apache.shiro.web.filter.authc.BearerHttpAuthenticationFilter;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.filter.authc.NoAccessFilter;
import org.apache.shiro.web.filter.authc.UserFilter;
import org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter;
import org.apache.shiro.web.filter.authz.IpFilter;
Expand All @@ -62,7 +63,6 @@
import java.util.List;
import java.util.Map;

@SuppressWarnings("checkstyle:JavadocVariable")
/**
* Sets up Shiro lifecycles within Guice, enables the injecting of Shiro objects, and binds a default
* {@link org.apache.shiro.web.mgt.WebSecurityManager},
Expand All @@ -73,21 +73,38 @@
* Also provides for the configuring of filter chains and binds a
* {@link org.apache.shiro.web.filter.mgt.FilterChainResolver} with that information.
*/
@SuppressWarnings("checkstyle:JavadocVariable")
public abstract class ShiroWebModule extends ShiroModule {
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<AnonymousFilter> ANON = Key.get(AnonymousFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<FormAuthenticationFilter> AUTHC = Key.get(FormAuthenticationFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<BasicHttpAuthenticationFilter> AUTHC_BASIC = Key.get(BasicHttpAuthenticationFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<BearerHttpAuthenticationFilter> AUTHC_BEARER = Key.get(BearerHttpAuthenticationFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<NoSessionCreationFilter> NO_SESSION_CREATION = Key.get(NoSessionCreationFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<LogoutFilter> LOGOUT = Key.get(LogoutFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<PermissionsAuthorizationFilter> PERMS = Key.get(PermissionsAuthorizationFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<PortFilter> PORT = Key.get(PortFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<HttpMethodPermissionFilter> REST = Key.get(HttpMethodPermissionFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<RolesAuthorizationFilter> ROLES = Key.get(RolesAuthorizationFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<SslFilter> SSL = Key.get(SslFilter.class);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<IpFilter> IP = Key.get(IpFilter.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);
@SuppressWarnings({"UnusedDeclaration"})
public static final Key<NoAccessFilter> NO_ACCESS = Key.get(NoAccessFilter.class);

static final String NAME = "SHIRO";

Expand All @@ -107,6 +124,7 @@ public static void bindGuiceFilter(Binder binder) {
binder.install(guiceFilterModule());
}

@SuppressWarnings({"UnusedDeclaration"})
public static void bindGuiceFilter(final String pattern, Binder binder) {
binder.install(guiceFilterModule(pattern));
}
Expand Down Expand Up @@ -343,6 +361,7 @@ protected static <T extends Filter> FilterConfig<T> filterConfig(TypeLiteral<T>
* @return A FilterConfig used to map a String path to this configuration.
* @since 1.4
*/
@SuppressWarnings({"UnusedDeclaration"})
protected static <T extends Filter> FilterConfig<T> filterConfig(Class<T> type, String configValue) {
return filterConfig(Key.get(type), configValue);
}
Expand Down Expand Up @@ -434,11 +453,13 @@ protected static <T extends PathMatchingFilter> Key<T> config(Key<T> baseKey, St
return new FilterConfigKey<T>(baseKey, configValue);
}

@SuppressWarnings({"UnusedDeclaration"})
@Deprecated
protected static <T extends PathMatchingFilter> Key<T> config(TypeLiteral<T> typeLiteral, String configValue) {
return config(Key.get(typeLiteral), configValue);
}

@SuppressWarnings({"UnusedDeclaration"})
@Deprecated
protected static <T extends PathMatchingFilter> Key<T> config(Class<T> type, String configValue) {
return config(Key.get(type), configValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ class ConfiguredGlobalFiltersTest {
NamedFilterList allChain = filterChainManager.getChain("/**")
assertThat allChain, contains(
instanceOf(DefaultFilter.invalidRequest.filterClass),
instanceOf(DefaultFilter.port.filterClass))
instanceOf(DefaultFilter.port.filterClass),
instanceOf(DefaultFilter.noAccess.filterClass))

InvalidRequestFilter invalidRequest = allChain.get(0)
assertThat "Expected invalidRequest.blockBackslash to be false", !invalidRequest.isBlockBackslash()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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.DefaultFilter
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager
import org.apache.shiro.web.servlet.AbstractShiroFilter
import org.junit.jupiter.api.Test
Expand All @@ -30,7 +31,9 @@ import org.springframework.context.annotation.Configuration
import org.springframework.test.context.junit.jupiter.SpringExtension

import static org.hamcrest.MatcherAssert.assertThat
import static org.hamcrest.Matchers.*
import static org.hamcrest.Matchers.contains
import static org.hamcrest.Matchers.equalTo
import static org.hamcrest.Matchers.instanceOf

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = [ShiroWebAutoConfigurationTestApplication, Config])
Expand All @@ -57,6 +60,6 @@ class DisabledGlobalFiltersTest {
// default config set
assertThat filterChainManager.globalFilterNames, equalTo([])
// default route configured
assertThat filterChainManager.getChain("/**"), nullValue()
assertThat filterChainManager.getChain("/**"), contains(instanceOf(DefaultFilter.noAccess.filterClass))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ class ShiroWebSpringAutoConfigurationTest extends AbstractJUnit4SpringContextTes
// default config set
assertThat filterChainManager.globalFilterNames, equalTo([DefaultFilter.invalidRequest.name()])
// default route configured
assertThat filterChainManager.getChain("/**"), contains(instanceOf(DefaultFilter.invalidRequest.filterClass))
assertThat filterChainManager.getChain("/**"), contains(
instanceOf(DefaultFilter.invalidRequest.filterClass),
instanceOf(DefaultFilter.noAccess.filterClass))
// configured routes also contain global filters
assertThat filterChainManager.getChain("/login.html"), contains(
instanceOf(DefaultFilter.invalidRequest.filterClass),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {
private String loginUrl;
private String successUrl;
private String unauthorizedUrl;
private boolean allowAccessByDefault;
private boolean caseInsensitive;

private AbstractShiroFilter instance;
Expand Down Expand Up @@ -309,6 +310,22 @@ public Map<String, Filter> getFilters() {
return filters;
}

/**
* @return {@code true} if the default filter chain will allow access if no other filter chain matches.
*/
public boolean isAllowAccessByDefault() {
return allowAccessByDefault;
}

/**
* Sets whether the default filter chain will allow access if no other filter chain matches.
*
* @param allowAccessByDefault {@code true} if the default filter chain will allow access
*/
public void setAllowAccessByDefault(boolean allowAccessByDefault) {
this.allowAccessByDefault = allowAccessByDefault;
}

/**
* Sets the filterName-to-Filter map of filters available for reference when creating
* {@link #setFilterChainDefinitionMap(java.util.Map) filter chain definitions}.
Expand Down Expand Up @@ -463,7 +480,11 @@ protected FilterChainManager createFilterChainManager() {

// create the default chain, to match anything the path matching would have missed
// TODO this assumes ANT path matching, which might be OK here
manager.createDefaultChain("/**");
if (isAllowAccessByDefault()) {
manager.createDefaultChain("/**", DefaultFilter.anon.name());
} else {
manager.createDefaultChain("/**", DefaultFilter.noAccess.name());
}

return manager;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
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;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
Expand Down Expand Up @@ -66,13 +65,12 @@ void testFilterDefinition() {
DefaultFilterChainManager fcManager = (DefaultFilterChainManager) resolver.getFilterChainManager();
NamedFilterList chain = fcManager.getChain("/test");
assertThat(chain).isNotNull();
assertThat(chain).hasSize(3);
assertThat(chain).hasSize(2);
Filter[] filters = new Filter[chain.size()];
filters = chain.toArray(filters);
// global filter
assertThat(filters[0] instanceof InvalidRequestFilter).isTrue();
assertThat(filters[1] instanceof DummyFilter).isTrue();
assertThat(filters[2] instanceof FormAuthenticationFilter).isTrue();
}

/**
Expand All @@ -93,7 +91,8 @@ void testFilterDefinitionWithInit() throws Exception {
expect(mockFilterConfig.getServletContext()).andReturn(mockServletContext).anyTimes();
HttpServletRequest mockRequest = createNiceMock(HttpServletRequest.class);
expect(mockRequest.getContextPath()).andReturn("/").anyTimes();
expect(mockRequest.getRequestURI()).andReturn("/").anyTimes();
expect(mockRequest.getRequestURI()).andReturn("/test").anyTimes();
expect(mockRequest.getServletPath()).andReturn("/test").anyTimes();
HttpServletResponse mockResponse = createNiceMock(HttpServletResponse.class);

replay(mockFilterConfig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
<property name="securityManager" ref="securityManager"/>
<property name="filterChainDefinitions">
<value>
/test = testFilter, authc
/test = testFilter
</value>
</property>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class IniFilterChainResolverFactory extends IniFactorySupport<FilterChain

private List<String> globalFilters = Collections.singletonList(DefaultFilter.invalidRequest.name());

private boolean allowAccessByDefault;
private boolean caseInsensitive = true;

public IniFilterChainResolverFactory() {
Expand Down Expand Up @@ -92,6 +93,14 @@ public void setGlobalFilters(List<String> globalFilters) {
this.globalFilters = globalFilters;
}

public boolean isAllowAccessByDefault() {
return allowAccessByDefault;
}

public void setAllowAccessByDefault(boolean allowAccessByDefault) {
this.allowAccessByDefault = allowAccessByDefault;
}

public boolean isCaseInsensitive() {
return caseInsensitive;
}
Expand Down Expand Up @@ -158,7 +167,11 @@ protected void buildChains(FilterChainManager manager, Ini ini) {

// create the default chain, to match anything the path matching would have missed
// TODO this assumes ANT path matching
manager.createDefaultChain("/**");
if (isAllowAccessByDefault()) {
manager.createDefaultChain("/**", DefaultFilter.anon.name());
} else {
manager.createDefaultChain("/**", DefaultFilter.noAccess.name());
}
}

protected void registerFilters(Map<String, Filter> filters, FilterChainManager manager) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.authc;

import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
Comment thread
lprimak marked this conversation as resolved.

/**
* A request filter that redirects all traffic to a login page. This filter is used as a catch-all to block requests
* that do not match existing filter patterns.
*/
public class NoAccessFilter extends AuthenticatingFilter {

private final Logger log = LoggerFactory.getLogger(NoAccessFilter.class);

@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
log.debug("Blocking access to request: '{}'", WebUtils.getPathWithinApplication(WebUtils.toHttp(request)));
saveRequestAndRedirectToLogin(request, response);
return false;
}

@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.apache.shiro.web.filter.authc.BearerHttpAuthenticationFilter;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.filter.authc.NoAccessFilter;
import org.apache.shiro.web.filter.authc.UserFilter;
import org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter;
import org.apache.shiro.web.filter.authz.IpFilter;
Expand Down Expand Up @@ -112,11 +113,14 @@ public enum DefaultFilter {
* user filter.
*/
user(UserFilter.class),

/**
* invalid request filter.
*/
invalidRequest(InvalidRequestFilter.class);
invalidRequest(InvalidRequestFilter.class),
/**
* no access filter.
*/
noAccess(NoAccessFilter.class);

private final Class<? extends Filter> filterClass;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,11 @@ public void addFilter(String name, Filter filter, boolean init) {
addFilter(name, filter, init, true);
}

public void createDefaultChain(String chainName) {
public void createDefaultChain(String chainName, String chainDefinition) {
// 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));
if (!getChainNames().contains(chainName)) {
createChain(chainName, chainDefinition);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,23 @@ public interface FilterChainManager {

/**
* Creates a chain that should match any non-matched request paths,
* typically {@code /**} assuming an {@link AntPathMatcher} I used.
* typically assuming an {@link AntPathMatcher} is used.
*
* @param chainName The name of the chain to create, likely {@code /**}.
* @see AntPathMatcher AntPathMatcher
* @since 1.6
*/
void createDefaultChain(String chainName);
void createDefaultChain(String chainName, String chainDefinition);

/**
* @since 1.6
* @deprecated use {@link FilterChainManager#createDefaultChain(String, String)} instead.
* @see AntPathMatcher AntPathMatcher
*/
@Deprecated
default void createDefaultChain(String chainName) {
createDefaultChain(chainName, DefaultFilter.noAccess.name());
}

/**
* Adds (appends) a filter to the filter chain identified by the given {@code chainName}. If there is no chain
Expand Down
Loading