Ajax跨域访问(二)

在之前的一篇博客《Ajax跨域访问》中简单的介绍了Ajax跨域访问能产生的原因并提供了解决方案,为了更好地处理跨域问题,今天为大家提供一种后端实现的解决方案,摘自Tomcat中对跨域的解决实现,废话不多说,上代码:

package com.jianggujin.web.util;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 跨域过滤器
 * 
 * @author jianggujin
 *
 */
public final class CorsFilter implements Filter
{

   // private static final StringManager sm =
   // StringManager.getManager(Constants.Package);

   /**
    * A {@link Collection} of origins consisting of zero or more origins that
    * are allowed access to the resource.
    */
   private final Collection<String> allowedOrigins;

   /**
    * Determines if any origin is allowed to make request.
    */
   private boolean anyOriginAllowed;

   /**
    * A {@link Collection} of methods consisting of zero or more methods that
    * are supported by the resource.
    */
   private final Collection<String> allowedHttpMethods;

   /**
    * A {@link Collection} of headers consisting of zero or more header field
    * names that are supported by the resource.
    */
   private final Collection<String> allowedHttpHeaders;

   /**
    * A {@link Collection} of exposed headers consisting of zero or more header
    * field names of headers other than the simple response headers that the
    * resource might use and can be exposed.
    */
   private final Collection<String> exposedHeaders;

   /**
    * A supports credentials flag that indicates whether the resource supports
    * user credentials in the request. It is true when the resource does and
    * false otherwise.
    */
   private boolean supportsCredentials;

   /**
    * Indicates (in seconds) how long the results of a pre-flight request can be
    * cached in a pre-flight result cache.
    */
   private long preflightMaxAge;

   /**
    * Determines if the request should be decorated or not.
    */
   private boolean decorateRequest;

   public CorsFilter()
   {
      this.allowedOrigins = new HashSet<>();
      this.allowedHttpMethods = new HashSet<>();
      this.allowedHttpHeaders = new HashSet<>();
      this.exposedHeaders = new HashSet<>();
   }

