发布于 1年前

Spring mvc解决跨域请求:Response to preflight request doesn't pass access control check

在nginx跨域请求cors配置如下:

location / {
        add_header         'Access-Control-Allow-Origin' 'https://api.xxxx.com';
        add_header         "Access-Control-Allow-Credentials" "true";
        add_header         "Access-Control-Allow-Headers" "x-requested-with,content-type";
        proxy_pass         http://localhost:8080;
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
}

主要是添加了三个用于控制CORS的头信息:

  • Access-Control-Allow-Origin:允许的来源
  • Access-Control-Allow-Credentials:设置为true,允许ajax异步请求带cookie信息
  • Access-Control-Allow-Headers:设置为x-requested-with,content-type,允许ajax余部请求。

这个配置也可以在spring mvc里配置。

问题:

配置好后,异步跨域请求成功。

当发起post请求,请求的数据是放在request body里,请求的Content-Type为application/json。Spring MVC需要使用@RequestBody来接收数据。

这是一次非简单的请求,浏览器会发起一次Options的预请求。发起Options请求报403错误:

Failed to load https://api.xxxx.com/auth.json: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://other.xxxx.com' is therefore not allowed access. The response had HTTP status code 403.

解决方法

原以为是自己的配置有误,但找了很多资料可以确定配置是没有问题。后来debug跟踪Spring MVC DispatcherServlet的调用,发现在DispacerServlet是因为没有找都到执行Options请求的方法。

在做跨域请求时,配置好CORS相关头信息后,以为就可以了,这很容易忽略在Spring MVC添加对Options请求的处理。

解决方法有两种:

  • 在拦截器统一添加对Options请求的支持
  • 添加一个支持Options的ReqeuestMapping。

拦截器示例:

public class CrosDomainAllowInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        if(request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            response.setStatus(HttpStatus.OK.value());
            return false;
        }
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
    }
}

@RequestMapping示例

@RequestMapping(value = "/*",method = RequestMethod.OPTIONS)
public ResponseEntity handleOptions(){
    return ResponseEntity.noContent();
}
©2020 edoou.com   京ICP备16001874号-3