SitecoreのWebサイトで Basic 認証を実装する機会があったので、覚書としてプログラムサンプルを記載します。IISのBasic認証の場合は Windowsのユーザー情報を使用しますが、今回作成するBasic認証ではサイトコアのユーザーを認証に利用します。他のデータソースをユーザーの認証に使用する場合はコードを適宜変更してください。

検証環境

  • Sitecore 7.2

1.Basic認証の要求と認証処理をするタイミングについて

Sitecoreでは httpRequestBeginパイプラインでユーザーの解決やリクエストとアイテムへのマッピング処理を行っています。そこで、ASP.NETのAuthenticationRequestとは別の場所で認証の要求と実際の認証処理を実装することにしました。

そのため、プログラミングサンプルを掲載する前に、今回、どのタイミングでBasic認証の要求と認証処理を行っているのかを記載します。

1.1Basic認証を要求するタイミング

今回のサンプルではhttpRequestBeginパイプラインの ItemResolver プロセッサーの後に コンテンツがnullでかつ ユーザーが認証されていなければ Basic認証をリクエストするレスポンスを返すようにしています。

1.2ユーザー認証のタイミング

今回のサンプルでは httpRequestBegin パイプラインの UserResolverプロセッサーの前でBasic認証のリクエストの場合にユーザーの認証を行います。

2.Basic認証プログラミングサンプル

2.1 Basic認証を要求するプロセッサー

アイテムに対するアクセス権限がない場合に Basic認証を要求するレスポンスを返す BasicAuthenticationResponseProcessor プロセッサーを以下のように作成しました。プロセッサーでは、ユーザーが匿名ユーザーで勝つ、Websiteパラメーターで指定されたサイトがコンテキストのサイトの場合に アイテムの解決ができないかつ、サイト上の実際のファイルへのアクセスではない場合に Basic認証を要求する レスポンスを返しています。BASIC認証の要求は ステータスコード 401, WWW-Authentication ヘッダーに Basic ream=サイト名 を設定して要求しています。

    public class BasicAuthenticationResponseProcessor : HttpRequestProcessor
    {
        public string Website { get; set; }

        public override void Process(HttpRequestArgs args)
        {
            // 認証されていないユーザーのリクエストのみ処理する
            if (!Sitecore.Context.User.IsAuthenticated)
            {
                // Basic 認証の対象サイトかのチェック
                if (!Sitecore.Context.Site.Name.Equals(this.Website)) return;

                // アイテムを読み込むことができなかった場合にBasic認証のレスポンスを返す
                if (Sitecore.Context.Item == null)
                {
                    // サイト上に存在するファイルへのアクセスの場合は処理しない
                    if (!string.IsNullOrEmpty(Sitecore.Context.Page.FilePath)) return;

                    HandleBasicAuthenticationResponse();
                    args.AbortPipeline();
                }
            }
        }

        protected virtual void HandleBasicAuthenticationResponse()
        {
            HttpContext.Current.Response.AppendHeader("WWW-Authenticate", string.Format("Basic realm={0}", Sitecore.Context.Site.Name));
            HttpContext.Current.Response.StatusCode = 401;
            HttpContext.Current.Response.End();
        }
    }

2.1 Basic認証を処理するプロセッサー

クライアントからBasic認証のリクエストが行われた場合に Basic認証を行うプロセッサーとして BasicAuthenticationProcessor を作成しました。プロセッサーでは、ユーザーが匿名かつ、コンテキストサイトが Website で指定されたサイトの場合、Authorization ヘッダーがあれば Basic認証を使用したユーザーの認証処理を HandleAuthMethod で実装しています。ヘッダーを解析して Basic認証の場合は、 Base64エンコードされた文字列をでコードしています。デコードされた文字列はユーザー名とパスワードがコロン(:)で区切られた文字列なので指定されたユーザー名とパスワードを使用して Sitecore のユーザー情報を使用して認証を行っています。

