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.authz;
020    
021    import org.apache.shiro.config.ConfigurationException;
022    import org.apache.shiro.util.StringUtils;
023    import org.apache.shiro.web.util.WebUtils;
024    
025    import javax.servlet.ServletRequest;
026    import javax.servlet.ServletResponse;
027    import javax.servlet.http.HttpServletRequest;
028    import java.io.IOException;
029    
030    /**
031     * A Filter that requires the request to be on a specific port, and if not, redirects to the same URL on that port.
032     * <p/>
033     * Example config:
034     * <pre>
035     * [filters]
036     * port.port = 80
037     * <p/>
038     * [urls]
039     * /some/path/** = port
040     * # override for just this path:
041     * /another/path/** = port[8080]
042     * </pre>
043     *
044     * @since 1.0
045     */
046    public class PortFilter extends AuthorizationFilter {
047    
048        public static final int DEFAULT_HTTP_PORT = 80;
049        public static final String HTTP_SCHEME = "http";
050    
051        private int port = DEFAULT_HTTP_PORT;
052    
053        public int getPort() {
054            return port;
055        }
056    
057        public void setPort(int port) {
058            this.port = port;
059        }
060    
061        protected int toPort(Object mappedValue) {
062            String[] ports = (String[]) mappedValue;
063            if (ports == null || ports.length == 0) {
064                return getPort();
065            }
066            if (ports.length > 1) {
067                throw new ConfigurationException("PortFilter can only be configured with a single port.  You have " +
068                        "configured " + ports.length + ": " + StringUtils.toString(ports));
069            }
070            return Integer.parseInt(ports[0]);
071        }
072    
073        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
074            int requiredPort = toPort(mappedValue);
075            int requestPort = request.getServerPort();
076            return requiredPort == requestPort;
077        }
078    
079        protected String getScheme(String requestScheme, int port) {
080            if (port == DEFAULT_HTTP_PORT) {
081                return HTTP_SCHEME;
082            } else if (port == SslFilter.DEFAULT_HTTPS_PORT) {
083                return SslFilter.HTTPS_SCHEME;
084            } else {
085                return requestScheme;
086            }
087        }
088    
089        /**
090         * Redirects the request to the same exact incoming URL, but with the port listed in the filter's configuration.
091         *
092         * @param request     the incoming <code>ServletRequest</code>
093         * @param response    the outgoing <code>ServletResponse</code>
094         * @param mappedValue the config specified for the filter in the matching request's filter chain.
095         * @return {@code false} always to force a redirect.
096         */
097        @Override
098        protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
099    
100            //just redirect to the specified port:
101            int port = toPort(mappedValue);
102    
103            String scheme = getScheme(request.getScheme(), port);
104    
105            StringBuilder sb = new StringBuilder();
106            sb.append(scheme).append("://");
107            sb.append(request.getServerName());
108            if (port != DEFAULT_HTTP_PORT && port != SslFilter.DEFAULT_HTTPS_PORT) {
109                sb.append(":");
110                sb.append(port);
111            }
112            if (request instanceof HttpServletRequest) {
113                sb.append(WebUtils.toHttp(request).getRequestURI());
114                String query = WebUtils.toHttp(request).getQueryString();
115                if (query != null) {
116                    sb.append("?").append(query);
117                }
118            }
119    
120            WebUtils.issueRedirect(request, response, sb.toString());
121    
122            return false;
123        }
124    }