SharePoint 2010 向けカスタム ログイン ページの作成 パート 2

SharePoint 2010 向けカスタム ログイン ページの作成 パート 2

この投稿に対する新たな序文とサイト所有者への率直なコメント:

ブログ サイトが厄介な最新バージョンにアップグレードされて以来、7 月 4 日時点の書式設定がひどいものになってしまいました。私はいつも相当な時間をかけて、この新しいバージョンに含まれる見苦しいマークアップ (またはそれらの不足) に対する応急処置を施しています。こうした書式設定の不適切な投稿がいかに読みにくいかをぜひとも Web マスターにお伝えください。当面は、書き上げた元の投稿と一緒に Word 文書をなるべく添付するようにします。テクノロジも万全ではないということです。それでは、本題に入りましょう。

このシリーズのパート 1 ( http://blogs.msdn.com/b/sharepoint_jp/archive/2010/11/16/sharepoint-2010-1.aspx ) では、まったく新しいフォーム ログイン ページを作成する方法を説明しました。 その背景となるシナリオでは、既定の UI では利用できない機能、具体的には 2 要素認証を必要とする状況を想定しました。 ここでは、また別のシナリオを想定します。 既定の UI で十分間に合うものの、ログイン時に "何らかの" 追加の処理を実行するという状況です。

この特別なシナリオでは、どのユーザーも以前アクセスしたことがないサイトについては使用条件に同意する必要があります。 そこで、まずは新しいログイン ページを作成します。 次に、ログオン イベントのハンドラーを追加し、そのイベントでユーザーが使用条件に同意したことを示す Cookie を持っているかどうかを確認します。 そうした Cookie を持たないユーザーは、同意を示すチェック ボックスをオンにするページにリダイレクトされた後、再び元のログイン ページにリダイレクトされます。 再度ログインした際には、使用条件の Cookie を持っていることが確認されるので、ホーム ページ、ドキュメントなど、ユーザーが要求したリソースが直ちに表示されます。

まず、新しいログイン ページを作成します。 この作業は、新しい ASPX ページを自分のプロジェクトに追加するだけで済みます。 このプロジェクトでは Microsoft.SharePoint への参照と Microsoft.SharePoint.IdentityModel への参照の追加が必要になります。 Microsoft.SharePoint.IdentityModel への参照の追加方法の詳細については、上記のリンクにあったパート 1 を参照してください。 作成するページの内部処理を記述するコードでは、やはり using ステートメント群を使用します。 以下に、使用するステートメント群を示します。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using Microsoft.SharePoint.IdentityModel.Pages;

using System.Diagnostics;

作成するページは、フォーム ベース認証用のログイン ページなので、FormsSignInPage を継承する必要があります。 クラス宣言は次のようになります。

public partial class AgreeLogin : FormsSignInPage

最初にコードで行うのは、ページの Load イベントのオーバーライドです。 そこでは、ページの ASP.NET ログイン コントロールに対するログイン イベント用のハンドラーを追加します。 以下に、ページの読み込みをオーバーライドするコードを示します。

protected override void OnLoad(EventArgs e)

{

try

       {

             base.OnLoad(e);

             this.signInControl.LoggingIn +=

                   new LoginCancelEventHandler(signInControl_LoggingIn);

}

catch (Exception ex)

       {

       Debug.WriteLine(ex.Message);

       }

}

ここで 1 つ補足しておきます。この処理を try…catch ブロック内で行っているのは、以下で説明するページ マークアップに問題があった場合、OnLoad(e) の呼び出し時にエラーを投げるためです。 次に、ログイン イベント ハンドラーの実装を示します。

void signInControl_LoggingIn(object sender, LoginCancelEventArgs e)

{

//look for a cookie; if not there then redirect to our accept terms page

       const string AGREE_COOKIE = "SignedTermsAgreement";

    try

       {

       //we want to check for our cookie here

              HttpCookieCollection cookies = HttpContext.Current.Request.Cookies;

              HttpCookie ck = cookies[AGREE_COOKIE];

              if (ck == null)

              //user doesn't have the cookie indicating that they've signed the

              //terms of use so redirect them

              Response.Redirect("AgreeTerms.aspx?" +

                     Request.QueryString.ToString());

}

       catch (Exception ex)

       {

       Debug.WriteLine("There was an error processing the request: " +

              ex.Message);

       }

}

ここでは、ログイン開始時に、ユーザーがサイトの使用条件に同意済みであることを示す Cookie をまず探しています。 Cookie が見つからなかった場合は、ユーザーを AgreeTerms.aspx ページにリダイレクトします。 ログイン ページが受け取った完全なクエリ文字列を記しているのは、ユーザーがログイン ページへのアクセス時に要求していたリソース、つまり、使用条件に同意した後にユーザーをどこにリダイレクトすればよいかがわかるからです。 ここで注意すべき重要な点は、使用条件のページに匿名でアクセスできるように SharePoint Web アプリケーションの web.config を変更する必要があることです。 そうしないと、仮にそのページがサイト内のその他すべてのページと同じようにセキュリティ保護されたリソースだったとすると、無限ループに陥ってしまいます。具体的には、問題の Cookie を持っていないユーザーは AgreeTerms.aspx ページにリダイレクトされますが、このページもセキュリティで保護されているのでまた元のカスタム ログイン ページに戻される、というリダイレクトの繰り返しになります。 そこで、web.config を変更して、</system.web> 終了タグの直後に以下のエントリを追加します。

<location path="_layouts/AgreeTerms.aspx">

<system.web>

<authorization>

<allow users="*" />

</authorization>

</system.web>

</location>

こうすることで、認証を受けることなく AgreeTerms.aspx ページにアクセスできるようになります。 ページの UI は凝っていますが、このページの内部処理を記述するコードは非常に単純です。

protected void SubmitBtn_Click(object sender, EventArgs e)

{

const string AGREE_COOKIE = "SignedTermsAgreement";

try

       {

       //make sure the checkbox is checked

if ((AgreeChk.Checked) && (Request.Browser.Cookies))

              {

              //create the cookie

HttpCookie ck = new HttpCookie(AGREE_COOKIE, "true");

//set the expiration so it's persisted

ck.Expires = DateTime.Now.AddYears(3);

//write it out

HttpContext.Current.Response.Cookies.Add(ck);

//get the src attribute from the query string and redirect there

Response.Redirect(Request.QueryString["Source"]);

}

else

StatusLbl.Text = "You must agree to the terms of use before continuing.";

}

catch (Exception ex)

       {

       string msg = "There was an error processing the request: " + ex.Message;

       Debug.WriteLine(msg);

       StatusLbl.Text = msg;

}

}

チェック ボックスをオンにした場合にユーザーが Cookie を受け取り、Response.Redirect によって当初要求していたソースにリダイレクトされることがよくわかると思います。 ログイン ページに戻っても、次のログイン時には問題の Cookie の存在が確認されるので、すべての処理が順調に進みます。 使用条件に同意しなかった場合は、どのページにも進めません。

コーディングの観点からの説明は以上ですが、もう 1 つ非常に重要なのがログイン ページのマークアップです。 先ほど述べたように、マークアップが適切でない場合、さまざまな問題が発生します。 まずは、以下に基本となるマークアップを示します。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AgreeLogin.aspx.cs" Inherits="FormsLoginPage.AgreeLogin,FormsLoginPage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cf32e76ff986e00f" MasterPageFile="~/_layouts/simple.master" %>

<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>

<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %>

<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<asp:Content ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">

    <SharePoint:EncodedLiteral ID="ClaimsFormsPageTitle" runat="server" text="<%$Resources:wss,login_pagetitle%>" EncodeMethod='HtmlEncode'/>

</asp:Content>

<asp:Content ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">

    <SharePoint:EncodedLiteral ID="ClaimsFormsPageTitleInTitleArea" runat="server" text="<%$Resources:wss,login_pagetitle%>" EncodeMethod='HtmlEncode'/>

</asp:Content>

<asp:Content ContentPlaceHolderId="PlaceHolderSiteName" runat="server"/>

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">

<SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" ID="ClaimsFormsPageMessage" />

 <asp:login id="signInControl" FailureText="<%$Resources:wss,login_pageFailureText%>" runat="server" width="100%">

    <layouttemplate>

        <asp:label id="FailureText" class="ms-error" runat="server"/>

        <table class="ms-input">

          <colgroup>

          <col width="25%"/>

          <col width="75%"/>

          </colgroup>

        <tr>

            <td nowrap="nowrap"><SharePoint:EncodedLiteral ID="EncodedLiteral3" runat="server" text="<%$Resources:wss,login_pageUserName%>" EncodeMethod='HtmlEncode'/></td>

            <td><asp:textbox id="UserName" autocomplete="off" runat="server" class="ms-long ms-login-textbox"/></td>

        </tr>

        <tr>

            <td nowrap="nowrap"><SharePoint:EncodedLiteral ID="EncodedLiteral4" runat="server" text="<%$Resources:wss,login_pagePassword%>" EncodeMethod='HtmlEncode'/></td>

            <td><asp:textbox id="password" TextMode="Password" autocomplete="off" runat="server" class="ms-long ms-login-textbox"/></td>

        </tr>

        <tr>

            <td colspan="2" align="right"><asp:button id="login" commandname="Login" text="<%$Resources:wss,login_pagetitle%>" runat="server" /></td>

        </tr>

        <tr>

            <td colspan="2"><asp:CheckBox id="RememberMe" text="<%$SPHtmlEncodedResources:wss,login_pageRememberMe%>" runat="server" /></td>

        </tr>

        </table>

    </layouttemplate>

 </asp:login>

</asp:Content>

実際に変更が必要になる唯一の箇所は、最初の行にある次の @Page ディレクティブです。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AgreeLogin.aspx.cs" Inherits="FormsLoginPage.AgreeLogin,FormsLoginPage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cf32e76ff986e00f" MasterPageFile="~/_layouts/simple.master" %>

黄色で強調表示された部分を、使用するカスタム アセンブリの厳密な名前に変更する必要があります。

すべてのコードを 1 つにつなげてコンパイルしたら、アセンブリを GAC に登録して、ASPX ページをレイアウト ディレクトリに追加します。 続いて、実際に実行を試す前の最後の手順として、FBA を使用する Web アプリケーション ゾーンのログイン ページを変更します。 サーバーの全体管理で [アプリケーション構成の管理] (Application Management)、[Web アプリケーションの管理] (Manage Web applications) の順に選択します。 使用する Web アプリケーションを選択し、ツール バーの [認証プロバイダー] (Authentication Providers) ボタンをクリックします。 変更するゾーンへのリンクをクリックし、表示されるダイアログで [ユーザー設定のサインイン ページ] (Custom Sign In Page) ラジオ ボタンをクリックして、ログイン ページの URL を入力します。 ここでは _layouts/AgreeLogin.aspx になります。 変更を保存すると、サイトにログインできるようになります。

最後に、カスタム ページを使用してサイトにログインしたときのようすを示しておきます。この投稿が同様の処理を実行するみなさんの参考になれば幸いです。

1. 最初のログイン

2. 使用条件を表示するページ

3. 最終的に到達する目的のページ

 

これはローカライズされたブログ投稿です。原文の記事は「Writing A Custom Forms Login Page for SharePoint 2010 Part 2」をご覧ください。