jQuery を使って Ajax によるユーザーの認証と登録を行う

Ajax (Asynchronous JavaScript + XML) と jQuery を組み合わせ、ブラウザーを更新せずにユーザーの認証とサイン・インを行う方法を学びましょう。またそれをさらに一歩進め、新たに登録しようとしているユーザーが既に使われているユーザー名で登録しようとしている場合に、ユーザー名の候補を提示する方法を学びましょう。

Kris Hadlock, Web Developer/Designer, Studio Sedition

Photo of Kris HadlockKris Hadlock は、1996年から契約 Web 開発者および設計者として、SPIN Magazine、IKEA、United Airlines、JP Morgan Chase、GoDaddy Software、Fire Mountain Gems などの企業のプロジェクトを手掛けてきました。著書には『Ajax for Web Application Developers』(Sams)、『The ActionScript Migration Guide』(New Riders) があります。また、コラムニストおよびライターとしても、Peachpit.com、InformIT.com、Practical Web Design magazine などの数々の Web サイトや設計関連の雑誌で活躍しています。彼は、フォームと関数の融合を専門とする Web 設計およびソフトウェア開発会社、www.studiosedition.com の創始者でもあります。



2010年 10月 26日

Ajax を使ったフォーム送信は、ブラウザーのウィンドウをリロードせずに Web フォームを送信する、強力な手法です。jQuery ライブラリーを使用すると、Ajax によるフォーム送信をさらに一歩進め、Ajax 対応の Web フォームを最小限のコードで素早く容易に作成することができます。この記事では、jQuery を使って Ajax による基本的なフォーム送信のコードを作成する方法を学んだ後、この手法を使ってユーザーを認証する方法を学びます。さらに、Ajax で jQuery を使用してユーザーを登録する方法について説明します。その方法の例として、ユーザーが登録しようとしているユーザー名が使われているかどうかを調べ、既に使われている場合にユーザー名の候補を提示する方法を示します。どちらの方法も、フォームを送信したりページをリロードしたりする必要はありません。

jQuery をよく知らない人のために説明すると、jQuery は基本的に、JavaScript 開発を容易にするための JavaScript ライブラリーです。jQuery には、通常であればカスタム関数やカスタム・オブジェクトの作成が必要となる機能が数多く組み込まれているため、jQuery を使用する場合は最小限のコードを作成するだけで済みます。jQuery ライブラリーについての情報、また jQuery ライブラリーをダウンロードするためのリンクについては、「参考文献」を参照してください。あるいは、この記事のサンプル・コードを見るとわかるように、最新バージョンの jQuery ライブラリーを直接組み込むこともできます。

jQuery によるフォーム送信

ブラウザーをリロードせずにフォームを送信できると、いくつかのシナリオで便利です。例えば、JavaScript コードを使用するフォームのフィールドを検証してからフォームを送信したり、1 ページの Web アプリケーションとしてフォームを送信したり、あるいはこの記事で説明するように、ユーザー登録の際にユーザー名が既に存在しているかどうかを判断することができます。jQuery を使ってフォーム送信をトリガーする方法は 2 つあります。submit ハンドラーを使う方法と、click ハンドラーを使う方法です。リスト 1 は submit ハンドラーを使ってフォームを送信する方法を示しています。

リスト 1. jQuery の submit ハンドラーを使ってフォームを送信する
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function() {
  $('#submitForm').submit(function(e) {
    alert($('#sample').attr('value'));
    return e.preventDefault();
  });
});
</script>

<form id="submitForm" method="post">
  <input type="text" name="sample" id="sample" value="Enter something" />
  <input type="submit" id="submitBtn" value="Submit" />
</form>

リスト 2 は click ハンドラーを使ってフォームを送信する方法を示しています。

リスト 2. jQuery の click ハンドラーを使ってフォームを送信する
<script type="text/javascript" 
    src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function() {
  $('#submitBtn').click(function(e) {
    alert($('#sample').attr('value'));
    return e.preventDefault();
  });
});
</script>

<form id="submitForm" method="post">
  <input type="text" name="sample" id="sample" value="Enter something" />
  <input type="submit" id="submitBtn" value="Submit" />
</form>

この 2 つのリストはほとんど同じです。どちらも jQuery ライブラリーを組み込み、ページがロードされていることを ready ハンドラーを使って確認してから何らかの要素にアクセスしようとしており、それぞれのハンドラー関数の中には同じコードが含まれています。2 つの間の唯一の違いは、ハンドラーと、どの要素にハンドラーを割り当てているか、という点です。submit ハンドラーはフォーム要素に割り当てる必要がありますが、click ハンドラーはクリック可能な任意の要素に割り当てることができ、この場合は「Submit (送信)」ボタンに割り当てられています。フォーム送信によってページが更新されないように、preventDefault 関数を使用する必要があります。preventDefault 関数を使用するには、ハンドラーにイベントをパラメーターとして渡し、そのパラメーターを使って preventDefault 関数にアクセスする必要があります。

