浏览器中的“跨源请求被阻止:同源策略”错误

Posted

技术标签:

【中文标题】浏览器中的“跨源请求被阻止:同源策略”错误【英文标题】:"Cross-Origin Request Blocked: The Same Origin Policy" Error in browser 【发布时间】:2015-01-08 18:17:35 【问题描述】:

当我尝试 POST json 文件到我的服务器时出现此错误。

在我的服务器端,代码是:

    @POST
    @Path("updatedata")
    @Produces("text/plain")
    @Consumes("application/json")
    public Response UpdateData(String info) 
        Gson gson = new Gson();
        List<Data> list = gson.fromJson(info, new TypeToken<List<Data>>() 
        .getType());

        int is_success = 0;
        try 
            is_success += trainingdata.updateData(list);
         catch (SQLException e) 
            e.printStackTrace();
        
        String returnjson = "\"raw\":\"" + list.size() + "\",\"success\":\"" + is_success + "\"";
        return Response.ok().entity(returnjson).header("Access-Control-Allow-Origin", "*").header("Access-Control-Allow-Methods", "POST").build();
    

我可以通过 RESTClient - Chrome 插件成功更新我的数据。

但是当我构建前端并尝试通过 jaascript 调用 API 时, Firefox 显示:跨域请求被阻止:同源策略.... Chrome 显示:XMLHttpRequest 无法加载...请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,Origin '...' 不允许访问

我是这样写的:

var json = JSON.stringify(array);

