2012/09/20

[socket.io] Authorizing和訳

Authorizing · LearnBoost/socket.io Wikiを和訳してみます。
素人の訳ですので、間違いもありますし、訳せていない文もあります。
間違いにお気づきの方は、よろしければコメント欄からご指摘下さい。

Authorizing

Socket.IOは2つの異なる認証方式をサポートします。 初期化/ハンドシェイクの過程でグローバルな認証を行い適用するか、 または個々のネームスペースごとに認証を行うことができます。
グローバルな認証とネームスペースごとの認証は、一緒に利用することもできますし、 各々独立して利用することもできます。 双方の間で唯一共通しているのは、 handeShakeData オブジェクトです。 このデータオブジェクトは、ハンドシェイクによるリクエストデータで生成されます。 したがって、認証処理がどのように動作するかをうまく理解するために、 まずハンドシェイクのプロセスを理解する必要があります。

Handshaking

クライアントがSocket.IOサーバーと永続的なリアルタイム通信を確立しようとする場合、 ハンドシェイク処理を開始しなくてはなりません。 ハンドシェイクは、XHRリクエストかJSONPリクエスト(クロスドメインリクエストの場合)のいずれかを使って開始されます。
サーバーが接続を受信したとき、そのリクエストから、後に必要となるデータの収集を開始します。 そうするには、2つの理由があります。
  1. ユーザーは、リクエストヘッダかIPアドレスのいずれかで、クライアントの認証を行う場合があります。
  2. サーバーとの接続を確立しようとする場合、ヘッダを送信しない通信もあるので、内部的にハンドシェイクデータを格納し、それによりクライアントが接続した際にその情報を再利用できるようになります。
例えば、クッキーヘッダからセッションIDを読み取り、接続されたソケットに対してExpressのセッションを初期化したい、という場合があるかもしれません。
handshakeData オブジェクトは、次の情報を持っています:
{
  headers: req.headers
 ,time:    (new Date) + ''
 ,address: socket.address()
 ,xdomain: !!headers.origin
 ,secure:  socket.secure
 ,issued:  +date
 ,url:     request.url
 ,query:   data.query
}
address は、socket.address() APIに従います。
全ての情報を読み込んだ後、グローバルな認証機能が設定されているかチェックします。 設定されている場合は、handShakeDataオブジェクトとコールバック関数を認証関数に渡します。 コールバック関数が実行されるとき、handShakeDataは内部に格納しているので、後のconnectionイベントの間にsocket.handshakeプロパティを通じてアクセスすることができます。コールバックの応答に基づいて、403か500、または成功時は200を応答します。

Global authorizing

グローバル認証は、コンフィグレーションメソッドでauthorizationを設定することで有効に成ります。
io.configure(function (){
  io.set('authorization', function (handShakeData, callback) {
    callback(null, true); // error first callback style
  });
});
上記の認証関数中、2つの引数をとります:
  1. handShakeData, ハンドシェイクの過程で生成したhandShakeDataオブジェクト。
  2. callback, ハンドシェイク処理がデータベースのルックアップを必要とする場合など。これは、undefinedか、エラー時には文字列が入るerrorとクライアントが認証されたか否かを示すbool値authorizedの2つの引数をとります。

エラーを送出するか、authorized引数にfalseを設定することで、ともにクライアントに対して、サーバーへの接続を許可しないようにします。
handshakeDataは認証処理の後に保存されているので、このオブジェクトに対して、データを追加したり削除することができます。
var io = require('socket.io').listen(80);

io.configure(function (){
  io.set('authorization', function (handshakeData, callback) {
    // findDatabyip is an async example function
    findDatabyIP(handshakeData.address.address, function(err, data) {
      if (err) return callback(err);

      if (data.authorized) {
        handshakeData.foo = 'bar';
        for(var prop in data) handshakeData[prop] = data[prop];
        callback(null, true);
      } else {
        callback(null, false);
      }
    });
  });
});

ちなみに、グローバル認証とネームスペース認証とで、handshakeDataは共有し合うので、handshakeDataオブジェクトからヘッダ情報や、重要なデータを削除してしまうと、ネームスペース認証の内部で、そのデータにアクセスすることができなくなります。一方で、新たに追加したデータは、ネームスペース認証においても参照できるようになります。
handshakeDataへのアクセスは、ネームスペースだけでなく、接続したソケットからもアクセスできます。 つまり、ハンドシェイクの過程で保存したデータは、ネームスペースのconnectionイベント内で、↓の例のようにscoket.handshakeプロパティを通じてアクセスできます:
io.sockets.on('connection', function (socket) {
  console.log(socket.handshake.foo == true); // write 'true'
  console.log(socket.handshake.address.address); // write 127.0.0.1
});

How does the client handle the global authorization

ソケットのerrorイベントを補足することで、クライアントサイドで認証が失敗したことを検知できます。 ネームスペース認証でも同様ですが、but there more error messages emitted there that could be related to errors in your namespace and not to the connection of socket.
接続に成功したソケットについては、connectイベントを補足することができます。
var sio = io.connect();

sio.socket.on('error', function (reason){
  console.error('Unable to connect Socket.IO', reason);
});

sio.on('connect', function (){
  console.info('successfully established a working connection \o/');
});

Namespace authorization

認証は、ネームスペース単位でも行うことができます。これは、例えば、パブリックな用途のチャットボックスや、登録メンバーに対するチャットボックスの拡張サポート用などのネームスペースにおいて、よりフレキシブルになります。
あらゆるネームスペースは、authorizationメソッドが利用できます。このチェーン可能なメソッドは、ネームスペースにおける認証方法を登録するために使用できます。この関数の引数は、グローバル認証機能と同じです。
var io = require('socket.io').listen(80);

io.of('/private').authorization(function (handshakeData, callback) {
  console.dir(handshakeData);
  handshakeData.foo = 'baz';
  callback(null, true);
}).on('connection', function (socket) {
  console.dir(socket.handshake.foo);
});

How the client handle the namespace authorization

認証失敗のハンドリングの仕方が、グローバル認証の場合と若干異なります。 errorイベントを送出するかわりに、connect_failedイベントを送出します。 ですが、認証をパスした場合は、期待通りにconnectイベントを送出します。

Example

var sio = io.connect()
  , socket = sio.socket;

socket.of('/example')
  .on('connect_failed', function (reason) {
    console.error('unable to connect to namespace', reason);
  })
  .on('connect', function () {
    console.info('sucessfully established a connection with the namespace');
  });

1 件のコメント:

匿名 さんのコメント...

翻訳ありがとう