上記の方法はどちらも有効ですが、submit ハンドラーを使う方がより一般的です。しかし「Submit (送信)」ボタンが複数ある場合には、各ボタンに click ハンドラーが必要です。リスト 3 は、フォーム送信をトリガーする「Submit (送信)」ボタンが 2 つあるために click ハンドラーを使う必要のあるシナリオを示しています。

リスト 3. 「Submit (送信)」ボタンが 2 つある場合のフォーム送信
<script type="text/javascript" 
    src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript" src="register.js"></script>
<div id="container">
  <div id="message"></div>
  <form method="post" id="mainform">
    <label for="username">Username</label>
    <input type="text" name="username" id="username" value="" />

    <label for="password">Password</label>
    <input type="password" name="password" value="" />

    <input type="submit" name="action" id="login" value="Log in" />

    <h2>Extra options (registration only)</h2>

    <label for="firstname">First name</label>
    <input type="text" name="firstname" value="" />

    <label for="lastname">Last name</label>
    <input type="text" name="lastname" value="" />

    <label for="email">Email</label>
    <input type="text" name="email" value="" />

    <input type="submit" name="action" id="register" value="Register" />
  </form>
</div>

この例では、この 1 つのフォームで複数のアクションを実行できることに注意してください。つまり既存のユーザーがサイン・インすることができ、また追加のアカウント情報を入力することで、新しいユーザーを登録することができます。このシナリオでは、submit ハンドラーを使用するフォームは使えません。submit ハンドラーを使用するフォームでは、どちらのボタンでフォーム送信がトリガーされたのかを判断できないからです。そこでリスト 4 では、click ハンドラーを使用して実行アクションを判断し、その判断に従ってデータを処理しています。

リスト 4. 「Submit (送信)」ボタンに対する click ハンドラー (register.js の場合)
$(document).ready(function() {
  $("#register, #login").click(function(e) {
    var name = ($(event.target).attr('id') == 'register') ? 'Registration' : 'Login';
    return e.preventDefault();
  });
});

文書を用意できたら、「Register (登録)」ボタンと「Login (ログイン)」ボタンに click ハンドラーを割り当てます。click ハンドラーは、(event を表す) e という名前のパラメーターを受け取ります。このイベント・オブジェクトは後で、デフォルトでフォームを送信することがないようにするために使われます (これは先ほどのコード・リストの場合と同じです)。click ハンドラーが呼び出されると、クリックされた現在の要素の ID にアクセスし、この ID を使用することで、ユーザーがログインしようとしているのか、あるいは新しいユーザーの登録をしようとしているのかを判断します。

これで jQuery を使ってフォームを送信する方法を理解できたので、Ajax に jQuery と PHP を使用することでユーザーを認証する方法を調べてみましょう。


Ajax と jQuery を使用してユーザーを登録、認証する

ユーザーを認証、登録するためには、サーバー・サイドの言語とデータベースが必要です。この記事では PHP をサーバー・サイド言語として、また、MySQL をデータベースに使用しますが、特定のサーバー・サイド言語やデータベースを使用しなくても、この機能を作成することは可能です。

まず、JavaScript ファイルの中に、Ajax を使用してフォームを PHP ファイルに POST 送信する追加のコードを作成します。リスト 5 のコードの最初の部分はリスト 4 と似ており、ボタンに対する ready ハンドラーと click ハンドラーがあり、どちらのボタンがクリックされたのかを判断しています。次に slideUp 関数を使用して、(メッセージ要素が既に開いている場合には) メッセージ要素を閉じます。最初少し見ただけでは、Ajax 呼び出しが明確にはわからないかもしれません (jQuery を使わずに Ajax を作成することに慣れている人にとっては、特にわかりにくいかもしれません)。ここでは Ajax 呼び出しを行ってフォームを送信するのに簡略化した関数を使っているため、Ajax が使われていることを示す記述はコードの中にはありません。

