クロスオリジン通信の実装手法とその実践

ブラウザの同一オリジンポリシー

同一オリジンとは、プロトコル、ホスト名、ポート番号が全て一致するURLを指します。ブラウザはセキュリティ上の理由から、Cookie、Web Storage、iframe、XMLHttpRequestなどのリソースに対するクロスオリジンアクセスを制限しています。

JSONPによるクロスオリジン通信

script要素のクロスオリジン読み込みを利用した手法です。サーバーからJavaScriptコードを返却し、クライアント側でコールバック関数を実行します。

function fetchCrossOriginData(config) {
  return new Promise((resolve) => {
    const scriptTag = document.createElement('script');
    const queryParams = new URLSearchParams(config.parameters);
    queryParams.append('callback', config.callbackName);
    
    scriptTag.src = `${config.endpoint}?${queryParams.toString()}`;
    document.head.appendChild(scriptTag);
    
    window[config.callbackName] = (response) => {
      document.head.removeChild(scriptTag);
      delete window[config.callbackName];
      resolve(response);
    };
  });
}

// 使用例
fetchCrossOriginData({
  endpoint: 'https://api.example.com/data',
  parameters: { user: 'tech_wizard' },
  callbackName: 'dataHandler'
}).then(result => {
  console.log('受信データ:', result);
});

CORSの設定と実装

サーバー側で適切なHTTPヘッダを設定することで、クロスオリジンリクエストを許可します。

const express = require('express');
const server = express();

server.use((request, response, next) => {
  response.header('Access-Control-Allow-Origin', request.headers.origin);
  response.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
  response.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
  response.header('Access-Control-Max-Age', '86400');
  response.header('Access-Control-Allow-Credentials', 'true');
  response.header('Access-Control-Expose-Headers', 'X-Custom-Header');
  next();
});

server.get('/api/users', (request, response) => {
  response.json({ users: ['alice', 'bob'] });
});

server.listen(8080);

postMessage APIの活用

異なるウィンドウやiframe間でのメッセージ通信を実現します。

// 送信側
const targetFrame = document.getElementById('externalFrame');
targetFrame.contentWindow.postMessage(
  { type: 'DATA_UPDATE', payload: { value: 42 } },
  'https://target-domain.com'
);

// 受信側
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://trusted-domain.com') return;
  console.log('受信メッセージ:', event.data);
});

window.nameを利用したデータ共有

iframeのwindow.nameプロパティを経由してデータを転送します。

let loadCount = 0;
const proxyFrame = document.createElement('iframe');

proxyFrame.onload = () => {
  if (loadCount === 0) {
    proxyFrame.src = '/proxy.html';
    loadCount++;
  } else {
    const sharedData = proxyFrame.contentWindow.name;
    console.log('共有データ:', JSON.parse(sharedData));
  }
};

proxyFrame.src = 'https://external-domain.com/data-source.html';
document.body.appendChild(proxyFrame);

location.hashによる状態伝達

URLフラグメントを介した親子iframe間の通信手法です。

// 子iframeでの処理
const parentHash = window.parent.location.hash;
window.parent.location.hash = '#response:' + encodeURIComponent(data);

// 親ウィンドウでのハッシュ変更監視
window.addEventListener('hashchange', () => {
  const responseData = decodeURIComponent(location.hash.slice(1));
  console.log('ハッシュ経由のデータ:', responseData);
});

document.domainの設定

サブドメイン間での制限緩和に使用されます。

// メインページ (www.example.com)
document.domain = 'example.com';
const frame = document.getElementById('subdomainFrame');
frame.onload = () => {
  console.log('サブドメインデータ:', frame.contentWindow.sharedData);
};

// サブドメインページ (api.example.com)  
document.domain = 'example.com';
window.sharedData = { apiKey: 'secret123' };

WebSocketを用いた双方向通信

const webSocket = new WebSocket('wss://echo.example.com');
webSocket.onopen = () => {
  webSocket.send(JSON.stringify({ action: 'subscribe' }));
};

webSocket.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log('WebSocketメッセージ:', message);
};

Nginxリバースプロキシの設定

server {
    listen 80;
    server_name localhost;
    
    location /api/ {
        proxy_pass http://backend-server:3000/;
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
        add_header Access-Control-Allow-Headers 'Authorization, Content-Type';
    }
    
    location /static/ {
        root /var/www/html;
        add_header Access-Control-Allow-Origin *;
    }
}

タグ: クロスオリジン JSONP CORS postMessage websocket

7月5日 00:07 投稿