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