アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

JavaScriptのクロスドメイン問題対応方法

皆さん、こんにちは。笹亀です。
しばらく立て混んでいた関係でブログがかけませんでした。
久しぶりにブログを書かせていただきます。

JavaScriptでいろいろと実装しているとき、別ウィンドでページを呼び出し、その別ウィンドーと呼び出しウィンドー間にて通信を行うといったときによくクロスドメイン問題に悩まされます。
今回はそのようなクロスドメイン問題を解決する方法についてご紹介します。

そもそもクロスドメインとは、異なるドメイン間をまたがったデータに対してアクセスをすることをいいます。
Webアプリケーションを設置・公開するホストのドメインと、そのアプリケーションがアクセスするWebサービスドメインが、サブドメインやポート番号も含め異なる場合は、JavaScriptなどでのデータのアクセスは遮断されます。

JavaScriptにてクロスドメイン間の通信をする場合には、JSONPを利用して行う方法もございますが、今回はHTML5より利用できるようになった、postMessageにてクロスドメイン間のデータの通信方法についてご紹介します。
データの流れについては下記のようなイメージになります。


次に上記の流れについては実際にテスト的に実装をしてみます。
まずは別ウィンドーで開く側のHTMLを作成します。
 1.Web Site Bのサイトを別ウィンドーで呼び出す
 2.1で呼び出したウィンドーオブジェクトを利用してpostMessageで通信をする
  ※必ずHTML5の記法で記載をします


<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JS_CrossDomainテスト</title>
<script language="javascript">
<!--

var winEntPopup;

//メッセージが送られたときに反応するイベントリスナー
window.addEventListener("message", receiveData, false);

//データを受け取る関数
function receiveData(e) {
    document.FormTest.bro_info.value =e.data;
}

//ポップアップで呼び出し
function fnc_popupWindow(varUrl) {
    wFeatures = "resizable=yes,status=yes,location=no,directories=no,menubar=no,scrollbars=yes,toolbar=no,width=1000,height=700,left=100,top=100";
    winEntPopup = window.open(varUrl, "testpopup", wFeatures);
    winEntPopup.focus();
    
    //読み込みまで待つため、あえて5秒後に問い合わせる
    setTimeout("execute();", 5000);
}
function execute() {
    winEntPopup.postMessage('こんにちは、小ウィンドーさん!通信できましたね!', '*');
}

-->
</script>
</head>
<body>
<h3>データ通信テスト用</h3>
<form method="POST" name='FormTest'>
<input type="button" value="別ウィンドーで開く" onClick="fnc_popupWindow('http://xxxxxx.xxxxxx/js_crossdomain_test.html')">
<textarea name="bro_info" cols="50" rows="10">通信データがここに表示されます</textarea>
</form>
</body>
</html>

作成したイメージは下記のようになります。



次に別ウィンドーで開かれる側のHTMLを作成します。
 3.2で通信した内容を受け取り、通信がきたオブジェクトを保持する
 4.3のオブジェクト使ってWeb Site AにデータをpostMessageで通信する


<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
<title>Js_crossDomainテスト</title>
<script language="javascript">
<!--
//呼び出し側から送られてくるデータの保存変数
var send_obj;
var set_flag = '';

window.addEventListener("message", postData, false);

function postData(ev){
  //下記のように呼び出し元の確認をすることができます
  if (ev.origin != "http://sasa.local:8080") {
    return;
  }
  
  //通信されたデータを受け取る
  document.FormTest.bro_info.value = ev.origin + "からのメッセージを受信しました:<br>「" + ev.data + "」";

  //呼び出し側のデータを保存
  set_flag = '1';
  send_obj = ev;
}

//送るデータをセットする
function fnc_click(message) {
  //呼び出し側からのデータが送られているか確認する
  if (set_flag == '') {
    alert('呼び出し元と通信しております。もうしばらくしてから番組を選択してください。※うまく設定できない場合は一度、閉じて再実行してください。');
    return false;
  }
  
  //親ウィンドーに送るデータの確認
  if (confirm('「' + message + '」をセットしますが、よろしいでしょうか?')) {
  
    //親ウィンドーにデータを送る
    send_obj.source.postMessage(message, send_obj.origin);
  }
  return false;
}
//-->
</script>


</head>

<body>
<form method="POST" name='FormTest'>
<textarea name="bro_info" cols="50" rows="10">送られたメッセージを受け取る</textarea>
</form>

<form name=f1>
<div id="program">
<a href="#" onClick="javascript:fnc_click('こんにちは、親ウィンドーさん!');">通信テスト</a>
<hr />
<input type="button" value=" とじる " onClick="javascript:window.close()">
</div>
</form>
</body>
</html>

作成したイメージは下記のようになります。



上記2つを作成した内容でクロスドメイン間にて正しくデータのやりとりができるかを確認します。「別ウィンドーで開く」ボタンをクリックして親ウィンドーから小ウィンドーに対して通信しているかを確認します。通信開始は別ウィンドーが読み込まれる間(5秒間)、待ってから通信を開始するようになっておりますので、ボタンをクリックしてからはしばらく待ちます。
※下記の画像のようにテキストエリアに通信データが表示されれば小ウィンドーと親ウィンドーが通信可能になったことになります


メッセージが表示された状態で「通信テスト」のリンクをクリックすると、下記の画像のように親ウィンドーのテキストエリアに通信ができた証拠としてメッセージが表示されます。




今回、ご紹介させていただいた方法を利用すればクロスドメインの問題も関係なく実装が可能になります。ただ、通信可能エリアの確認・制御をして、万が一のことも想定し、セキュリティの対策も施しておきましょう。