リスト 5. Ajax と jQuery を使用して Web フォームを送信する
$(document).ready(function() {
  $("#register, #login").click(function(e) {
    var name = ($(event.target).attr('id') == 'register') ? 'Registration' : 'Login';
    $('#message').slideUp('fast');

    $.post('service.php', $('#mainform').serialize() 
        +'&action='+ $(event.target).attr('id'), function(data) {
      var code = $(data)[0].nodeName.toLowerCase();

      $('#message').removeClass('error');
      $('#message').removeClass('success');
      $('#message').addClass(code);
      if(code == 'success') {
        $('#message').html(name + ' was successful.');
      }
      else if(code == 'error') {
        $('#message').html('An error occurred, please try again.');
      }
      $('#message').slideDown('fast');
    });
    return e.preventDefault();
  });
});

post 関数はリスト 6 のコードと等価な簡略化した関数です。この関数は引数として、リクエストされたファイルへのパス、シリアライズされたフォーム・データ、そして最後にコールバック関数を取ります。フォーム・データのシリアライズは jQuery を使えば簡単で、単純に form 要素にアクセスして serialize 関数を呼び出すと、標準的なクエリー・ストリングが得られます。コールバック関数はまず、呼び出しの成功/失敗を、レスポンスの最初のノードにアクセスすることによって判断します。この PHP ファイルは呼び出しの結果を、success または error という名前のノードとして返します。呼び出しの結果を判断できたら、前回のフォーム送信で message 要素上に残されたクラスをすべて削除した後、レスポンスの成功に対応するクラスを追加します。message 要素は成功または失敗のメッセージを記述した HTML に追加され、その message を jQuery の slideDown 関数を使って開きます。

リスト 6.jQuery による Ajax 関数
$.ajax({
  type: 'POST',
  url: url,
  data: data,
  success: success
  dataType: dataType
});

データベースを操作する PHP ファイルを作成する前に、新しいユーザーの格納先として、また既存ユーザーの選択元として、使用する予定のデータベースをセットアップする必要があります。リスト 7 には、ibm_user_auth という名前の MySQL テーブルを作成するための SQL コードが含まれています。またこのコードには、ID、ユーザー名、パスワード、苗字、名前、E メール・アドレスが含まれています。ID は AUTO_INCREMENT に設定されており、主キーとして使われます。他の値はすべて tinytext ですが、パスワードは例外で varchar(32) に設定されています。これは後で、MD5 (Message-Digest 5) アルゴリズムで暗号化した値をこのパスワードを使って格納するためです。

リスト 7. ユーザー用の MySQL データベース・テーブルを作成する SQL コード
CREATE TABLE `ibm_user_auth` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` tinytext NOT NULL,
  `password` varchar(32) NOT NULL,
  `firstname` tinytext NOT NULL,
  `lastname` tinytext NOT NULL,
  `email` tinytext NOT NULL,
  PRIMARY KEY (`id`)
);

テーブルをセットアップしたら、データベースを操作するために使用する PHP コードを作成することができます。この (service.php という) ファイルを、post という Ajax 関数の中で呼び出します。リスト 8 は、このファイルを構成するコードを示しています。最初にデータベース接続用の変数を定義します。データベースの情報を設定した後、ユーザー名とパスワードがフォームの POST によって渡されていることを確認します。渡されている場合には、POST されたデータを抽出し、データベースに接続します。これでデータベースに接続できたので、POST されたデータを使って既存のユーザーのログインを行うのか、それとも新しいユーザーの登録を行うのかを判断する必要があります。この判断は、送信データから抽出されて Ajax によるフォームの POST によって送信された action 変数をチェックすることで行われます。

これが新しいユーザーの登録であると判断された場合には、苗字、名前、E メール・アドレスも送信されていることを確認する必要があります。送信されていなかった場合には、エラーが発生します。すべての要件が満たされている場合には、そのユーザー名がまだデータベースの中に存在していないことを確認します。既に存在している場合には、エラーを返します。存在していない場合には、先に進み、E メール・アドレスを検証し、新しいユーザーのデータをデータベースに挿入し、成功メッセージを返します。

これが既存ユーザーによるログインと判断された場合には、そのユーザーがデータベースに存在していることを確認します。存在している場合には、そのユーザーのデータをセッションに保存し、成功メッセージを返します。

リスト 8. サーバー・サイドの PHP コードによって JavaScript コードとデータベースを操作する
// Database connection values
define('DB_HOST', 'localhost');
define('DB_USERNAME', 'YOUR_USERNAME');
define('DB_PASSWORD', 'YOUR_PASSWORD');
define('DB_NAME', 'YOUR_DB_NAME');

if(isset($_POST['username'], $_POST['password'])) {
  extract($_POST);

  $db = mysql_connect(DB_HOST, DB_USERNAME, DB_PASSWORD);
  mysql_select_db(DB_NAME, $db);

  if($action == 'register' 
      && isset($_POST['firstname'], $_POST['lastname'], $_POST['email'])) {
    // Verify that the username is unique
    $query = mysql_query("select count(id) 
        from ibm_user_auth where username='$username'");
    $result = mysql_fetch_row($query);
    if ( $result[0] > 0 ) {
      die("<error id='0' />");
    }

    // Validate email
    if( !preg_match("^[a-z0-9,!#\$%&'\*\+/=\?\^_`\{\|}~-]+(\.[a-z0-9,!#\$%&
      '\*\+/=\?\^_`\{\|}~-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*\.([a-z]{2,})$^", 
      $_POST['email']) ) {
      die("<error id='1' />");
    }

    mysql_query("insert into ibm_user_auth 
      (username, password, firstname, lastname, email) 
      VALUES ('$username', MD5('$password'), '$firstname', '$lastname', '$email')");
    die("<success />");
  }
  else if($action == 'login') {
    $query = mysql_query("select count(id) from ibm_user_auth where 
       username='$username' and password=md5('$password')");
    $result = mysql_fetch_row($query);
    if($result[0] == 1) {
      session_start();
      $_SESSION['username'] = $username;
      die("<success />");
    }
    else die("<error id='2' />");
  }
}

