8月 262012
 

Sequelizeを使ってユーザー認証/実装編 | Inhale n’ Exhale設計編からだいぶ間があいてしまったが、routes/auth.jsに枠組みだけ作ったauthモジュールの実装を埋めていこう。

スタート地点はこの段階。

module.exports = function(models) {
  var User = models.user;

  return {
      signup: function(req, res) {
      }

    , login: function(req, res) {
      }

    , logout: function(req, res) {
      }
  };
};

サインアップ

signupメソッドの役割は新規ユーザーを登録すること。リクエストボディからusernamepasswordの2つのパラメータを読み取って、Userモデルを介して DBにレコードを追加する。

signup: function(req, res) {
  var username = req.body.username || null
    , password = req.body.password || null;

  if(!username || !password) {
    res.statusCode = 403;
    res.end({ error: 'Shortage of params' });
    return;
  }

  User.create({
      username: username
    , password: User.hashPassword(password)
  })
  .success(function(user) {
    user = user.values;
    delete user.password;
    res.end(user);
  })
  .error(function(error) {
    res.statusCode = 500;
    res.end({ error: error });
  });
}

11-14行目:DBにレコードを追加するにはモデルのcreateメソッドを使う。引数は新しいレコードのデータを表すオブジェクトリテラル。リクエストボディから読み取ったpasswordは平文なので、Userモデルのクラスメソッドとして実装したhashPasswordメソッドを使ってハッシュ値に変換している。

15-19行目:レコードの追加が成功するとsuccessメソッドに渡した関数がコールバックされ、追加されたモデルのインスタンスが引数に渡される。追加されたレコードにアクセスするにはvaluesプロパティを使う。例えば、自動連番のインデックスや、現在時刻がデフォルト値になっている時刻データのように、追加時に確定するようなデータもvaluesプロパティには含まれている。passwordカラムはハッシュ化されたパスワードだが、外部に漏らすことはないので、クライアントには送信しないようにしている。

20-23行目:errorメソッドにはレコードの追加に失敗した場合のコールバック(エラーハンドラ)を渡す。

レコードの追加はcreateメソッドを使う以外に、buildメソッドとsaveメソッドを別々に呼び出す方法もある。データの追加前にバリデーションを行うのがよくあるケースだろう。例えば、usernameカラムをメールアドレス形式にするように仕様を変えたとした場合、Userモデルの定義時にバリデーションも含めるようにする。

// models/user.js
module.exports = function(sequelize, DataTypes) {
    return sequelize.define('user', {
        username: {
            type: DataTypes.STRING
          , unique: true
          , validate: { isEmail: true }
        }
      , password: 'char(40)'
    });
};

データ追加前にバリデーションを行うには、先にbuildメソッドでモデルのインスタンスを生成してvalidateメソッドを呼び出す。

  var new_user = User.build({
          username: username
        , password: User.hashPassword(password)
      })
    , invalid = new_user.validate();

  if(invalid) {
    res.statusCode = 403;
    res.end({ error: 'Invalid param', detail: invalid });
    return;
  }

  new_user.save()
  .success(function(user) {
    ...
  })
  .error(function(error) {
    ...
  });

validateメソッドは、モデル定義のvalidateで指定したルールを使ってバリデーションを行い、何もエラーがなければnullが返ってくる。Sequelizeに標準で組み込まれているバリデータがたくさんあるので見ておくといいだろう。

すでに設計編でルーティングは実装しているので、curlを使ってサインアップを実行してみよう。

$ curl -X POST -d 'username=h2plus&password=pass' localhost:3000/auth/signup

ログイン

loginメソッドの役割はユーザーを認証すること。リクエストボディからusernamepasswordの2つのパラメータを読み取って、Userモデルを介して DBからレコードを検索する。

login: function(req, res) {
  var username = req.body.username || null
    , password = req.body.password || null;

  if(!username || !password) {
    res.statusCode = 403;
    res.end({ error: 'Shortage of params' });
    return;
  }

  User.find({
      where: {
          username: username
        , password: User.hashPassword(password)
      }
  })
  .success(function(user) {
    if(user) {
      user = user.values;
      delete user.password;
      req.session.user_id = user.id;
      res.send(user);
    }
    else {
      res.statusCode = 403;
      res.end({ error: 'Authentication failure' });
    }
  })
  .error(function(error) {
    res.statusCode = 500;
    res.end({ error: error });
  });
}

11-16行目:1件のレコードを検索するにはfindメソッドを使う。引数には検索(SELECT文)の条件などを渡すことができる。今回はユーザー認証が目的なので、ユーザー名とパスワードのハッシュ値をWHERE句に指定して検索している。複数件のレコードを検索する場合は、findAll(またはall)メソッドを使う。

18-23行目:検索を実行した結果、レコードが見つかった場合は、該当レコードのインスタンスがコールバック関数の引数に渡ってくる。サインアップで使ったcreateと同様にデータへのアクセスはvaluesプロパティを使う。レコードが見つかったということは、ユーザー名とパスワードが一致した証なので、セッションにidカラムを保存しておくようにする。

24-27行目:検索の結果、レコードが見つからなかった場合もsuccessメソッドに渡したコールバック関数が呼び出されるが、引数はnullになっている。errorメソッド側のコールバックが呼ばれるわけではないので注意。

先ほどサインアップしたアカウントを使って、curlからログインを実行してみる。セッション管理にはCookieが使われているので、-cオプションでCookieJarに保存するようにしておこう。

$ curl -c ~/.cookies -X POST -d 'username=h2plus&password=pass' localhost:3000/auth/login

ログアウト

logoutメソッドの役割は既存のログインセッションを破棄すること。サインアップ、ログインに比べると、DBアクセスもないので簡素な実装になる。

logout: function(req, res) {
  req.session.destroy();
  res.end();
}

curlでのログアウト実行時は、ログイン時と同じCookieJarを-bオプションで指定する。

$ curl -b ~/.cookies -X POST localhost:3000/auth/logout

  1 コメント

 返信する

以下のHTML タグと属性が利用できます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください