• 追加された行はこの色です。
  • 削除された行はこの色です。
#title(OpenIDでログインできるサイトを作成する1)

#navi(.NET プログラミング研究)

#contents

*OpenIDでログインできるサイトを作成する1 [#ic83db37]

OpenIDでログインできるサイトをASP.NETで作成する方法を説明します。なおここでは[[DotNetOpenId>http://code.google.com/p/dotnetopenid/]]というフリー([[New BSD License>http://www.opensource.org/licenses/bsd-license.php]])のライブラリを使った方法を紹介しますので、ほとんどがDotNetOpenIdの使い方の説明になります。

**OpenIDとは? [#p843754a]

まずはOpenIDについて簡単に説明します。OpenIDとは、1つのIDとパスワードでOpenIDに対応したすべてのサイトにログインできるシステムです。または、そのIDをOpenIDと言うこともあります。OpenIDに対応したサイトはまだまだ少ないですが、Google、Yahooをはじめ、マイクロソフトのWindows Live IDもOpenIDとして使用できるようになるということなので、これからどんどん増えていくでしょう。

-[[OpenID - Wikipedia>http://ja.wikipedia.org/wiki/OpenID]]

OpenIDの仕様は、「[[OpenID ≫ Read the Specifications>http://openid.net/developers/specs/]]」で公開されています。

実際にOpenIDを使った方が分かりやすいと思いますので、実際に使用する例を示します。

まずはOpenIDを取得します。OpenIDを発行しているサイトは、日本では、[[OpenID.ne.jp>http://www.openid.ne.jp/]]、[[livedoor>http://auth.livedoor.com/openid/]]、[[Yahoo! JAPAN>http://developer.yahoo.co.jp/other/openid/]]、[[はてな>http://www.hatena.ne.jp/info/openid/]]、[[Jugemkey>http://jugemkey.jp/api/openid/]]、[[mixi>http://developer.mixi.co.jp/openid]]、[[BIGLOBE>http://openid.biglobe.ne.jp/forrp.html]]、[[エキサイト>http://openid.excite.co.jp/]]などがあります。Googleでは[[Blogger>http://www.blogger.com/]]で作成したブログのURLを、Six Apartでは[[Vox>http://www.sixapart.jp/vox/]]で作成したブログのURLをOpenIDとして使用することができます。海外では、[[myOpenID>https://www.myopenid.com/]]などが有名です。

ここでは、[[OpenID.ne.jp>http://www.openid.ne.jp/]]でOpenIDを作成したものとします((OpenID.ne.jpでOpenIDを作成することをお勧めしているわけではありません。))。登録に成功すると、"http://username.openid.ne.jp/"のような"OpenID URL"をもらえます。もし既にYahoo! JAPANなどのアカウントを持っているならば、サイトで簡単な手続きを行うことですぐにOpenIDを発行してもらえますので、それで十分でしょう。

次にこのOpenIDを使って、OpenIDに対応したサイトにログインします。ここでは[[Choix>http://www.choix.jp/]]というサイトにログインしてみます。(ただしユーザー登録が必要ですので、正確に言うと、OpenIDを使ってログインできるようにユーザー登録してみます。)

Choixのトップページの上に「ログイン」というリンクがありますので、これをクリックしてログインページに移動します。ログインページにはユーザー名とパスワードを入力するフォームがありますが、その右に「OpenIDでログイン」というフォームがあり、テキストボックスが1つだけあります。ここに先ほど取得したOpenID(http://username.openid.ne.jp/)を入力します。

#netvideos(<a href="http://www.flickr.com/photos/dobondotnet/3625887324/" title="chox1 by DOBON.NET on Flickr"><img src="http://farm3.static.flickr.com/2470/3625887324_1e2bf9609b.jpg" width="500" height="374" alt="chox1" /></a>,,,,flickr)

ただし、Choixのログインページにも書かれていますが、ここにURLを入力しなくてもよい場合があります。例えば、Yahoo! JAPANでは"yahoo.co.jp"と入力することができます。また、サイトによっては、OpenIDを入力するテキストボックスが用意されておらず、その代りに「Yahoo!のアカウントでログイン」のようなリンクが用意されている場合もあります。なお、Choixのログインページにも同様のリンクがありますが、このリンクはOpenIDではないようです。

OpenIDを入力して「ログイン」ボタンを押すと、OpenID.ne.jpのページに飛ばされます。ここでパスワードを入力することを要求されます。ただし、すでにOpenID.ne.jpにログインしているときは、このページが表示されず、次の「OpenID 確認」ページが表示されます。

#netvideos(<a href="http://www.flickr.com/photos/dobondotnet/3625069499/" title="chox2 by DOBON.NET on Flickr"><img src="http://farm3.static.flickr.com/2428/3625069499_d382804072.jpg" width="500" height="374" alt="chox2" /></a>,,,,flickr)

パスワードを入力して「ログイン」ボタンを押すと、今度は「OpenID 確認」というページに飛ばされます。

#netvideos(<a href="http://www.flickr.com/photos/dobondotnet/3653627909/" title="chox3 by DOBON.NET on Flickr"><img src="http://farm4.static.flickr.com/3602/3653627909_f44f9383e6.jpg" width="500" height="374" alt="chox3" /></a>,,,,flickr)

このページでは、

現在 https://www.choix.jpで、あなたのOpenid (http://username.openid.ne.jp/)があなたの個人情報と一致しているという証明を要求しています。https://www.choix.jp に証明するための必要な個人情報を追加チェックし、認証してください。

という文章が表示されます。そしてその下のチェックボックスでChoxに送信する個人情報を選択します。"Optional"となっている項目は送信を拒否しても問題ありませんが、"Required"となっている項目は必須項目ですので、送信を拒否するとログインできないなどの不具合が起こる可能性があります(Choixでは拒否しても問題ないようです)。Choixの場合はこのように個人情報を要求しますが、そうしないサイトも多いです。

その下に「一度だけ認証」「認証状態を保持」「認証拒否」という3つのボタンが表示されますが、ここでは「一度だけ認証」をクリックします。「認証状態を保持」をクリックすると、次にChoixにログインするときはこのページが表示されません。

ボタンをクリックすると、今度はChoixのページに戻ってきます。

#netvideos(<a href="http://www.flickr.com/photos/dobondotnet/3625069591/" title="chox4 by DOBON.NET on Flickr"><img src="http://farm4.static.flickr.com/3339/3625069591_c798e5f043.jpg" width="500" height="374" alt="chox4" /></a>,,,,flickr)

「Choix IDの設定」という会員登録のフォームページが表示され、IDとメールアドレスを入力する必要があります。ただし、パスワードは必要ありません。また、個人情報を送った場合は、IDとメールアドレスにOpenID.ne.jpに登録されているニックネームとE-mailの情報があらかじめ入力されます。

Choixの会員登録が完了すれば、次回からはOpenIDでログインが可能になります。

**この記事で使用している用語 [#c86378fb]

OpenIDでよく使われる用語があります。この記事でもこれらの用語を使いますので、まずは用語の説明をします(3つだけです)。

:OpenID Provider|OPと略します。OpenIDの認証をするサーバーです。上記の例では、OpenID.ne.jpです。
:Relying Party|RPと略します。OpenIDの認証を受け取る側のWebアプリケーションのことです。上記の例では、Choixのことです。この記事で紹介するのは、RPを作成する方法ということになります。
:Claimed Identifier|ユーザーのOpenID URLのことです。上記の例では、"http://username.openid.ne.jp/"のことです。ちなみに、先ほどの例でOpenIDを入力する欄に"yahoo.co.jp"のように入力できると書きましたが、この"yahoo.co.jp"は"OP Identifier"といいます。Claimed IdentifierとOP Identifierを合わせて、"User-Supplied Identifier"と呼びます。

さらにここでは、エンドユーザー(OpenIDを使ってログインしようとしているユーザー)のことを単にユーザーと表記します。

**DotNetOpenIdを使ってRelying Partyを作成する [#qcf1a58c]

それでは本題に入りましょう。ここではDotNetOpenIdを使いますので、まずは[[DotNetOpenIdのサイト>http://code.google.com/p/dotnetopenid/]]からダウンロードして、適当なフォルダに展開してください。なお私が使用したDotNetOpenIdのバージョンは、2.5.4.9045 です。DotNetOpenIdのバージョンが2.xであれば.NET 2.0に対応しており、3以上ならば.NET 3.5に対応しているようです。

#column(補足){{
DotNetOpenId以外にも.NET Frameworkで使用できるライブラリはいくつかあります。私が調べた限りでは、以下のようなライブラリが公開されています。

-[[OpenID Consumer for .NET 2.0>http://extremeswank.com/aspnet_openid.html]]
-[[DNOpenID>http://www.codeplex.com/DNOpenID]]
-[[OpenID implementation in C# and ASP.NET>http://madskristensen.net/post/OpenID-implementation-in-Csharp-and-ASPNET.aspx]]
}}

#column(補足){{
DotNetOpenIdを使用したサンプルは、DotNetOpenIdアーカイブ内のSamplesフォルダにあります。RelyingPartyPortalプロジェクトは、RPを作成するサンプルです。RelyingPartyMvcプロジェクトは、RPをASP.NET MVCで作成するサンプルです(ASP.NET MVCがインストールされていないと開くことができません)。ProviderPortalプロジェクトは、OPを作成するサンプルです。
}}

Visual StudioでRPのプロジェクトを新規に作成するか、すでにある場合は開きます。

Web.configの"authentication mode"が"Forms"以外の場合は、"Forms"にします。また、ログインページを"login.aspx"以外にする場合は、"loginUrl"も指定しておきます。

#pre{{
<configuration>
  <system.web>
    <authentication mode="Forms">
      <forms name="RelyingPartySession" loginUrl="login.aspx"/>
    </authentication>
  </system.web>
</configuration>
}}

Visual Studioの左側にあるツールボックスで右クリックしてメニューを表示し、「アイテムの選択」を選択します。「ツールボックスアイテムの選択」ダイアログが表示されるので、下にある「参照」ボタンをクリックして、"DotNetOpenId.dll"を選択します。"OpenIdLogin"などが選択されますので、そのまま"OK"をクリックします。

#netvideos(<a href="http://www.flickr.com/photos/dobondotnet/3625887192/" title="DotNetOpenId2 by DOBON.NET on Flickr"><img src="http://farm3.static.flickr.com/2476/3625887192_9f5ce12d19.jpg" width="500" height="284" alt="DotNetOpenId2" /></a>,,,,flickr)

これでツールボックスに"OpenIdLogin"などのコントロール追加されます。

ログインページ"login.aspx"を作成し、そのページにツールボックスから"OpenIdLogin"をドラッグ&ドロップして配置します。

#netvideos(<a href="http://www.flickr.com/photos/dobondotnet/3625069617/" title="OpenIdLogin1 by DOBON.NET on Flickr"><img src="http://farm4.static.flickr.com/3631/3625069617_40fb17fc36.jpg" width="500" height="108" alt="OpenIdLogin1" /></a>,,,,flickr)

OpenIdLoginコントロールを配置しただけのlogin.aspxのソースは以下のようになります。

#code(vbnet){{
<%@ Page Language="vb" %>
<%@ Register assembly="DotNetOpenId" namespace="DotNetOpenId.RelyingParty"
 tagprefix="RP" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <RP:OpenIdLogin ID="OpenIdLogin1" runat="server" />
    </div>
    </form>
</body>
</html>
}}

#code(csharp){{
<%@ Page Language="C#" %>
<%@ Register assembly="DotNetOpenId" namespace="DotNetOpenId.RelyingParty"
 tagprefix="RP" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <RP:OpenIdLogin ID="OpenIdLogin1" runat="server" />
    </div>
    </form>
</body>
</html>
}}

実はこれだけでRPが出来上がっています。しかしこれだけではログインできたか分かりませんので、"Default.aspx"ページにLoginStatusコントロールとLoginNameコントロールを配置しておきます。"Default.aspx"のソースは以下のようになります。

#code(vbnet){{
<%@ Page Language="vb" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:LoginName ID="LoginName1" runat="server" />
        <asp:LoginStatus ID="LoginStatus1" runat="server" />
    </div>
    </form>
</body>
</html>
}}

#code(csharp){{
<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:LoginName ID="LoginName1" runat="server" />
        <asp:LoginStatus ID="LoginStatus1" runat="server" />
    </div>
    </form>
</body>
</html>
}}

これで完了です。早速実行してみましょう。

Default.aspxをWebブラウザで開くと、LoginStatusコントロールの「ログイン」リンクがあります。これをクリックすると、login.aspxが開きます。

#netvideos(<a href="http://www.flickr.com/photos/dobondotnet/3625069361/" title="OpenIdLogin2 by DOBON.NET on Flickr"><img src="http://farm3.static.flickr.com/2113/3625069361_0539b16b6a.jpg" width="500" height="408" alt="OpenIdLogin2" /></a>,,,,flickr)

このページでOpenIDを入力し、「Login」ボタンをクリックします。すると、先ほどのChoixの例と同じように、OPのページに飛んでログインを行い、再びDefault.aspxに戻ってきます。OpenIDの認証に成功すると、Default.aspxのLoginNameコントロールにOpenID(Claimed Identifier)が表示され、LoginStatusコントロールは「ログアウト」となりますので、実際にログインできたことが確認できます。このようにOpenIdLoginコントロールはOpenIDの認証に成功すると自動的にログインします。

たったこれだけでRPを作れてしまいました。

**Claimed Identifierを取得する [#bf0a45ea]

認証に成功した時、OPが送信するユーザーのClaimed Identifierを取得するには、IAuthenticationResponse.ClaimedIdentifierプロパティを使います。IAuthenticationResponseを取得するには、OpenIdLoginコントロールのLoggedInイベントハンドラのパラメータであるOpenIdEventArgsオブジェクトのResponseプロパティを使います。

また、これとは別に、IAuthenticationResponse.FriendlyIdentifierForDisplayプロパティというものもあります。ClaimedIdentifierプロパティで取得できるClaimed Identifierが意味不明の文字列となる場合があり、このような文字列を表示されてもユーザーはピンと来ないかもしれません。FriendlyIdentifierForDisplayプロパティはより分かりやすい文字列を返しますので、表示用として適しています。具体的には、Claimed Identifierの"http://"が取られるなどするようです。ただし、FriendlyIdentifierForDisplayプロパティを表示以外の用途で使ってはいけません。例えば、Claimed Identifierでユーザーを特定する場合は、必ずClaimedIdentifierプロパティの値を使います。

ちなみに、LoginNameコントロールに表示される名前は、ClaimedIdentifierプロパティと同じものになるようです。

**OPに移動する前にキャンセルする [#ma5e5091]

例えば、OpenIdLoginコントロールのテキストボックスに入力されたURLを見て、このOpenID URLは拒否したいという場合もあります。そのようなときは、OpenIdLoginのLoggingInイベントでOpenIdEventArgs.CancelプロパティをTrueにすれば、OPのページに移動するのをキャンセルできます。

**OPからキャンセルされたり、認証が失敗した時 [#r487b3e0]

認証を行うOPのページでユーザーがキャンセルをすることもできます。この時、OpenIdLoginコントロールのCanceledイベントが発生します。

また、認証に失敗したときは、Failedイベントが発生します。エラーの詳細は、OpenIdEventArgs.Response.Exceptionプロパティに格納されます。

**LoggedIn、LoggingIn、Canceled、Failedイベントを使用した例 [#f974413e]

先に紹介したLoggedIn、LoggingIn、Canceled、Failedイベントを使用して"login.aspx"を書き換えた例を以下に紹介します。

LoggedInイベントハンドラでは、Claimed Identifierをセッションに保存しています。LoggingInイベントハンドラでは、ユーザーが入力したOpenID URLに"hogehoge.com"が含まれていたらキャンセルするようにしています。Canceledイベントハンドラでは、Labelコントロールにキャンセルされたというメッセージを表示しています。Failedイベントハンドラでは、Labelコントロールにエラーメッセージを表示しています。

#code(vbnet){{
<%@ Page Language="vb" %>
<%@ Register assembly="DotNetOpenId" namespace="DotNetOpenId.RelyingParty"
 tagprefix="RP" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    
    'ログインしたとき 
    Protected Sub OpenIdLogin1_LoggedIn(ByVal sender As Object, _
                                        ByVal e As OpenIdEventArgs)
        'Claimed Identifierを覚えておく 
        Session("ClaimedIdentifier") = e.Response.ClaimedIdentifier
    End Sub

    'ログインする前 
    Protected Sub OpenIdLogin1_LoggingIn(ByVal sender As Object, _
                                         ByVal e As OpenIdEventArgs)
        'OpenID URLに"hogehoge.com"が含まれていたらキャンセルする 
        If OpenIdLogin1.Text.IndexOf("hogehoge.com") > -1 Then
            MessageLabel.Text = """hogehoge.com""はダメです。"
            e.Cancel = True
        Else
            MessageLabel.Text = ""
        End If
    End Sub

    'OPからキャンセルされたとき 
    Protected Sub OpenIdLogin1_Canceled(ByVal sender As Object, _
                                        ByVal e As OpenIdEventArgs)
        MessageLabel.Text = "キャンセルされました。"
    End Sub

    '認証が失敗したとき 
    Protected Sub OpenIdLogin1_Failed(ByVal sender As Object, _
                                      ByVal e As OpenIdEventArgs)
        MessageLabel.Text = "エラー : " & e.Response.Exception.Message
    End Sub
    
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <RP:OpenIdLogin ID="OpenIdLogin1" runat="server" 
            oncanceled="OpenIdLogin1_Canceled"
            onfailed="OpenIdLogin1_Failed" 
            onloggedin="OpenIdLogin1_LoggedIn"
            onloggingin="OpenIdLogin1_LoggingIn" />
    </div>
    <div>
        <asp:Label ID="MessageLabel" runat="server" EnableViewState="False">
        </asp:Label>
    </div>
    </form>
</body>
</html>
}}

#code(csharp){{
<%@ Page Language="C#" %>
<%@ Register assembly="DotNetOpenId" namespace="DotNetOpenId.RelyingParty" tagprefix="RP" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

    //ログインしたとき
    protected void OpenIdLogin1_LoggedIn(object sender, OpenIdEventArgs e)
    {
        //Claimed Identifierを覚えておく
        Session["ClaimedIdentifier"] = e.Response.ClaimedIdentifier;
    }
    
    //ログインする前
    protected void OpenIdLogin1_LoggingIn(object sender, OpenIdEventArgs e)
    {
        //OpenID URLに"hogehoge.com"が含まれていたらキャンセルする
        if (OpenIdLogin1.Text.IndexOf("hogehoge.com") > -1)
        {
            MessageLabel.Text = "\"hogehoge.com\"はダメです。";
            e.Cancel = true;
        }
        else
        {
            MessageLabel.Text = "";
        }
    }
    
    //OPからキャンセルされたとき
    protected void OpenIdLogin1_Canceled(object sender, OpenIdEventArgs e)
    {
        MessageLabel.Text = "キャンセルされました。";
    }

    //認証が失敗したとき
    protected void OpenIdLogin1_Failed(object sender, OpenIdEventArgs e)
    {
        MessageLabel.Text = "エラー : " + e.Response.Exception.Message;
    }
    
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <RP:OpenIdLogin ID="OpenIdLogin1" runat="server" 
            oncanceled="OpenIdLogin1_Canceled"
            onfailed="OpenIdLogin1_Failed" 
            onloggedin="OpenIdLogin1_LoggedIn"
            onloggingin="OpenIdLogin1_LoggingIn" />
    </div>
    <div>
        <asp:Label ID="MessageLabel" runat="server" EnableViewState="False">
        </asp:Label>
    </div>
    </form>
</body>
</html>
}}

**次回予告 [#i360e722]

次回はOpenIdLoginコントロールについてさらに詳しく紹介する予定です。

**コメント [#a03d14c4]
#comment

//これより下は編集しないでください
#pageinfo([[:Category/.NET]] [[:Category/ASP.NET]],2009-06-16 (火) 03:33:55,DOBON!,2009-06-16 (火) 03:33:55,DOBON!)

[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]