?>

これで基本的な部分が動作するようになったので、ユーザビリティーを考えた方がよさそうです。このコードの最大の問題は、エラーが発生した場合に何が悪かったのをユーザーに知らせていないことです。ただし、おそらく読者の皆さんはお気付きかと思いますが、各エラー・レスポンスには id 属性が含まれています。次のセクションでは、これらの値を使用して各シナリオに対するエラー・レスポンスを作成する方法と、登録の際にユーザー名の候補を提示する方法を説明します。


登録の際のエラーを処理し、ユーザー名の候補を表示する

この段階になると、ここまでに説明したコードを使ってエラー処理を行うのは非常に簡単です。ここでは問題に対応する特定の ID を持つエラーを既に返しているため、なおさら簡単です。ID は既に設定されているので、最初はまず、ユーザー名の候補を提示する PHP コードを追加し、その後で JavaScript コードに戻ります。リスト 9 は、ユーザーが送信したデータ (この場合は苗字と名前) に基づいてユーザー名の候補を作成する方法の例を示しています。

リスト 9. ユーザーが送信したデータを使ってユーザー名の候補を作成する
// Database connection values
define('DB_HOST', 'localhost');
define('DB_USERNAME', 'YOUR_USERNAME');
define('DB_PASSWORD', 'YOUR_PASSWORD');
define('DB_NAME', 'YOUR_DB_NAME');

