2019-05-06
クロスオリジン制約を回避して、異なるドメイン間でリソースを取得する方法
Ajax通信を用いてjsonやCSVファイルを取得する場合、そのページのドメインとは別のドメインから取得したいということがあります。そこで問題になるのがクロスドメインによる制約です。
クロスドメイン制約は、セキュリティの観点から、アクセスしているページのドメイン以外のドメインからはAjax通信をさせないという制約です。
クロスドメイン制約について解説し、制約を回避しデータ通信する方法をご紹介します。
目次
クロスドメインによる制約
例えば、Webブラウザを通してhttps://example.com/サイト上のページから、Ajax通信を用いてhttps://source.com/data.jsonを取得し読み込む、ということを前提に考えてみましょう。
下記のようなエラーが、開発ツールのコンソールに表示されてしまいます。CORSヘッダがセットされていないので、異なるドメインのデータは取得できない、という内容です。
firefoxの場合
クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、https://source.com/data.json にあるリモートリソースの読み込みは拒否されます (理由: CORS ヘッダー ‘Access-Control-Allow-Origin’ が足りない)。
これに対していくつかの回避策があります。
CORS は異なるドメイン間の要求を安全に許可する仕組み
回避策の説明に先立ち、エラーに表示されている、クロスオリジンや同一生成元ポリシー、CORSについて説明します。
クロスオリジン
厳密には異なりますが、オリジンはドメインに読み替えて問題ありません。つまり、クロスオリジンとは異なるドメイン間のデータ等のやり取りのことです。ここでは、クロスドメインと言っても間違いではありません。
ブラウザはセキュリティ上の理由から、オリジン(プロトコル、ホスト、ポート番号の組み合わせ)が異なるウェブページ間で直接的な通信を制限しています。これは、異なるオリジンのスクリプトやリソースが、ユーザーのプライバシーやセキュリティを脅かすのを防ぐためです。
同一生成元ポリシー(Same-Origin Policy)
ひとことで言うと、javascriptで自由にデータをやりとりできるのはそのjavascriptと同じドメインだけに制限する、ということです。
同一生成元かどうかの判断は、先ほどのオリジンがチェックされます。
ブラウザはデフォルトで、異なるオリジン間のスクリプトやデータのアクセスを制限します。これにより、example.com
ドメイン内のスクリプトが、source.comドメインのデータにアクセスできないようになります。
CORS(Cross-Origin Resource Sharing)
CORSは同一生成元ポリシーの制限を緩和するための仕組みです。サーバ側で特定のHTTPヘッダーを設定することで、クロスドメインでのリソース共有を許可することができます。
回避策① Access-Control-Allow-Originを設定する
ブラウザのデベロッパーツールでは、下記のエラーが表示されていました。
理由: CORS ヘッダー ‘Access-Control-Allow-Origin’ が足りない
つまり、ヘッダーでクロスドメインの情報共有を許可してあげれば解決します。
Access-Control-Allow-Originを設定するにはサーバー側での設定が必要となります。
その許可をサーバ側(ここでは.htaccessファイル)で設定します。
.htaccessによる設定が許可されていない環境では使えません。読み込みたいファイルが置いてあるサーバー(source.com)側を自分で管理している場合は、有効な方法だと思います。
ちなみに、楽天GOLDサーバにデータを設置しデータを運用したかったのですが、この方法では不可でした。サーバ側でより強力な制約があるのでしょう。
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin: "*"
</IfModule>
特定のドメインのみ許可する場合は、*を当該特定ドメインに置き換えてください。
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin: "https://example.com"
</IfModule>
上記設定により、source.comに対する、example.comからのリソースへのアクセスを許可しました。jsonファイル、data.jsonが取得できます。
回避策② プログラムを経由させて取得する
管理者でなくてもクロスドメイン制約を回避できる方法です。
下記の例ではPHPを使用しており、PHPが動く環境であることが必要です。
まず、クライアント側のphpファイルをajaxで取得します。取得するphpファイル(getcsv.php)内では、getで渡されたcsvファイルのURLを取得し、そのURLをfile_get_contents関数に渡し、データを取得します。
すべてクライアントサイドで完結していますね。
https://example.com/内に下記プログラムを用意
$(function(){
$.ajax({
url:"getcsv.php?url=https://source.com/data.csv",
type:"GET",
dataType:"text",
timeout: 5000
})
.done((data) => {
//成功した場合の処理
console.log(data);
})
.fail((data) => {
//失敗した場合の処理
console.log(data.responseText); //レスポンス文字列を表示
})
.always((data) => {
//成功・失敗どちらでも行う処理
console.log(data);
});
});
getcsv.php
<?php
echo file_get_contents($_GET['url']);
?>
まとめ
取得するファイルを設置しているサーバを自身で管理しているのであれば、回避策①がスマートだと思います。
サーバサイドプログラムが動作する環境であれば、回避策②が確実に仕組みをコントロールできる方法ではないでしょうか。ただ個人であればともかく、チームでこの仕組みを運用するのはガチャガチャしていて面倒な気がします。
ちなみに自分は、今回は紹介していないJSONP(JSON with Padding)という仕組みを使用することがあります。機会があれば近々ご紹介します。