認証にSitecoreのユーザー情報を使用します。SitecoreはActive Directory や Dynamics CRM やカスタムメンバシッププロバイダーなど複数のメンバシッププロバイダーを同時にかつ透過的に使用できます。そのため、プログラムを変更せずに、サイトコアデータベース上のユーザー以外のAD,CRM,独自DBなどを資格情報ストアとして使用できるはずです。

    public class BasicAuthenticationProcessor : HttpRequestProcessor
    {
        public string Website { get; set; }

        public override void Process(HttpRequestArgs args)
        {
            // 認証されていないリクエストのみ処理対象
            if (!Sitecore.Context.User.IsAuthenticated)
            {
                string header = HttpContext.Current.Request.Headers["Authorization"];
                // 認証ヘッダーがあれば認証を処理する
                if (!string.IsNullOrEmpty(header))
                {
                    HandleAuthHeader(header);
                }
            }
        }
        /// <summary>
        /// Authorization ヘッダーを解析して認証処理を行う
        /// </summary>
        /// <param name="header"></param>
        protected virtual void HandleAuthHeader(string header)
        {
            Sitecore.Diagnostics.Assert.ArgumentNotNull(header, "header");
            // Basic 認証用のヘッダーであることを確認
            if (header.StartsWith("Basic"))
            {
                var values = header.Split(' ');
                // BasicとBase64コードが空白で区切られていない場合は処理しない
                if (values.Length != 2) return;

                // Base64エンコードされた username:password をデコード
                var nameAndPassword = System.Text.Encoding.GetEncoding("iso-8859-1").GetString(System.Convert.FromBase64String(values[1]));
                var temp = nameAndPassword.Split(':');
                
                // ユーザーとパスワードが:で区切られていない場合処理しない
                if (temp.Length != 2) return;

                Login(temp[0], temp[1]);
            }
        }
        /// <summary>
        /// Authorization ヘッダーに設定されたユーザー名とパスワードでログイン
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        protected virtual void Login(string username, string password)
        {
            Sitecore.Diagnostics.Assert.ArgumentNotNull(username, "username");
            Sitecore.Diagnostics.Assert.ArgumentNotNull(password, "password");

            if (!username.Contains("\\"))
            {
                username = Sitecore.Context.Domain.Name + "\\" + username;
            }
            Sitecore.Security.Authentication.AuthenticationManager.Login(username, password, false);
        }
    }

プログラムの準備は完了です。ビルドしてエラーが発生しないことを確認してください。

2.3 コンフィグレーション用のパッチの準備

作成した2つのプロセッサーを適用するパッチの構成ファイルを作成します。冒頭で記載した通り、 UserResolver の前に BasicAuthenticationProcessor を配置します。またItemResolverの後にBasicAuthenticationResponseProcessorを配置するパッチを作成します。パッチでは website パラメーターを使用して 公開サイト website でアクセスした場合のみ Basic認証の要求と認証処理をするようにしています。

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <httpRequestBegin>
        <processor type="sc72.Web.Extensions.Pipelines.BasicAuthenticationProcessor,sc72.Web" patch:before="processor[@type='Sitecore.Pipelines.HttpRequest.UserResolver, Sitecore.Kernel']" >
          <website>website</website>
        </processor>
        <processor type="sc72.Web.Extensions.Pipelines.BasicAuthenticationResponseProcessor,sc72.Web" patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']">
          <website>website</website>
        </processor>
      </httpRequestBegin>
    </pipelines>
  </sitecore>
</configuration>

パッチまで作成したら、dllとパッチファイルを サイトコアのWebサイトに配置してください。

動作を確認する場合は、 アイテムに対してアクセス権限を設定して パブリッシュサイトでアイテム(ページ)のリクエストを行い、 Basic 認証が行われることを確認してください。

3.まとめ

今回の説明は以上です。Basic認証を要求するストラテジとして、アイテムに対するアクセス権がない場合に要求を行っていましたが、特定のパス配下の場合に要求するなど必要に応じてカスタマイズして使ってください。細かいテストを行っていないので、使用する場合は十分検証するようにしてください。