var xhr = new XMLHttpRequest();
xhr.open("POST", "http://myurl:4080/updatedata", true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(json);

xhr.onload = function (e) 
    if (xhr.readyState === 4) 
        if (xhr.status === 200) 
            alert('hello');
        
    
;
xhr.onerror = function (e) 
    console.error(xhr.statusText);
;

我的 javascript 代码有问题吗?

我将后端代码和前端代码部署在同一台机器上。

GET 功能成功运行。

@GET
@Produces("application/json")
@Path("/cat_id")
public Response getAllDataById(@PathParam("cat_id") String cat_id) 
    ReviewedFormat result = null;
    try 
        result = trainingdata.getAllDataById(cat_id);
        Gson gson = new Gson();
        Type dataListType = new TypeToken<ReviewedFormat>() 
        .getType();
        String jsonString = gson.toJson(result, dataListType);
        return Response.ok().entity(jsonString).header("Access-Control-Allow-Origin", "*").header("Access-Control-Allow-Methods", "GET").build();

     catch (SQLException e) 
        logger.warn(e.getMessage());
    
    return null;

前端:

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://URL:4080/mywebservice/v1/trainingdata/" + cat_id, true);

xhr.onload = function (e) 
    if (xhr.readyState === 4) 
        if (xhr.status === 200) 
            //console.log(xhr.responseText);
            var jsoninfo = xhr.responseText;
            var obj = JSON.parse(jsoninfo);
        
     

【问题讨论】:

协议、域和端口必须匹配 developer.mozilla.org/en-US/docs/Web/Security/… 【参考方案1】:

这是在 javascript 中发出跨域请求导致的问题。出于安全原因,浏览器会阻止这种情况。

在 javascript 中,默认情况下您不能向不同的域(包括不同的端口)发出请求。

如果您需要向另一个域发送请求,您可以选择启用 CORS 或使用反向代理。

【讨论】:

我将服务器代码和前端代码放在同一台机器上。应该有问题吗? 托管在不同的域(包括不同的端口)上是不够的 当你提到反向代理时。你的意思是例如,我创建一个 php 文件来调用我的服务器。而前端调用这个PHP文件? 是的,这是正确的。在您自己的域上创建一个页面,然后调用服务器上的另一个域。 他只需要启用CORS。这不是火箭科学。标头是 Apache 中最简单的方法。本地机器只需切换到 FireFox 进行开发。我正在写一篇关于如何做到这一点的文章。【参考方案2】:

CORS 可防止跨站点攻击发生问题,并通过不依赖其他人的资源(可能会死)来强制进行智能开发。它是大多数服务器和浏览器的默认安全功能。

在 Apache 中,您可以通过添加标头来禁用 CORS,IIS 和 AppEngine 的工作方式类似。

由于您是在本地开发,因此最好的选择是 XAMPP/WAMPP 加上适当的标头 - 或者直接切换到 FireFox。

FireFox 不考虑 CORS 下的本地文件,而大多数浏览器会考虑。

Apache 修复

添加标题 ->

Header set Access-Control-Allow-Origin "*"

重置服务器 ->

apachectl -t

sudo service apache2 重新加载

IIS 修复:

修改根目录下的web.config(类似HTAccess)

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <system.webServer>
   <httpProtocol>
     <customHeaders>
       <add name="Access-Control-Allow-Origin" value="*" />
     </customHeaders>
   </httpProtocol>
 </system.webServer>
</configuration>

App 引擎

Python 的标头方法:self.response.headers.add_header()

class CORSEnabledHandler(webapp.RequestHandler):
  def get(self):
    self.response.headers.add_header("Access-Control-Allow-Origin", "*")
    self.response.headers['Content-Type'] = 'text/csv'
    self.response.out.write(self.dump_csv())

对于 Java:resp.addHeader()

public void doGet(HttpServletRequest req, HttpServletResponse resp) 
  resp.addHeader("Access-Control-Allow-Origin", "*");
  resp.addHeader("Content-Type", "text/csv");
  resp.getWriter().append(csvString);

Go:w.Header().Add()

func doGet(w http.ResponseWriter, r *http.Request) 
  w.Header().Add("Access-Control-Allow-Origin", "*")
  w.Header().Add("Content-Type", "text/csv")
  fmt.Fprintf(w, csvData)

如果您对此感兴趣,可以通过 JSONP 绕过 GET 请求的 CORS 问题:http://en.wikipedia.org/wiki/JSONP

【讨论】:

大部分时间您只能将 JSONP 与 GET 请求一起使用。但你基本上是正确的。【参考方案3】:

听起来您可以控制要发布到的远程资源。如果是这种情况,您的远程资源需要具有以下标头:

Access-Control-Allow-Origin: http://yourrequestingurl.com

这里有更多信息(看起来有人已经问过像你这样的问题了):Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at

【讨论】:

【参考方案4】:

感谢@TGH的提示,我终于通过添加网络代理解决了这个问题。

参考Using a Web Proxy,我创建了一个proxy.php文件,它接收Javascript的xmlHttpRequest,获取postdata并调用web service API。

<?php

$method = isset($_POST['method']) ? $_POST['method'] : 'POST';
$postData = file_get_contents('php://input');
$url = $envArray['url'] . ':' . $envArray['port'] . '/mywebservice/v1/trainingdata/updatedata';

echo curl($url, $postData, $method);


function curl($url = '', $params = '', $method = 'POST')
    if (empty($url)) 
        error_log('Curl URL is empty.');
        return;
    
    $envArray = Settings::getEnvAry();

    $ch = curl_init(); 
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);                                                                     
    curl_setopt($ch, CURLOPT_POSTFIELDS, html_entity_decode($params, ENT_QUOTES));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(                                                                          
            'Content-Type: application/json',                                                                                
            'Content-Length: ' . strlen($params)
        )                                                                       
    );
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);                                                                      

    $response = curl_exec($ch);
    $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $rs = array('status' => $http_status, 'response' => $response);

    return json_encode($rs);

?>

在前端,我调用了proxy.php

 var xhr = new XMLHttpRequest();
    xhr.open("POST", "proxy.php", true);
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.send(json);

我认为这更适合将项目部署到远程机器上,而不是修改 Apache 配置。

【讨论】:

【参考方案5】:

您可以在 nginx 配置中添加标头,在其他 Web 服务器中也可以类似地添加标头

示例

add_header Access-Control-Allow-Origin *;

【讨论】:

以上是关于浏览器中的“跨源请求被阻止:同源策略”错误的主要内容,如果未能解决你的问题,请参考以下文章

Javascript XMlHttprequest 到另一个域

CORS 颤动网络

即使在配置 ASP.NET Core Web API 中启用了 CORS,CORS 也会阻止发布请求

从 localhost Vue 项目调用 api 时出现 NodeJS CORS 错误

将 JSON 数据发布到返回状态代码 0 的 API

ASP.NET WebAPI2 和 AngularJS