   @Override
   public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
         final FilterChain filterChain) throws IOException, ServletException
   {
      if (!(servletRequest instanceof HttpServletRequest) || !(servletResponse instanceof HttpServletResponse))
      {
         throw new ServletException("CORS doesn't support non-HTTP request or response");
      }

      // Safe to downcast at this point.
      HttpServletRequest request = (HttpServletRequest) servletRequest;
      HttpServletResponse response = (HttpServletResponse) servletResponse;

      // Determines the CORS request type.
      CorsFilter.CORSRequestType requestType = checkRequestType(request);

      // Adds CORS specific attributes to request.
      if (decorateRequest)
      {
         CorsFilter.decorateCORSProperties(request, requestType);
      }
      switch (requestType)
      {
      case SIMPLE:
         // Handles a Simple CORS request.
         this.handleSimpleCORS(request, response, filterChain);
         break;
      case ACTUAL:
         // Handles an Actual CORS request.
         this.handleSimpleCORS(request, response, filterChain);
         break;
      case PRE_FLIGHT:
         // Handles a Pre-flight CORS request.
         this.handlePreflightCORS(request, response, filterChain);
         break;
      case NOT_CORS:
         // Handles a Normal request that is not a cross-origin request.
         this.handleNonCORS(request, response, filterChain);
         break;
      default:
         // Handles a CORS request that violates specification.
         this.handleInvalidCORS(request, response, filterChain);
         break;
      }
   }

   @Override
   public void init(final FilterConfig filterConfig) throws ServletException
   {
      // Initialize defaults
      parseAndStore(DEFAULT_ALLOWED_ORIGINS, DEFAULT_ALLOWED_HTTP_METHODS, DEFAULT_ALLOWED_HTTP_HEADERS,
            DEFAULT_EXPOSED_HEADERS, DEFAULT_SUPPORTS_CREDENTIALS, DEFAULT_PREFLIGHT_MAXAGE, DEFAULT_DECORATE_REQUEST);

      if (filterConfig != null)
      {
         String configAllowedOrigins = filterConfig.getInitParameter(PARAM_CORS_ALLOWED_ORIGINS);
         String configAllowedHttpMethods = filterConfig.getInitParameter(PARAM_CORS_ALLOWED_METHODS);
         String configAllowedHttpHeaders = filterConfig.getInitParameter(PARAM_CORS_ALLOWED_HEADERS);
         String configExposedHeaders = filterConfig.getInitParameter(PARAM_CORS_EXPOSED_HEADERS);
         String configSupportsCredentials = filterConfig.getInitParameter(PARAM_CORS_SUPPORT_CREDENTIALS);
         String configPreflightMaxAge = filterConfig.getInitParameter(PARAM_CORS_PREFLIGHT_MAXAGE);
         String configDecorateRequest = filterConfig.getInitParameter(PARAM_CORS_REQUEST_DECORATE);

         parseAndStore(configAllowedOrigins, configAllowedHttpMethods, configAllowedHttpHeaders, configExposedHeaders,
               configSupportsCredentials, configPreflightMaxAge, configDecorateRequest);
      }
   }

   /**
    * Handles a CORS request of type {@link CORSRequestType}.SIMPLE.
    *
    * @param request
    *           The {@link HttpServletRequest} object.
    * @param response
    *           The {@link HttpServletResponse} object.
    * @param filterChain
    *           The {@link FilterChain} object.
    * @throws IOException
    * @throws ServletException
    * @see <a href="http://www.w3.org/TR/cors/#resource-requests">Simple
    *      Cross-Origin Request, Actual Request, and Redirects</a>
    */
   protected void handleSimpleCORS(final HttpServletRequest request, final HttpServletResponse response,
         final FilterChain filterChain) throws IOException, ServletException
   {

      CorsFilter.CORSRequestType requestType = checkRequestType(request);
      if (!(requestType == CorsFilter.CORSRequestType.SIMPLE || requestType == CorsFilter.CORSRequestType.ACTUAL))
      {
         throw new IllegalArgumentException("Expects a HttpServletRequest object of type ["
               + CorsFilter.CORSRequestType.SIMPLE + "] or [" + CorsFilter.CORSRequestType.ACTUAL + "]");
      }

      final String origin = request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN);
      final String method = request.getMethod();

      // Section 6.1.2
      if (!isOriginAllowed(origin))
      {
         handleInvalidCORS(request, response, filterChain);
         return;
      }

      if (!allowedHttpMethods.contains(method))
      {
         handleInvalidCORS(request, response, filterChain);
         return;
      }

      // Section 6.1.3
      // Add a single Access-Control-Allow-Origin header.
      if (anyOriginAllowed && !supportsCredentials)
      {
         // If resource doesn't support credentials and if any origin is
         // allowed
         // to make CORS request, return header with '*'.
         response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*");
      }
      else
      {
         // If the resource supports credentials add a single
         // Access-Control-Allow-Origin header, with the value of the Origin
         // header as value.
         response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, origin);
      }

      // Section 6.1.3
      // If the resource supports credentials, add a single
      // Access-Control-Allow-Credentials header with the case-sensitive
      // string "true" as value.
      if (supportsCredentials)
      {
         response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
      }

      // Section 6.1.4
      // If the list of exposed headers is not empty add one or more
      // Access-Control-Expose-Headers headers, with as values the header
      // field names given in the list of exposed headers.
      if ((exposedHeaders != null) && (exposedHeaders.size() > 0))
      {
         String exposedHeadersString = join(exposedHeaders, ",");
         response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, exposedHeadersString);
      }

      // Forward the request down the filter chain.
      filterChain.doFilter(request, response);
   }

   /**
    * Handles CORS pre-flight request.
    *
    * @param request
    *           The {@link HttpServletRequest} object.
    * @param response
    *           The {@link HttpServletResponse} object.
    * @param filterChain
    *           The {@link FilterChain} object.
    * @throws IOException
    * @throws ServletException
    */
   protected void handlePreflightCORS(final HttpServletRequest request, final HttpServletResponse response,
         final FilterChain filterChain) throws IOException, ServletException
   {

      CORSRequestType requestType = checkRequestType(request);
      if (requestType != CORSRequestType.PRE_FLIGHT)
      {
         throw new IllegalArgumentException("Expects a HttpServletRequest object of type ["
               + CORSRequestType.PRE_FLIGHT.name().toLowerCase(Locale.ENGLISH) + "]");
      }

      final String origin = request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN);

      // Section 6.2.2
      if (!isOriginAllowed(origin))
      {
         handleInvalidCORS(request, response, filterChain);
         return;
      }

      // Section 6.2.3
      String accessControlRequestMethod = request.getHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD);
      if (accessControlRequestMethod == null)
      {
         handleInvalidCORS(request, response, filterChain);
         return;
      }
      else
      {
         accessControlRequestMethod = accessControlRequestMethod.trim();
      }

      // Section 6.2.4
      String accessControlRequestHeadersHeader = request
            .getHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
      List<String> accessControlRequestHeaders = new LinkedList<>();
      if (accessControlRequestHeadersHeader != null && !accessControlRequestHeadersHeader.trim().isEmpty())
      {
         String[] headers = accessControlRequestHeadersHeader.trim().split(",");
         for (String header : headers)
         {
            accessControlRequestHeaders.add(header.trim().toLowerCase(Locale.ENGLISH));
         }
      }

      // Section 6.2.5
      if (!allowedHttpMethods.contains(accessControlRequestMethod))
      {
         handleInvalidCORS(request, response, filterChain);
         return;
      }

      // Section 6.2.6
      if (!accessControlRequestHeaders.isEmpty())
      {
         for (String header : accessControlRequestHeaders)
         {
            if (!allowedHttpHeaders.contains(header))
            {
               handleInvalidCORS(request, response, filterChain);
               return;
            }
         }
      }

      // Section 6.2.7
      if (supportsCredentials)
      {
         response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, origin);
         response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
      }
      else
      {
         if (anyOriginAllowed)
         {
            response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*");
         }
         else
         {
            response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, origin);
         }
      }

      // Section 6.2.8
      if (preflightMaxAge > 0)
      {
         response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE, String.valueOf(preflightMaxAge));
      }

      // Section 6.2.9
      response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS, accessControlRequestMethod);

      // Section 6.2.10
      if ((allowedHttpHeaders != null) && (!allowedHttpHeaders.isEmpty()))
      {
         response.addHeader(CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, join(allowedHttpHeaders, ","));
      }

      // Do not forward the request down the filter chain.
   }

   /**
    * Handles a request, that's not a CORS request, but is a valid request i.e.
    * it is not a cross-origin request. This implementation, just forwards the
    * request down the filter chain.
    *
    * @param request
    *           The {@link HttpServletRequest} object.
    * @param response
    *           The {@link HttpServletResponse} object.
    * @param filterChain
    *           The {@link FilterChain} object.
    * @throws IOException
    * @throws ServletException
    */
   private void handleNonCORS(final HttpServletRequest request, final HttpServletResponse response,
         final FilterChain filterChain) throws IOException, ServletException
   {
      // Let request pass.
      filterChain.doFilter(request, response);
   }

   /**
    * Handles a CORS request that violates specification.
    *
    * @param request
    *           The {@link HttpServletRequest} object.
    * @param response
    *           The {@link HttpServletResponse} object.
    * @param filterChain
    *           The {@link FilterChain} object.
    */
   private void handleInvalidCORS(final HttpServletRequest request, final HttpServletResponse response,
         final FilterChain filterChain)
   {
      // String origin = request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN);
      // String method = request.getMethod();
      // String accessControlRequestHeaders =
      // request.getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);

      response.setContentType("text/plain");
      response.setStatus(HttpServletResponse.SC_FORBIDDEN);
      response.resetBuffer();

      // if (log.isDebugEnabled())
      // {
      // // Debug so no need for i18n
      // StringBuilder message = new StringBuilder("Invalid CORS request;
      // Origin=");
      // message.append(origin);
      // message.append(";Method=");
      // message.append(method);
      // if (accessControlRequestHeaders != null)
      // {
      // message.append(";Access-Control-Request-Headers=");
      // message.append(accessControlRequestHeaders);
      // }
      // log.debug(message.toString());
      // }
   }

   @Override
   public void destroy()
   {
      // NOOP
   }

   /**
    * Decorates the {@link HttpServletRequest}, with CORS attributes.
    * <ul>
    * <li><b>cors.isCorsRequest:</b> Flag to determine if request is a CORS
    * request. Set to <code>true</code> if CORS request; <code>false</code>
    * otherwise.</li>
    * <li><b>cors.request.origin:</b> The Origin URL.</li>
    * <li><b>cors.request.type:</b> Type of request. Values: <code>simple</code>
    * or <code>preflight</code> or <code>not_cors</code> or
    * <code>invalid_cors</code></li>
    * <li><b>cors.request.headers:</b> Request headers sent as
    * 'Access-Control-Request-Headers' header, for pre-flight request.</li>
    * </ul>
    *
    * @param request
    *           The {@link HttpServletRequest} object.
    * @param corsRequestType
    *           The {@link CORSRequestType} object.
    */
   protected static void decorateCORSProperties(final HttpServletRequest request, final CORSRequestType corsRequestType)
   {
      if (request == null)
      {
         throw new IllegalArgumentException("HttpServletRequest object is null");
      }

      if (corsRequestType == null)
      {
         throw new IllegalArgumentException("CORSRequestType object is null");
      }

      switch (corsRequestType)
      {
      case SIMPLE:
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST, Boolean.TRUE);
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN,
               request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN));
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE,
               corsRequestType.name().toLowerCase(Locale.ENGLISH));
         break;
      case ACTUAL:
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST, Boolean.TRUE);
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN,
               request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN));
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE,
               corsRequestType.name().toLowerCase(Locale.ENGLISH));
         break;
      case PRE_FLIGHT:
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST, Boolean.TRUE);
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN,
               request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN));
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE,
               corsRequestType.name().toLowerCase(Locale.ENGLISH));
         String headers = request.getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
         if (headers == null)
         {
            headers = "";
         }
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS, headers);
         break;
      case NOT_CORS:
         request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST, Boolean.FALSE);
         break;
      default:
         // Don't set any attributes
         break;
      }
   }

   /**
    * Joins elements of {@link Set} into a string, where each element is
    * separated by the provided separator.
    *
    * @param elements
    *           The {@link Set} containing elements to join together.
    * @param joinSeparator
    *           The character to be used for separating elements.
    * @return The joined {@link String}; <code>null</code> if elements
    *         {@link Set} is null.
    */
   protected static String join(final Collection<String> elements, final String joinSeparator)
   {
      String separator = ",";
      if (elements == null)
      {
         return null;
      }
      if (joinSeparator != null)
      {
         separator = joinSeparator;
      }
      StringBuilder buffer = new StringBuilder();
      boolean isFirst = true;
      for (String element : elements)
      {
         if (!isFirst)
         {
            buffer.append(separator);
         }
         else
         {
            isFirst = false;
         }

         if (element != null)
         {
            buffer.append(element);
         }
      }

      return buffer.toString();
   }

   /**
    * Determines the request type.
    *
    * @param request
    */
   protected CORSRequestType checkRequestType(final HttpServletRequest request)
   {
      CORSRequestType requestType = CORSRequestType.INVALID_CORS;
      if (request == null)
      {
         throw new IllegalArgumentException("HttpServletRequest object is null");
      }
      String originHeader = request.getHeader(REQUEST_HEADER_ORIGIN);
      // Section 6.1.1 and Section 6.2.1
      if (originHeader != null)
      {
         if (originHeader.isEmpty())
         {
            requestType = CORSRequestType.INVALID_CORS;
         }
         else if (!isValidOrigin(originHeader))
         {
            requestType = CORSRequestType.INVALID_CORS;
         }
         else if (isLocalOrigin(request, originHeader))
         {
            return CORSRequestType.NOT_CORS;
         }
         else
         {
            String method = request.getMethod();
            if (method != null)
            {
               if ("OPTIONS".equals(method))
               {
                  String accessControlRequestMethodHeader = request
                        .getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD);
                  if (accessControlRequestMethodHeader != null && !accessControlRequestMethodHeader.isEmpty())
                  {
                     requestType = CORSRequestType.PRE_FLIGHT;
                  }
                  else if (accessControlRequestMethodHeader != null && accessControlRequestMethodHeader.isEmpty())
                  {
                     requestType = CORSRequestType.INVALID_CORS;
                  }
                  else
                  {
                     requestType = CORSRequestType.ACTUAL;
                  }
               }
               else if ("GET".equals(method) || "HEAD".equals(method))
               {
                  requestType = CORSRequestType.SIMPLE;
               }
               else if ("POST".equals(method))
               {
                  String mediaType = getMediaType(request.getContentType());
                  if (mediaType != null)
                  {
                     if (SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES.contains(mediaType))
                     {
                        requestType = CORSRequestType.SIMPLE;
                     }
                     else
                     {
                        requestType = CORSRequestType.ACTUAL;
                     }
                  }
               }
               else
               {
                  requestType = CORSRequestType.ACTUAL;
               }
            }
         }
      }
      else
      {
         requestType = CORSRequestType.NOT_CORS;
      }

      return requestType;
   }

   private boolean isLocalOrigin(HttpServletRequest request, String origin)
   {

      // Build scheme://host:port from request
      StringBuilder target = new StringBuilder();
      String scheme = request.getScheme();
      if (scheme == null)
      {
         return false;
      }
      else
      {
         scheme = scheme.toLowerCase(Locale.ENGLISH);
      }
      target.append(scheme);
      target.append("://");

      String host = request.getServerName();
      if (host == null)
      {
         return false;
      }
      target.append(host);

      int port = request.getServerPort();
      if ("http".equals(scheme) && port != 80 || "https".equals(scheme) && port != 443)
      {
         target.append(':');
         target.append(port);
      }

      return origin.equalsIgnoreCase(target.toString());
   }

   /*
    * Return the lower case, trimmed value of the media type from the content
    * type.
    */
   private String getMediaType(String contentType)
   {
      if (contentType == null)
      {
         return null;
      }
      String result = contentType.toLowerCase(Locale.ENGLISH);
      int firstSemiColonIndex = result.indexOf(';');
      if (firstSemiColonIndex > -1)
      {
         result = result.substring(0, firstSemiColonIndex);
      }
      result = result.trim();
      return result;
   }

   /**
    * Checks if the Origin is allowed to make a CORS request.
    *
    * @param origin
    *           The Origin.
    * @return <code>true</code> if origin is allowed; <code>false</code>
    *         otherwise.
    */
   private boolean isOriginAllowed(final String origin)
   {
      if (anyOriginAllowed)
      {
         return true;
      }

      // If 'Origin' header is a case-sensitive match of any of allowed
      // origins, then return true, else return false.
      return allowedOrigins.contains(origin);
   }

   /**
    * Parses each param-value and populates configuration variables. If a param
    * is provided, it overrides the default.
    *
    * @param allowedOrigins
    *           A {@link String} of comma separated origins.
    * @param allowedHttpMethods
    *           A {@link String} of comma separated HTTP methods.
    * @param allowedHttpHeaders
    *           A {@link String} of comma separated HTTP headers.
    * @param exposedHeaders
    *           A {@link String} of comma separated headers that needs to be
    *           exposed.
    * @param supportsCredentials
    *           "true" if support credentials needs to be enabled.
    * @param preflightMaxAge
    *           The amount of seconds the user agent is allowed to cache the
    *           result of the pre-flight request.
    * @throws ServletException
    */
   private void parseAndStore(final String allowedOrigins, final String allowedHttpMethods,
         final String allowedHttpHeaders, final String exposedHeaders, final String supportsCredentials,
         final String preflightMaxAge, final String decorateRequest) throws ServletException
   {
      if (allowedOrigins != null)
      {
         if (allowedOrigins.trim().equals("*"))
         {
            this.anyOriginAllowed = true;
         }
         else
         {
            this.anyOriginAllowed = false;
            Set<String> setAllowedOrigins = parseStringToSet(allowedOrigins);
            this.allowedOrigins.clear();
            this.allowedOrigins.addAll(setAllowedOrigins);
         }
      }

      if (allowedHttpMethods != null)
      {
         Set<String> setAllowedHttpMethods = parseStringToSet(allowedHttpMethods);
         this.allowedHttpMethods.clear();
         this.allowedHttpMethods.addAll(setAllowedHttpMethods);
      }

      if (allowedHttpHeaders != null)
      {
         Set<String> setAllowedHttpHeaders = parseStringToSet(allowedHttpHeaders);
         Set<String> lowerCaseHeaders = new HashSet<>();
         for (String header : setAllowedHttpHeaders)
         {
            String lowerCase = header.toLowerCase(Locale.ENGLISH);
            lowerCaseHeaders.add(lowerCase);
         }
         this.allowedHttpHeaders.clear();
         this.allowedHttpHeaders.addAll(lowerCaseHeaders);
      }

      if (exposedHeaders != null)
      {
         Set<String> setExposedHeaders = parseStringToSet(exposedHeaders);
         this.exposedHeaders.clear();
         this.exposedHeaders.addAll(setExposedHeaders);
      }

      if (supportsCredentials != null)
      {
         // For any value other then 'true' this will be false.
         this.supportsCredentials = Boolean.parseBoolean(supportsCredentials);
      }

      if (preflightMaxAge != null)
      {
         try
         {
            if (!preflightMaxAge.isEmpty())
            {
               this.preflightMaxAge = Long.parseLong(preflightMaxAge);
            }
            else
            {
               this.preflightMaxAge = 0L;
            }
         }
         catch (NumberFormatException e)
         {
            throw new ServletException("Unable to parse preflightMaxAge", e);
         }
      }

      if (decorateRequest != null)
      {
         // For any value other then 'true' this will be false.
         this.decorateRequest = Boolean.parseBoolean(decorateRequest);
      }
   }

   /**
    * Takes a comma separated list and returns a Set<String>.
    *
    * @param data
    *           A comma separated list of strings.
    * @return Set<String>
    */
   private Set<String> parseStringToSet(final String data)
   {
      String[] splits;

      if (data != null && data.length() > 0)
      {
         splits = data.split(",");
      }
      else
      {
         splits = new String[] {};
      }

      Set<String> set = new HashSet<>();
      if (splits.length > 0)
      {
         for (String split : splits)
         {
            set.add(split.trim());
         }
      }

      return set;
   }

   /**
    * Checks if a given origin is valid or not. Criteria:
    * <ul>
    * <li>If an encoded character is present in origin, it's not valid.</li>
    * <li>If origin is "null", it's valid.</li>
    * <li>Origin should be a valid {@link URI}</li>
    * </ul>
    *
    * @param origin
    * @see <a href="http://tools.ietf.org/html/rfc952">RFC952</a>
    */
   protected static boolean isValidOrigin(String origin)
   {
      // Checks for encoded characters. Helps prevent CRLF injection.
      if (origin.contains("%"))
      {
         return false;
      }

      // "null" is a valid origin
      if ("null".equals(origin))
      {
         return true;
      }

      URI originURI;

      try
      {
         originURI = new URI(origin);
      }
      catch (URISyntaxException e)
      {
         return false;
      }
      // If scheme for URI is null, return false. Return true otherwise.
      return originURI.getScheme() != null;

   }

   /**
    * Determines if any origin is allowed to make CORS request.
    *
    * @return <code>true</code> if it's enabled; false otherwise.
    */
   public boolean isAnyOriginAllowed()
   {
      return anyOriginAllowed;
   }

   /**
    * Returns a {@link Set} of headers that should be exposed by browser.
    */
   public Collection<String> getExposedHeaders()
   {
      return exposedHeaders;
   }

   /**
    * Determines is supports credentials is enabled.
    */
   public boolean isSupportsCredentials()
   {
      return supportsCredentials;
   }

   /**
    * Returns the preflight response cache time in seconds.
    *
    * @return Time to cache in seconds.
    */
   public long getPreflightMaxAge()
   {
      return preflightMaxAge;
   }

   /**
    * Returns the {@link Set} of allowed origins that are allowed to make
    * requests.
    *
    * @return {@link Set}
    */
   public Collection<String> getAllowedOrigins()
   {
      return allowedOrigins;
   }

   /**
    * Returns a {@link Set} of HTTP methods that are allowed to make requests.
    *
    * @return {@link Set}
    */
   public Collection<String> getAllowedHttpMethods()
   {
      return allowedHttpMethods;
   }

   /**
    * Returns a {@link Set} of headers support by resource.
    *
    * @return {@link Set}
    */
   public Collection<String> getAllowedHttpHeaders()
   {
      return allowedHttpHeaders;
   }

   // -------------------------------------------------- CORS Response Headers
   /**
    * The Access-Control-Allow-Origin header indicates whether a resource can be
    * shared based by returning the value of the Origin request header in the
    * response.
    */
   public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";

   /**
    * The Access-Control-Allow-Credentials header indicates whether the response
    * to request can be exposed when the omit credentials flag is unset. When
    * part of the response to a preflight request it indicates that the actual
    * request can include user credentials.
    */
   public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";

   /**
    * The Access-Control-Expose-Headers header indicates which headers are safe
    * to expose to the API of a CORS API specification
    */
   public static final String RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";

   /**
    * The Access-Control-Max-Age header indicates how long the results of a
    * preflight request can be cached in a preflight result cache.
    */
   public static final String RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";

   /**
    * The Access-Control-Allow-Methods header indicates, as part of the response
    * to a preflight request, which methods can be used during the actual
    * request.
    */
   public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";

   /**
    * The Access-Control-Allow-Headers header indicates, as part of the response
    * to a preflight request, which header field names can be used during the
    * actual request.
    */
   public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";

   // -------------------------------------------------- CORS Request Headers
   /**
    * The Origin header indicates where the cross-origin request or preflight
    * request originates from.
    */
   public static final String REQUEST_HEADER_ORIGIN = "Origin";

   /**
    * The Access-Control-Request-Method header indicates which method will be
    * used in the actual request as part of the preflight request.
    */
   public static final String REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";

   /**
    * The Access-Control-Request-Headers header indicates which headers will be
    * used in the actual request as part of the preflight request.
    */
   public static final String REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";

   // ----------------------------------------------------- Request attributes
   /**
    * The prefix to a CORS request attribute.
    */
   public static final String HTTP_REQUEST_ATTRIBUTE_PREFIX = "cors.";

   /**
    * Attribute that contains the origin of the request.
    */
   public static final String HTTP_REQUEST_ATTRIBUTE_ORIGIN = HTTP_REQUEST_ATTRIBUTE_PREFIX + "request.origin";

   /**
    * Boolean value, suggesting if the request is a CORS request or not.
    */
   public static final String HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST = HTTP_REQUEST_ATTRIBUTE_PREFIX + "isCorsRequest";

   /**
    * Type of CORS request, of type {@link CORSRequestType}.
    */
   public static final String HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE = HTTP_REQUEST_ATTRIBUTE_PREFIX + "request.type";

   /**
    * Request headers sent as 'Access-Control-Request-Headers' header, for
    * pre-flight request.
    */
   public static final String HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS = HTTP_REQUEST_ATTRIBUTE_PREFIX
         + "request.headers";

   // -------------------------------------------------------------- Constants
   /**
    * Enumerates varies types of CORS requests. Also, provides utility methods
    * to determine the request type.
    */
   protected static enum CORSRequestType
   {
      /**
       * A simple HTTP request, i.e. it shouldn't be pre-flighted.
       */
      SIMPLE,
      /**
       * A HTTP request that needs to be pre-flighted.
       */
      ACTUAL,
      /**
       * A pre-flight CORS request, to get meta information, before a non-simple
       * HTTP request is sent.
       */
      PRE_FLIGHT,
      /**
       * Not a CORS request, but a normal request.
       */
      NOT_CORS,
      /**
       * An invalid CORS request, i.e. it qualifies to be a CORS request, but
       * fails to be a valid one.
       */
      INVALID_CORS
   }

   /**
    * {@link Collection} of HTTP methods. Case sensitive.
    * 
    * @deprecated Not used. Will be removed in Tomcat 8.5.x onwards.
    */
   @Deprecated
   public static final Collection<String> HTTP_METHODS = new HashSet<>(
         Arrays.asList("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"));

   /**
    * {@link Collection} of non-simple HTTP methods. Case sensitive.
    * 
    * @deprecated Not used. Will be removed in Tomcat 8.5.x onwards. All HTTP
    *             methods not in {@link #SIMPLE_HTTP_METHODS} are assumed to be
    *             non-simple.
    */
   @Deprecated
   public static final Collection<String> COMPLEX_HTTP_METHODS = new HashSet<>(
         Arrays.asList("PUT", "DELETE", "TRACE", "CONNECT"));
   /**
    * {@link Collection} of Simple HTTP methods. Case sensitive.
    *
    * @see <a href="http://www.w3.org/TR/cors/#terminology" >http://www.w3.org/
    *      TR/cors/#terminology</a>
    *
    * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
    */
   @Deprecated
   public static final Collection<String> SIMPLE_HTTP_METHODS = new HashSet<>(Arrays.asList("GET", "POST", "HEAD"));

   /**
    * {@link Collection} of Simple HTTP request headers. Case in-sensitive.
    *
    * @see <a href="http://www.w3.org/TR/cors/#terminology" >http://www.w3.org/
    *      TR/cors/#terminology</a>
    *
    * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
    */
   @Deprecated
   public static final Collection<String> SIMPLE_HTTP_REQUEST_HEADERS = new HashSet<>(
         Arrays.asList("Accept", "Accept-Language", "Content-Language"));

   /**
    * {@link Collection} of Simple HTTP request headers. Case in-sensitive.
    *
    * @see <a href="http://www.w3.org/TR/cors/#terminology" >http://www.w3.org/
    *      TR/cors/#terminology</a>
    *
    * @deprecated Unused. Will be removed in Tomcat 8.5.x onwards.
    */
   @Deprecated
   public static final Collection<String> SIMPLE_HTTP_RESPONSE_HEADERS = new HashSet<>(
         Arrays.asList("Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma"));

   /**
    * {@link Collection} of media type values for the Content-Type header that
    * will be treated as 'simple'. Note media-type values are compared ignoring
    * parameters and in a case-insensitive manner.
    *
    * @see <a href="http://www.w3.org/TR/cors/#terminology" >http://www.w3.org/
    *      TR/cors/#terminology</a>
    */
   public static final Collection<String> SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES = new HashSet<>(
         Arrays.asList("application/x-www-form-urlencoded", "multipart/form-data", "text/plain"));

   // ------------------------------------------------ Configuration Defaults
   /**
    * By default, all origins are allowed to make requests.
    */
   public static final String DEFAULT_ALLOWED_ORIGINS = "*";

   /**
    * By default, following methods are supported: GET, POST, HEAD and OPTIONS.
    */
   public static final String DEFAULT_ALLOWED_HTTP_METHODS = "GET,POST,HEAD,OPTIONS";

   /**
    * By default, time duration to cache pre-flight response is 30 mins.
    */
   public static final String DEFAULT_PREFLIGHT_MAXAGE = "1800";

   /**
    * By default, support credentials is turned on.
    */
   public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true";

   /**
    * By default, following headers are supported:
    * Origin,Accept,X-Requested-With, Content-Type,
    * Access-Control-Request-Method, and Access-Control-Request-Headers.
    */
   public static final String DEFAULT_ALLOWED_HTTP_HEADERS = "Origin,Accept,X-Requested-With,Content-Type,"
         + "Access-Control-Request-Method,Access-Control-Request-Headers";

   /**
    * By default, none of the headers are exposed in response.
    */
   public static final String DEFAULT_EXPOSED_HEADERS = "";

   /**
    * By default, request is decorated with CORS attributes.
    */
   public static final String DEFAULT_DECORATE_REQUEST = "true";

   // ----------------------------------------Filter Config Init param-name(s)
   /**
    * Key to retrieve allowed origins from {@link FilterConfig}.
    */
   public static final String PARAM_CORS_ALLOWED_ORIGINS = "cors.allowed.origins";

   /**
    * Key to retrieve support credentials from {@link FilterConfig}.
    */
   public static final String PARAM_CORS_SUPPORT_CREDENTIALS = "cors.support.credentials";

   /**
    * Key to retrieve exposed headers from {@link FilterConfig}.
    */
   public static final String PARAM_CORS_EXPOSED_HEADERS = "cors.exposed.headers";

   /**
    * Key to retrieve allowed headers from {@link FilterConfig}.
    */
   public static final String PARAM_CORS_ALLOWED_HEADERS = "cors.allowed.headers";

   /**
    * Key to retrieve allowed methods from {@link FilterConfig}.
    */
   public static final String PARAM_CORS_ALLOWED_METHODS = "cors.allowed.methods";

   /**
    * Key to retrieve preflight max age from {@link FilterConfig}.
    */
   public static final String PARAM_CORS_PREFLIGHT_MAXAGE = "cors.preflight.maxage";

   /**
    * Key to determine if request should be decorated.
    */
   public static final String PARAM_CORS_REQUEST_DECORATE = "cors.request.decorate";
}

在web.xml中,我们可以这样那个配置:

 <filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>com.jianggujin.web.util.CorsFilter</filter-class>
  <init-param>
   <param-name>cors.allowed.origins</param-name>
   <param-value>*</param-value>
  </init-param>
  <init-param>
   <param-name>cors.allowed.methods</param-name>
   <param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
  </init-param>
  <init-param>
   <param-name>cors.allowed.headers</param-name>
   <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
  </init-param>
  <init-param>
   <param-name>cors.exposed.headers</param-name>
   <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
  </init-param>
  <init-param>
   <param-name>cors.support.credentials</param-name>
   <param-value>true</param-value>
  </init-param>
  <init-param>
   <param-name>cors.preflight.maxage</param-name>
   <param-value>10</param-value>
  </init-param>
 </filter>
 <filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值