if(isset($_POST['username'], $_POST['password'])) {
  extract($_POST);

  $db = mysql_connect(DB_HOST, DB_USERNAME, DB_PASSWORD);
  mysql_select_db(DB_NAME, $db);

  if($action == 'register' 
      && isset($_POST['firstname'], $_POST['lastname'], $_POST['email'])) {
    // Verify that the username is unique
    $query = mysql_query("select count(id) 
      from ibm_user_auth where username='$username'");
    $result = mysql_fetch_row($query);
    if ( $result[0] > 0 ) {
      $out = "<error id='0'><suggestions>";
      $out .= "<suggestion>" . $firstname . $lastname . "</suggestion>";
      $out .= "<suggestion>" . $firstname . "_" . $lastname . "</suggestion>";
      $out .= "<suggestion>" . $lastname . $firstname . "</suggestion>";
      $out .= "<suggestion>" . $lastname . "_" . $firstname . "</suggestion>";
      $out .= "</suggestions></result>";
      die($out);
    }

    // Validate email
    if( !preg_match("^[a-z0-9,!#\$%&'\*\+/=\?\^_`\{\|}~-]+(\.[a-z0-9,!#\$%&
        '\*\+/=\?\^_`\{\|}~-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*\.([a-z]{2,})$^", 
      $_POST['email']) ) {
      die("<error id='1' />");
    }

    mysql_query("insert into ibm_user_auth 
      (username, password, firstname, lastname, email) 
      VALUES ('$username', MD5('$password'), '$firstname', '$lastname', '$email')");
    die("<success />");
  }
  else if($action == 'login') {
    $query = mysql_query("select count(id) from ibm_user_auth 
        where username='$username' 
        and password=md5('$password')");
    $result = mysql_fetch_row($query);
    if($result[0] == 1) {
      session_start();
      $_SESSION['username'] = $username;
      die("<success />");
    }
    else die("<error id='2' />");
  }
}

?>

このコードでは、登録の際にユーザー名が既に存在している場合に XML 構造を作成していることに注意してください。この XML 構造には、ユーザーが送信したデータをさまざまに組み合わせたものが含まれており、これらの組み合わせによってユーザー名の候補を作成します。この処理をさらに一歩進め、ユーザー名の候補がデータベースに存在していないことを確認してから、それらの候補をユーザーに返すこともできます。

リスト 10 は jQuery を使ってユーザー名の候補を表示する方法を示しています。

リスト 10. jQuery を使ってユーザー名の候補を表示する
$(document).ready(function() {
  $("#register, #login").click(function(e) {
  var name = ($(event.target).attr('id') == 'register') ? 'Registration' : 'Login';
  $('#message').slideUp('fast');

  $.post('service.php', 
      $('#mainform').serialize() +'&action='+ $(event.target).attr('id'), 
          function(data) {
    var code = $(data)[0].nodeName.toLowerCase();

    $('#message').removeClass('error');
    $('#message').removeClass('success');
    $('#message').addClass(code);
    if(code == 'success') {
      $('#message').html(name + ' was successful.');
    }
    else if(code == 'error') {
      var id = parseInt($(data).attr('id'));
      switch(id) {
        case 0:
          $('#message').html('This user name has already been taken. 
              Try some of these suggestions:

'); form = $(document.createElement('form')); $(data).find('suggestions > suggestion').each(function(idx, el) { radio = $(document.createElement('input')); radio.attr({type: 'radio', name: 'suggested', id: 'suggested_'+idx, value: el.innerHTML}); lbl = $(document.createElement('label')); lbl.attr('for', 'suggested_'+idx); lbl.html(el.innerHTML); form.append(radio); form.append(lbl); form.append('
'); }); $('#message').append(form); $('#message form input[type="radio"]').click(function() { $('#username').val($(this).attr('value')); }); break; case 1: $('#message').html('The e-mail entered is invalid.'); break; case 2: $('#message').html('The user name or password you entered was invalid.'); break; default: $('#message').html('An error occurred, please try again.'); } } $('#message').slideDown('fast'); }); return e.preventDefault(); }); });

今度は、エラーが返されると、ユーザーにとっては役に立たないデフォルトのエラー・メッセージを単純に表示する代わりに、エラー ID をチェックします。まず、PHP から返された XML 構造の中にある ID を解析し、続いて switch 文を使用してエラー処理を分岐させ、該当するメッセージを表示したり、関連する処理を行うコードを実行したりします。最初のエラー ID は、そのユーザー名が既にシステムに存在していることを示します。その場合は候補となるユーザー名にアクセスし、その候補をユーザーに表示することで、新しいユーザー名を選択するようにユーザーに促します。まず suggestions ノードにアクセスし、それぞれの候補に対して繰り返し処理を行います。繰り返し処理の中で、ラジオ・ボタンと、候補を含むラベルを作成します。次にそのラベルをカスタムのエラー・メッセージに追加してユーザーに表示します。この時点で、ユーザー名の候補をユーザーが選択すると、選択された名前はユーザー名のテキスト入力に自動的に追加され、登録に進みます。

次のエラー ID は E メール・アドレスが無効なことを示します。関連する処理を行うコードは、カスタムのエラー・メッセージを表示して、何が悪かったのかをユーザーに知らせるだけです。この場合、不適切なフィールドを強調するためのコード行を追加することもできます。次はログインに失敗した場合のカスタムのエラー・メッセージです。このコードで使用しているメッセージは曖昧です。これはセキュリティー上の理由から、どのフィールドが不適切であったのかを誰にでも表示してしまうのを避けたいからです。最後に、デフォルトのメッセージはリスト 5 で表示したメッセージと同じです。このメッセージはおそらく使用されませんが、フォールバック用に用意しておいた方が望ましいです。


まとめ

Ajax を使用したユーザー認証は次第に一般的になりつつあり、1 つのページで構成される Web アプリケーションではほとんど必須です。また、この記事で説明したように、ユーザー名の候補を表示するのは極めて有効です。ページの送信に成功したけれどもエラー・メッセージが返されるだけでは、ユーザーはぬか喜びすることになるからです。この記事で説明した方法によって、レスポンスは自動的でユーザーに理解しやすいものになり、より使い勝手の良い Web エクスペリエンスを提供できるようになります。


ダウンロード

内容ファイル名サイズ
Sample Ajax scripts used in this articleAjaxUserAuthentication.zip5KB

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=591196
ArticleTitle=jQuery を使って Ajax によるユーザーの認証と登録を行う
publish-date=10262010