后端解决跨域问题

package cops.com.deppon.notice.restful.service;


import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;


import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class SimpleCORSFilter extends OncePerRequestFilter {


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (request.getHeader(HttpHeader.ORIGIN) != null) {
            boolean preFlightRequest = "OPTIONS".equals(request.getMethod())
                    && request.getHeader(HttpHeader.ACCESS_CONTROL_REQUEST_METHOD) != null;
            this.processRequest(request, response, preFlightRequest);
            if (preFlightRequest) {
                return;
            }
        }
        filterChain.doFilter(request, response);
    }


    protected void processRequest(HttpServletRequest request, HttpServletResponse response, boolean preFlightRequest)
            throws IOException {


        Map<String, String> reqHeaderFirstValMap = CorsUtils.createReqHeaderFirstValMap(request);


        // 同域情况下不需要跨域头
        if (CorsUtils.isSameOrigin(request, reqHeaderFirstValMap)) {
            logger.debug("Skip CORS processing: request is from same origin");
            return;
        }


        // 根据指定规则配置跨域头
        this.handleInternal(reqHeaderFirstValMap, request.getMethod(), response, preFlightRequest);
    }


    /** Handle the given request. */
    private void handleInternal(Map<String, String> reqHeaderFirstValMap, String requestMethod,
            HttpServletResponse response, boolean preFlightRequest) throws IOException {


        String allowOrigin = reqHeaderFirstValMap.get(HttpHeader.ORIGIN);


        Set<String> allowHeaders = preFlightRequest ?
                Collections.singleton(reqHeaderFirstValMap.get(HttpHeader.ACCESS_CONTROL_REQUEST_HEADERS)) :
                reqHeaderFirstValMap.keySet();


        String allowMethod = preFlightRequest ?
                reqHeaderFirstValMap.get(HttpHeader.ACCESS_CONTROL_REQUEST_METHOD) :
                requestMethod;


        response.addHeader(HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN, allowOrigin);
        response.addHeader(HttpHeader.VARY, HttpHeader.ORIGIN);
        response.addHeader(HttpHeader.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");


        if (preFlightRequest) {
            response.addHeader(HttpHeader.ACCESS_CONTROL_ALLOW_METHODS, allowMethod);
            if (!allowHeaders.isEmpty()) {
                response.addHeader(HttpHeader.ACCESS_CONTROL_ALLOW_HEADERS,
                        StringUtils.collectionToCommaDelimitedString(allowHeaders));
            }
        }
    }


    private interface HttpHeader {


        /**
         * The CORS {@code Access-Control-Request-Headers} request header field name.
         *
         * @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>
         */
        String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";


        /**
         * The CORS {@code Access-Control-Request-Method} request header field name.
         *
         * @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>
         */
        String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";


        /**
         * The HTTP {@code Origin} header field name.
         *
         * @see <a href="http://tools.ietf.org/html/rfc6454">RFC 6454</a>
         */
        String ORIGIN = "Origin";


        /**
         * The CORS {@code Access-Control-Allow-Origin} response header field name.
         *
         * @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>
         */
        String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";


        /**
         * The CORS {@code Access-Control-Allow-Credentials} response header field name.
         *
         * @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>
         */
        String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";


        /**
         * The CORS {@code Access-Control-Allow-Methods} response header field name.
         *
         * @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>
         */
        String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";


        /**
         * The CORS {@code Access-Control-Allow-Headers} response header field name.
         *
         * @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>
         */
        String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";


        /**
         * The HTTP {@code Vary} header field name.
         *
         * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.1.4">Section 7.1.4 of RFC 7231</a>
         */
        String VARY = "Vary";


    }


    private static abstract class CorsUtils {


        private static final String SCHEME_PATTERN = "([^:/?#]+):";


        private static final String USERINFO_PATTERN = "([^@\\[/?#]*)";


        private static final String HOST_IPV4_PATTERN = "[^\\[/?#:]*";


        private static final String HOST_IPV6_PATTERN = "\\[[\\p{XDigit}\\:\\.]*[%\\p{Alnum}]*\\]";


        private static final String HOST_PATTERN = "(" + HOST_IPV6_PATTERN + "|" + HOST_IPV4_PATTERN + ")";


        private static final String PORT_PATTERN = "(\\d*(?:\\{[^/]+?\\})?)";


        private static final String PATH_PATTERN = "([^?#]*)";


        private static final String QUERY_PATTERN = "([^#]*)";


        private static final String LAST_PATTERN = "(.*)";


        // Regex patterns that matches URIs. See RFC 3986, appendix B
        private static final Pattern URI_PATTERN = Pattern.compile(
                "^(" + SCHEME_PATTERN + ")?" + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN +
                        ")?" + ")?" + PATH_PATTERN + "(\\?" + QUERY_PATTERN + ")?" + "(#" + LAST_PATTERN + ")?");


        private static final Pattern FORWARDED_HOST_PATTERN = Pattern.compile("host=\"?([^;,\"]+)\"?");


        private static final Pattern FORWARDED_PROTO_PATTERN = Pattern.compile("proto=\"?([^;,\"]+)\"?");




        public static boolean isSameOrigin(HttpServletRequest request, Map<String, String> reqHeaderFirstValMap) {
            String actualScheme = request.getScheme();
            String actualHost = request.getServerName();
            int actualPort = request.getServerPort();
            String forwardedHeader = reqHeaderFirstValMap.get("Forwarded");
            if (StringUtils.hasText(forwardedHeader)) {
                String forwardedToUse = StringUtils.commaDelimitedListToStringArray(forwardedHeader)[0];
                Matcher matcher = FORWARDED_HOST_PATTERN.matcher(forwardedToUse);
                if (matcher.find()) {
                    actualHost = matcher.group(1).trim();
                }
                matcher = FORWARDED_PROTO_PATTERN.matcher(forwardedToUse);
                if (matcher.find()) {
                    actualScheme = matcher.group(1).trim();
                }
            } else {
                String hostHeader = reqHeaderFirstValMap.get("X-Forwarded-Host");
                if (StringUtils.hasText(hostHeader)) {
                    String hostToUse = StringUtils.tokenizeToStringArray(hostHeader, ",")[0];
                    int portSeparatorIdx = hostToUse.lastIndexOf(":");
                    if (portSeparatorIdx > hostToUse.lastIndexOf("]")) {
                        actualHost = hostToUse.substring(0, portSeparatorIdx);
                        actualPort = Integer.parseInt(hostToUse.substring(portSeparatorIdx + 1));
                    } else {
                        actualHost = hostToUse;
                        actualPort = -1;
                    }
                }
                String portHeader = reqHeaderFirstValMap.get("X-Forwarded-Port");
                if (StringUtils.hasText(portHeader)) {
                    actualPort = Integer.parseInt(StringUtils.commaDelimitedListToStringArray(portHeader)[0]);
                }
                String protocolHeader = reqHeaderFirstValMap.get("X-Forwarded-Proto");
                if (StringUtils.hasText(protocolHeader)) {
                    actualScheme = StringUtils.commaDelimitedListToStringArray(protocolHeader)[0];
                }
            }
            actualPort = resolvePort(actualPort, actualScheme);
            String origin = reqHeaderFirstValMap.get(HttpHeader.ORIGIN);
            Matcher matcher = URI_PATTERN.matcher(origin);
            if (matcher.matches()) {
                String originScheme = matcher.group(2);
                if (!StringUtils.hasLength(originScheme)) {
                    originScheme = null;
                }
                String originHost = matcher.group(6);
                int originPort = resolvePort(matcher.group(8), originScheme);
                return actualHost.equals(originHost) && actualPort == originPort;
            } else {
                throw new IllegalArgumentException("[" + origin + "] is not a valid \"Origin\" header value");
            }
        }


        private static int resolvePort(String port, String scheme) {
            if (StringUtils.hasLength(port)) {
                return resolvePort(Integer.parseInt(port), scheme);
            }
            return resolvePort(-1, scheme);
        }




        private static int resolvePort(int port, String scheme) {
            if (port == -1) {
                if ("https".equals(scheme) || "wss".equals(scheme)) {
                    port = 443;
                } else if ("http".equals(scheme) || "ws".equals(scheme)) {
                    port = 80;
                }
            }
            return port;
        }




        public static Map<String, String> createReqHeaderFirstValMap(HttpServletRequest request) {


            Map<String, String> reqHeaderFirstValMap = new LinkedHashMap<String, String>() {


                private Map<String, String> caseInsensitiveKeys = new HashMap<String, String>();


                @Override
                public String put(String key, String value) {
                    String oldKey = this.caseInsensitiveKeys.put(convertKey(key), key);
                    if (oldKey != null && !oldKey.equals(key)) {
                        super.remove(oldKey);
                    }
                    return super.put(key, value);
                }


                @Override
                public void putAll(Map<? extends String, ? extends String> map) {
                    if (map.isEmpty()) {
                        return;
                    }
                    for (Map.Entry<? extends String, ? extends String> entry : map.entrySet()) {
                        put(entry.getKey(), entry.getValue());
                    }
                }


                @Override
                public boolean containsKey(Object key) {
                    return (key instanceof String && this.caseInsensitiveKeys.containsKey(convertKey((String) key)));
                }


                @Override
                public String get(Object key) {
                    if (key instanceof String) {
                        String caseInsensitiveKey = this.caseInsensitiveKeys.get(convertKey((String) key));
                        if (caseInsensitiveKey != null) {
                            return super.get(caseInsensitiveKey);
                        }
                    }
                    return null;
                }


                // Overridden to avoid LinkedHashMap's own hash computation in its getOrDefault impl JDK 1.8
                public String getOrDefault(Object key, String defaultValue) {
                    if (key instanceof String) {
                        String caseInsensitiveKey = this.caseInsensitiveKeys.get(convertKey((String) key));
                        if (caseInsensitiveKey != null) {
                            return super.get(caseInsensitiveKey);
                        }
                    }
                    return defaultValue;
                }


                @Override
                public String remove(Object key) {
                    if (key instanceof String) {
                        String caseInsensitiveKey = this.caseInsensitiveKeys.remove(convertKey((String) key));
                        if (caseInsensitiveKey != null) {
                            return super.remove(caseInsensitiveKey);
                        }
                    }
                    return null;
                }


                @Override
                public void clear() {
                    this.caseInsensitiveKeys.clear();
                    super.clear();
                }


                @Override @SuppressWarnings("unchecked")
                public Object clone() {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    ObjectOutputStream out = null;
                    try {
                        // stream closed in the finally
                        out = new ObjectOutputStream(bos);
                        out.writeObject(this);
                    } catch (final IOException e) {
                        throw new UndeclaredThrowableException(e);
                    } finally {
                        try {
                            if (out != null) {
                                out.close();
                            }
                        } catch (final IOException e) { // NOPMD
                            // ignore close exception
                        }
                    }
                    ByteArrayInputStream bis =
                            new ByteArrayInputStream(bos.toByteArray());
                    ObjectInputStream ois = null;
                    try {
                        ois = new ObjectInputStream(bis);
                        return ois.readObject();
                    } catch (Exception e) {
                        throw new UndeclaredThrowableException(e);
                    } finally {
                        try {
                            if (ois != null) {
                                ois.close();
                            }
                        } catch (final IOException e) { // NOPMD
                            // ignore close exception
                        }
                    }
                }


                protected String convertKey(String key) {
                    return key.toLowerCase(Locale.ENGLISH);
                }
            };


            Enumeration<?> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = (String) headerNames.nextElement();
                reqHeaderFirstValMap.put(headerName,
                        (String) request.getHeaders(headerName).nextElement());
            }


            return Collections.unmodifiableMap(reqHeaderFirstValMap);
        }


    }


}

猜你喜欢

转载自blog.csdn.net/jiewenike/article/details/78796474