跨域请求阻塞 Spring REST 服务 + AJAX

Posted

技术标签:

【中文标题】跨域请求阻塞 Spring REST 服务 + AJAX【英文标题】:Cross-Origin Request Blocked Spring REST service + AJAX 【发布时间】:2015-06-05 23:54:26 【问题描述】:

无法调用spring REST服务

我的春季服务

@RequestMapping(value = "/MAS/authenticate", method = RequestMethod.POST)
public ResponseEntity<Map<String, String>> authenticate(@RequestBody Subject subject) 
    Map<String, String> result = new HashMap<String, String>();
    result.put("result_detail", "Invalid Password");
    result.put("result", "failure");
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setContentType(MediaType.APPLICATION_JSON);
    responseHeaders.add("Access-Control-Allow-Origin", "*"); // also added header to allow cross domain request for any domain
    return new ResponseEntity<Map<String, String>>(result, responseHeaders, HttpStatus.OK);

我的 AJAX 代码

$.ajax(

  crossDomain: true,
  type: "POST",
  contentType: "application/json; charset=utf-8",
  async: false,
  url: "http://localhost:8080/SpringMVC/rest/MAS/authenticate",
  headers: "Access-Control-Allow-Origin" : "*",
  data:,
  dataType: "json", //also tried "jsonp"
  success: function(data, status, jqXHR)
  
    alert('success');
  ,
  error: function(jqXHR, status)
  
    alert('error');
  
);

我收到以下错误:(

跨域请求被阻止:同源策略不允许读取位于http://localhost:8080/SpringMVC/rest/MAS/authenticate 的远程资源。这可以通过将资源移动到同一域或启用 CORS 来解决。

我也试过dataType: "jsonp"。它将我的正文对象附加到 URL 中,该 URL 生成不同的 URL,然后无法访问我的服务 URL 并得到 404 错误。

我的浏览器:firefox 36.0.4

我怎样才能摆脱这个错误,有什么帮助吗?

【问题讨论】:

你使用的是哪个服务器? @Halayem Anis:我正在使用 webSphere,但也在 tomcat 上尝试过。两者都出现相同的错误 【参考方案1】:

我的 AJAX 调用和服务正常。在互联网上搜索了很多后,我发现它的服务器端问题不是客户端。

在使用 Spring 的服务器端,我们必须实现允许 CORS 请求的过滤器。

过滤器看起来像这样。

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.filter.OncePerRequestFilter;

public class CORSFilter extends OncePerRequestFilter 
    private static final Log LOG = LogFactory.getLog(CORSFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException 

        response.addHeader("Access-Control-Allow-Origin", "*");
        if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) 
            LOG.trace("Sending Header....");
            // CORS "pre-flight" request
            response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
            // response.addHeader("Access-Control-Allow-Headers", "Authorization");
            response.addHeader("Access-Control-Allow-Headers", "Content-Type");
            response.addHeader("Access-Control-Max-Age", "1");
        
        filterChain.doFilter(request, response);
    


并在 web.xml 中将此过滤器应用于您的服务请求,如下所示

    <filter>
        <filter-name>cors</filter-name>
        <filter-class>com.test.common.controller.CORSFilter</filter-class> <!-- your package name and filter class -->
    </filter>
    <filter-mapping>
        <filter-name>cors</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping> 

这可能会帮助遇到此问题的其他人。 :)

【讨论】:

【参考方案2】:

默认情况下,唯一允许的方法是GET,并且您不允许在服务器端使用POST

Access-Control-Allow-Origin: *

此标头仅启用 CORS,但您需要添加:

Access-Control-Allow-Methods: POST, GET

More detailed how-to about the HTTP access control (CORS) Mozilla 项目

所以你的代码应该是这样的:

responseHeaders.add("Access-Control-Allow-Methods", "POST, GET"); // also added header to allow POST, GET method to be available
responseHeaders.add("Access-Control-Allow-Origin", "*"); // also added header to allow cross domain request for any domain

更新

我重新阅读了这篇文章,发现了一些细节:

一个简单的跨站点请求是:

仅使用 GET、HEAD 或 POST。 如果使用 POST 将数据发送到 服务器,Content-Type 用 HTTP 发送到服务器的数据 POST 请求是 application/x-www-form-urlencoded 之一, multipart/form-data 或 text/plain。 不设置自定义标题 HTTP 请求(如 X-Modified 等)

正如你可以看到的粗体,你必须为你的数据设置其他Content-Type(目前是contentType: "application/json; charset=utf-8",)或者使用后面描述的预检技术:

它使用 GET、HEAD 或 POST 以外的方法。此外,如果使用 POST 发送 Content-Type 以外的请求数据 application/x-www-form-urlencoded、multipart/form-data 或 text/plain, 例如如果 POST 请求使用 application/xml 或 text/xml,然后预检请求。 它在请求中设置自定义标头(例如,请求使用标头,例如 X-PINGOTHER)

所以我建议您更改 contentType 或尝试将此标头用于您的请求:

Access-Control-Request-Headers: X-HEADER_NAME_OF_YOUR_CHOOSE

这个标题进入你的响应:

Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-HEADER_NAME_OF_YOUR_CHOOSE

然后你可以尝试调用你的方法。

【讨论】:

在服务中添加了“Access-Control-Allow-Methods”、“POST, GET”,但仍然出现相同的错误:( 更新了答案,尝试阅读原文并尝试他们的解决方案。 非常感谢您的回复,我尝试了您的其他建议,但无法摆脱此错误:(【参考方案3】:

以下是跨平台spring boot web服务调用的解决方案。

申请网址:http://localhost:8080

网络服务网址:http://localhost:9090

在你的 spring 控制器中使用以下注解

@CrossOrigin(origins = "http://localhost:8080")
@RequestMapping(value = "/uri", method = RequestMethod.GET)
public SomeObject someMethod()
// your logic will come here

【讨论】:

以上是关于跨域请求阻塞 Spring REST 服务 + AJAX的主要内容,如果未能解决你的问题,请参考以下文章

Spring Data REST CORS - 如何处理预检选项请求?

跨域 jQuery Ajax 请求和 WCF REST 服务

带有 Spring Security 核心和 CORS 插件的 grails REST API 不适用于 OPTIONS http 方法请求

AngularJS 中的跨域 HTTP 请求失败

使用 Kotlin 协程的 Spring Boot Rest 服务

Vue+axios+spring boot遇到的问题(跨域请求)