001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.shiro.web.filter.mgt;
020    
021    import org.apache.shiro.util.AntPathMatcher;
022    import org.apache.shiro.util.PatternMatcher;
023    import org.apache.shiro.web.util.WebUtils;
024    import org.slf4j.Logger;
025    import org.slf4j.LoggerFactory;
026    
027    import javax.servlet.FilterChain;
028    import javax.servlet.FilterConfig;
029    import javax.servlet.ServletRequest;
030    import javax.servlet.ServletResponse;
031    
032    /**
033     * A {@code FilterChainResolver} that resolves {@link FilterChain}s based on url path
034     * matching, as determined by a configurable {@link #setPathMatcher(org.apache.shiro.util.PatternMatcher) PathMatcher}.
035     * <p/>
036     * This implementation functions by consulting a {@link org.apache.shiro.web.filter.mgt.FilterChainManager} for all configured filter chains (keyed
037     * by configured path pattern).  If an incoming Request path matches one of the configured path patterns (via
038     * the {@code PathMatcher}, the corresponding configured {@code FilterChain} is returned.
039     *
040     * @since 1.0
041     */
042    public class PathMatchingFilterChainResolver implements FilterChainResolver {
043    
044        private static transient final Logger log = LoggerFactory.getLogger(PathMatchingFilterChainResolver.class);
045    
046        private FilterChainManager filterChainManager;
047    
048        private PatternMatcher pathMatcher;
049    
050        public PathMatchingFilterChainResolver() {
051            this.pathMatcher = new AntPathMatcher();
052            this.filterChainManager = new DefaultFilterChainManager();
053        }
054    
055        public PathMatchingFilterChainResolver(FilterConfig filterConfig) {
056            this.pathMatcher = new AntPathMatcher();
057            this.filterChainManager = new DefaultFilterChainManager(filterConfig);
058        }
059    
060        /**
061         * Returns the {@code PatternMatcher} used when determining if an incoming request's path
062         * matches a configured filter chain.  Unless overridden, the
063         * default implementation is an {@link org.apache.shiro.util.AntPathMatcher AntPathMatcher}.
064         *
065         * @return the {@code PatternMatcher} used when determining if an incoming request's path
066         *         matches a configured filter chain.
067         */
068        public PatternMatcher getPathMatcher() {
069            return pathMatcher;
070        }
071    
072        /**
073         * Sets the {@code PatternMatcher} used when determining if an incoming request's path
074         * matches a configured filter chain.  Unless overridden, the
075         * default implementation is an {@link org.apache.shiro.util.AntPathMatcher AntPathMatcher}.
076         *
077         * @param pathMatcher the {@code PatternMatcher} used when determining if an incoming request's path
078         *                    matches a configured filter chain.
079         */
080        public void setPathMatcher(PatternMatcher pathMatcher) {
081            this.pathMatcher = pathMatcher;
082        }
083    
084        public FilterChainManager getFilterChainManager() {
085            return filterChainManager;
086        }
087    
088        @SuppressWarnings({"UnusedDeclaration"})
089        public void setFilterChainManager(FilterChainManager filterChainManager) {
090            this.filterChainManager = filterChainManager;
091        }
092    
093        public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
094            FilterChainManager filterChainManager = getFilterChainManager();
095            if (!filterChainManager.hasChains()) {
096                return null;
097            }
098    
099            String requestURI = getPathWithinApplication(request);
100    
101            //the 'chain names' in this implementation are actually path patterns defined by the user.  We just use them
102            //as the chain name for the FilterChainManager's requirements
103            for (String pathPattern : filterChainManager.getChainNames()) {
104    
105                // If the path does match, then pass on to the subclass implementation for specific checks:
106                if (pathMatches(pathPattern, requestURI)) {
107                    if (log.isTraceEnabled()) {
108                        log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  " +
109                                "Utilizing corresponding filter chain...");
110                    }
111                    return filterChainManager.proxy(originalChain, pathPattern);
112                }
113            }
114    
115            return null;
116        }
117    
118        /**
119         * Returns {@code true} if an incoming request path (the {@code path} argument)
120         * matches a configured filter chain path (the {@code pattern} argument), {@code false} otherwise.
121         * <p/>
122         * Simply delegates to
123         * <b><code>{@link #getPathMatcher() getPathMatcher()}.{@link org.apache.shiro.util.PatternMatcher#matches(String, String) matches(pattern,path)}</code></b>.
124         * Subclass implementors should think carefully before overriding this method, as typically a custom
125         * {@code PathMatcher} should be configured for custom path matching behavior instead.  Favor OO composition
126         * rather than inheritance to limit your exposure to Shiro implementation details which may change over time.
127         *
128         * @param pattern the pattern to match against
129         * @param path    the value to match with the specified {@code pattern}
130         * @return {@code true} if the request {@code path} matches the specified filter chain url {@code pattern},
131         *         {@code false} otherwise.
132         */
133        protected boolean pathMatches(String pattern, String path) {
134            PatternMatcher pathMatcher = getPathMatcher();
135            return pathMatcher.matches(pattern, path);
136        }
137    
138        /**
139         * Merely returns
140         * <code>WebUtils.{@link org.apache.shiro.web.util.WebUtils#getPathWithinApplication(javax.servlet.http.HttpServletRequest) getPathWithinApplication(request)}</code>
141         * and can be overridden by subclasses for custom request-to-application-path resolution behavior.
142         *
143         * @param request the incoming {@code ServletRequest}
144         * @return the request's path within the appliation.
145         */
146        protected String getPathWithinApplication(ServletRequest request) {
147            return WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
148        }
149    }