きよくらの備忘録

「三日坊主と呼ばせない!日記」改め。主にソフトウェア開発関連の話題。

ASP.NET Identityのカスタマイズ-既存のユーザ情報を使っての認証

ASP.NET MVC 5になって、認証のインフラとしてASP.NET Identitiyが登場しました。今までのMembership Providerと比べてかカスタマイズもし易く、使い勝手の良いところが素敵です。

ちょうど、『既に別システムで運用されている独自のユーザー情報をそのまま利用し、アプリケーションのユーザー認証を行う』という要件で実装する機会がありました。せっかくなので要点を抜粋したサンプルをgithubにおいてみました。

kiyokura/AspNetIdentityCustomStoreSample

 

 

以下に、ポイントを軽く紹介してみたいと思います。

概要

基本的には、@okazuki さんの ASP.NET Identityカスタマイズに挑戦 で取り上げられているサンプルそのままです。ですので、差分となるポイントを中心に(というかそこだけ)を紹介しようと思います。

ポイントとなるのは

  1. 独自のユーザー情報クラスの作成
  2. 独自のユーザーストアクラスの作成
  3. 独自のパスワードハッシュアルゴリズムの実装
  4. UserManagerで利用するパスワードハッシュアルゴリズムを設定

です。

@okazuki さんのサンプルに対して追加されているのが、特に『3. 独自のパスワードハッシュアルゴリズムの実装』となっています。 また、既に存在するデータを使って認証を行うだけなので、ユーザーの登録に必要な処理などは実装していません(そのあたりは既存の別の管理ツールなどがある想定)。

 

 

独自のユーザー情報の例

既に別システムで利用されているユーザ情報の例として、以下のようなテーブルに格納されているものを想定します。

CREATE TABLE [dbo].[UserInfo] (
    [UserID]    UNIQUEIDENTIFIER NOT NULL,
    [LoginCode] NVARCHAR (50)    NOT NULL,
    [FirstName] NVARCHAR (50)    NOT NULL,
    [LastName]  NVARCHAR (50)    NOT NULL,
    [EMail]     NVARCHAR (MAX)   NOT NULL,
    [Password]  NVARCHAR (MAX)   NOT NULL,
    PRIMARY KEY CLUSTERED ([UserID] ASC)
);
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_UserInfo_U1]
    ON [dbo].[UserInfo]([LoginCode] ASC);

また、Passwordには、DBのユーザー関数として定義されている独自のアルゴリズムでハッシュ化されていることを想定しています。

 

独自のユーザー情報クラスの作成

IUser の実装として、MyAppUser という名前で作りました。基本的にはDBのテーブルの情報をそのまま読み込むつもりの構成です。ハッシュ化されたパスワードもここで保持するようにしておきます。

/// サンプルプロジェクト:Models/Auth/Auth.csに実装
public class MyAppUser : IUser
{
    /// <summary>
    /// DB上の一意キー
    /// </summary>
    public string Id { get; set; }

    /// <summary>
    /// ログイン認証に使うユーザーID
    /// </summary>
    public string UserName { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string EMail { get; set; }

    /// <summary>
    /// ハッシュ化されてDBに格納されているパスワード
    /// </summary>
    public string HashedPassword { get; set; }
}

 

独自のユーザーストアクラスの作成

カスタムストアを実装します。今回はログイン認証に必要最低限の機能を実現するため、IUserStore と IUserPasswordStore のみの実装とします。処理を実装するメソッドも、とりあえず必要最低限の2つのみにしています。

/// サンプルプロジェクト:Models/Auth/Auth.csに実装
using Dapper;

~

public class MyAppUserStore : IUserStore<MyAppUser>, IUserPasswordStore<MyAppUser>
{
    public Task<MyAppUser> FindByNameAsync(string userName)
    {
        // DBを検索する。Entitiy Franewrokを使っても良い。今回は諸事情と好みの問題でDapperを利用。
        using (var cn = DbConnectionFactory.Create("DefaultConnection"))
        {
            cn.Open();
            var sql = " SELECT " +
                      "   Convert(nvarchar(MAX), UserID) AS ID , LoginCode AS UserName , FirstName , LastName , EMail , Password AS HashedPassword " +
                      " FROM " +
                      "   UserInfo " +
                      " WHERE " +
                      "   LoginCode = @UserName ";

            var users = cn.Query<MyAppUser>(sql, new { UserName = userName });
            return Task.FromResult(users.FirstOrDefault());
        }
    }

    public Task<string> GetPasswordHashAsync(MyAppUser user)
    {
        // DBから取得されたMyAppUserにはハッシュ化されたパスワードが既に格納されているはず。
        // なのでここではそれをそのまま返す
        return Task.FromResult(user.HashedPassword);
    }

    public Task CreateAsync(MyAppUser user)
    {
        throw new NotImplementedException();
    }

    public Task DeleteAsync(MyAppUser user)
    {
        throw new NotImplementedException();
    }

    public Task<MyAppUser> FindByIdAsync(string userId)
    {
        throw new NotImplementedException();
    }

    public Task UpdateAsync(MyAppUser user)
    {
        throw new NotImplementedException();
    }

    public void Dispose()
    {
        //例外は出ないようにNotImplementedExceptionは消しておく
    }

    public Task<bool> HasPasswordAsync(MyAppUser user)
    {
        throw new NotImplementedException();
    }

    public Task SetPasswordHashAsync(MyAppUser user, string passwordHash)
    {
        throw new NotImplementedException();
    }
}

 

独自のパスワードハッシュアルゴリズムの実装

ASP.NET Identity では 標準のパスワードハッシュアルゴリズムとして PasswordHasher クラスが用意されています。 しかし、今回のように別システムで独自のアルゴリズムによってハッシュ化されている場合は、当然これではうまくいきません。 その場合は、IPasswordHasher の実装としてHasherを作ることで解決できます。以下は、DB側で実装されたユーザー定義関数を利用してハッシュ化されたパスワードを取得するカスタム・ハッシャーの例です。

/// サンプルプロジェクト:Models/Auth/Auth.csに実装
public class MyPasswordHasher : IPasswordHasher
{
    public string HashPassword(string password)
    {
        using (var cn = DbConnectionFactory.Create("DefaultConnection"))
        {
            cn.Open();
            var sql = "SELECT dbo.HashPassword(@RawPassword)";
            return cn.Query<string>(sql, new { RawPassword = password }).Single();
        }
    }

    public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
    {
        if (hashedPassword == this.HashPassword(providedPassword))
        {
            return PasswordVerificationResult.Success;
        }
        else
        {
            return PasswordVerificationResult.Failed;
        }
    }
}

 

UserManagerで利用するパスワードハッシュアルゴリズムを設定

作成したカスタム・ハッシャーをUserManagerが利用するように設定します。といっても、UserManagerのPasswordHasher プロパティーにインスタンスを設定してあげるだけです。こんな感じで設定してやれば、あとは勝手に使ってくれます。

/// サンプルプロジェクト: Controllers/AuthController.csに実装
//ログインを処理するコントローラのActionメソッド
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        // カスタマイズのポイント:
        //   UserManagerのインスタンス作成時、
        //     型引数:カスタマイズしたユーザー情報
        //     コンストラクタの引数:カスタマイズしたユーザストアのインスタンス
        //   をそれぞれ渡す
        var userManager = new UserManager<MyAppUser>(new MyAppUserStore());

        // カスタマイズのポイント:
        //   パスワードのハッシュ化アルゴリズムとして、IPasswordHasherを実装したカスタムクラスのインスタンスを設定
        userManager.PasswordHasher = new MyPasswordHasher();

        var user = await userManager.FindAsync(model.UserName, model.Password);
        if (user != null)
        {
            var authentication = this.HttpContext.GetOwinContext().Authentication;
            var identify = await userManager.CreateIdentityAsync(
                user,
                DefaultAuthenticationTypes.ApplicationCookie);
            authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            authentication.SignIn(new AuthenticationProperties() { IsPersistent = false }, identify);

            return Redirect(returnUrl);
        }
        else
        {
            ModelState.AddModelError("", "ログインIDまたはパスワードが無効です。");
        }
    }
    
    // ここで問題が発生した場合はフォームを再表示します
    return View(model);
}

 

別のカスタマイズ方法も…

今回は UserManager はそのまま利用し、ユーザーストアをカスタマイズする方法でやってみました。

しかし、ユーザー認証処理そのものがDB側でストアドプロシージャによって行われ、アプリ開発者にそのロジックが公開されていないようなケースもあると思います。 その場合はユーザーストアのカスタマイズで対応するのでは無く、、UserManager を継承したカスタムのUserManager を作成し、FindAsyncメソッドをオーバーライドしてやるほうが簡単かもしれません。

 

まとめ

ということで、簡単ですがこんな感じで既存の独自構成のユーザ情報を使っての認証を実装してみました。特にエンタープライズアプリケーションだと、社内で別途管理統一されたユーザーデータを使って個々のアプリケーションで認証を行う…という事も少なくないのではないかと思っています。そういった場合でも、ASP.NET Identityでは比較的簡単なカスタマイズで対応できると思います。 また今回は読み込みのみですが、このテーブルに対してユーザの登録やパスワードの変更処理などをカスタマイズして実装することも容易だと思います。

 

なお、今回は下記の情報を参考にさせていただきました。

オンラインイベント"dotnetConf"が開催されます(ただし英語)

日本時間の2014.06.26-27に、開発者向けオンラインイベント「dotnetConf」が開催されます(※ただし英語。現地太平洋夏時間では2014.06.25-26)。

 

f:id:kiyokura:20140620065740p:plain

 

詳細はこちらのdotnetConfオフィシャルサイトから。

日本時間では深夜の時間帯とまあアレなのですが、Channel 9にて無償で視聴できますので、興味がある方はご覧になってみてはいかがでしょうか*1

特に事前登録の必要はないようです。が、参加者の把握的な意味で助かるので事前登録してね、的なことも書いてあります。視聴予定の方は登録してあげても良いのではないかと思います。

自分のメモの意味で、スケジュールを抜粋しておきます。

※2014.06.20現在の状態です。(まだ未定っぽいところもありますし、今後更新されていくと思います。気が付いたら更新する予定) ※2014.06.23 Day1のセッション順変更と、Day2のタイトルのアップデートを反映。

 

 

day 1 - JST 2014.06.26 01:00 Start

PDT JST Session
09:00 01:00 State of .NET (Keynote)
09:15 01:15 New Innovations in the .NET Runtime
10:15 02:15 The Future of C#
11:15 03:15 Building Universal Windows Apps with XAML and C# in Visual Studio
12:00 04:00 .NET Native Deep Dive
12:45 04:45 Fun with .NET - Windows Phone, LEGO Mindstorms, and Azure
13:15 05:15 Kinect for Windows
14:15 06:15 What's new in XAML Platform & Tooling
15:15 07:15 Developing native iOS, Android, and Windows apps with Xamarin
16:15 08:15 What's new for WPF Developers
17:00 09:00 .NET Micro Framework and IoT

 

day 2 - JST 2014.06.27 01:00 Start

PDT JST Session
09:00 01:00 ASP.NET Today and Tomorrow (Keynote)
09:30 01:30 ASP.NET Web Forms
10:00 02:00 ASP.NET MVC 6 (now with integrated Web API!)
11:00 03:00 Entiy Framework
12:00 04:00 Taking Your ASP.NET Apps to the Cloud with Microsoft Azure Web Sites
13:00 05:00 ASP.NET Publishing Explained
14:00 06:00 ASP.NET Identity
15:00 07:00 Dependency Injection and Testability in .NET
16:00 08:00 SignalR
16:30 08:30 ASP.NET vNext 101

 

個人的には、ASP.NET関係が2日目に固まっているのでそちら注目の予定です。 (27日は仕事はお休みにしようかな…)

*1:リアルタイムで見なくても、多分後で動画が公開されるんじゃないかとは思います

Room Metro #23 大阪でNancyについてLTさせていただきました

先週土曜日の3/1、Room Metro #23 大阪の勉強会に参加しました。せっかくなのでLTもやらせていただきました。

Room Metro #23 大阪

 

今回は秋に一度台風の影響で中止になった回のリベンジ。講師も大阪近郊の方だけでなく、北陸から3名のMVPが登壇されました。

ASP.NETの最近の状況&TypeScriptの紹介、Xamarinの実際にチームでクロスプラットホームのアプリ開発を行った経験を踏まえたお話、MVP Showcaseで一位をとったアプリを例にUI/UXの考え方のお話、クリティカルな業務でSignalRを適用した際の実際のチューニングを行ったお話、先日のアップデートで利用できるようになったAzure Mobile Serviceの.NETでのサービス実装のお話、C#によるメタプログラミングの非常に濃いお話。

……と、ものすごく濃くてためになるお話ばかりでした。

講師の皆様、スタッフの皆様に感謝です。

 

最後にLTで私もASP.NETの軽量フレームワーク、Nancyのお話をさせていただきました*1

主催のさおさんからLTのお誘いを頂いて二つ返事を返したあと、「そういえば丸山さんがASP.NET全般のお話されるし、SignalRとWebAPIのお話もあるしで下手に話すを被りまくるのでは」と若干悩んだあげく、「さすがにNancyは被らんやろう」ということでこのネタにさせていただきました。

 

「ちょっとしたことやりたいだけだから、MVCとかWeb API使うまでもないんだけどなぁ…」なんてときにちょうど良い感じの軽量かつシンプルなフレームワークだとおもいます。もし未体験でしたら軽くだけでも触ってみられると良いかもですよ!

 

*1:実は年末の岡山の合同勉強会のときにやったネタのマイナーチェンジ版

Windows Azureの実践的なチュートリアルが公開さています

ASP.NET Official Siteに、Windows Azureを利用したクラウドアプリケーションのかなり実践的なチュートリアルが公開されていました。

Building Real-World Cloud Apps with Windows Azure

『Fix It!』という架空の(?)バグトラッカーのサービスアプリケーションを作成してWindows Azureで運用する、というお題にそってすすめるチュートリアルです*1。が、このチュートリアル、そのへんのチュートリアルとはちょっと違います。

 

クラウド開発パターン(Cloud development patterns)』として、13のパターン(トピック)にまとめて一気通貫で解説するチュートリアルになっています。

アジェンダからかいつまんで、トピックを翻訳すると、多分こんな感じです(※翻訳は適当なので原文見てください)。

  • 全てを自動化する(スクリプトベースでAzureを操作するとか)
  • ソース管理(DevOpsを実践する流れについて/センシティブなデータをどう管理するか/VSでGit)
  • 継続的インテグレーションとデプロイ
  • Webアプリ開発のベストプラクティス
  • シングルサインオンWindows Azure Active Directoryを利用)
  • データストレージの選択肢
  • データのパーティショニング戦略
  • Blobストレージ
  • 障害を生き延びるための設計(障害の種類やSLAの話)
  • 監視と遠隔測定(New Relic/ログの仕込み方の話)
  • 一過性の障害のハンドリング(例外時の再試行などの戦略と実装)
  • 分散キャッシュ
  • キュー中心パターン(キューを効果的につかって高可用性を実現するとかそういう話)

これだけ見ても、単純に「サンプルアプリケーションをAzureにデプロイしましたよ、はい終わり!」というチュートリアルとは、一線を画していますね。

 

上記Webサイトでのチュートリアルの他、PDF形式のe-bookやサンプルソースも提供されています。スクリーンショットや図などもふんだんに使われていますので、英語が苦手な方でも、Google翻訳やBing Translatorを使えば十分行けると思います。

 

 

試そうにもクラウドサービスだからお金がかかるのでは…?」と心配されるかもしれませんが、Windows Azureは最初の一か月は17,000円分を無償で利用できます。このチュートリアルを通すくらいならきっと十分だと思います。 また開発環境も無償版のVisual Studio 2013 Express for Webが利用できます。

 

Azureに興味があるや、「とりあえずWebアプリを作ってデプロイしては見たけど、その次は…」というか方も、ぜひ、試してみてください。

*1:以前どこかのイベントか何かでScott Guthrieがデモっているのを見たことがあった気がします

年末年始にもおすすめ!最新のASP.NETのキャッチアップをしよう!

あっという間に2013年も残りわずかになった今日この頃ですが、みなさまはどうお過ごしでしょうか。 私はなんとか無事に仕事納めを終えることができて、ほっとしているところです。

最近のASP.NETの更新ペースは結構すごい

 ASP.NETマイクロソフトの開発系テクノロジの中でも、特にここ数年はとても進化のペースがが早いテクノロジといっていいでしょう。2009年の登場以降ものすごいスピードでバージョンアップを繰り返すASP.NET MVCに加え、『One ASP.NET』というビジョンの元、Web Pages、Web API、SignalR、SPAとフレームワークの種類もどんどん増えてきました。

f:id:kiyokura:20131227230417p:plain

 また今年2013年も、Visual Studio 2013と.NET Framework 4.5.1のリリースとともにMVC 5 / Web API 2 / Web Pages 3がリリース。そして聞こえ始めたOWINの足音。まったくもって話題に事欠かない一年でした。さらに2014年の早い段階でMVC 5.1 / Web API 2.1 / Web Pages 3.1のリリースが予定されています*1

f:id:kiyokura:20131227231033p:plain

 

 一方で『全然キャッチアップできない!!』という声も聞こえてきます。早いスパンで新規リリースやバージョンアップが行われる状況なので、書籍(とくに日本語書籍)もなかなかそろいません。そんな中、目の前の業務に追われ、なかなか最新の状況がキャッチアップできてないという方は決して少なくないと思います。

 

Microsoft Virtual Academyの学習コンテンツがおすすめ

 そんな方にお勧めなのが、Microsoft Virtual Academyです(以下、MVA)。

f:id:kiyokura:20131227231327p:plain

 MVAとは、オンラインでMicrosoftのテクノロジーが学習できるコンテンツサイトで、ビデオストリーミングやダウンロード資料などを用いて自分のペースで学習することができるものになっています。マイクロソフト・アカウントでログインすることにより、自分用にコンテンツをチョイスしたコースを作成したり進捗具合をポイントで管理したりする仕組みもあります。

f:id:kiyokura:20131227232133p:plain

 そしてもちろん、ASP.NETのコンテンツもあります!

 

おすすめのASP.NET学習コンテンツ

以下に、2013.12現在MVAにあるASP.NETに関係するコンテンツをピックアップしてみました。

まずは今のASP.NETの全体像を知る

ASP.NETの最新状況の全体像をキャッチアップしたい開発者におすすめしたいのが、この二つのコンテンツです。

f:id:kiyokura:20131227235903p:plain

 どちらも今年10月に開催された、開発者向けイベント.NET Weekで行われたセッションを元に構成されています。ASP.NETだけでなく、現在の.NETのテクノロジを用いて開発する際の重要な情報がちりばめられています。

 

 前者『アプリケーション パターンと開発技術の選択 ~ .NET テクノロジ ガイド詳解』は「.NET テクノロジ ガイド」で解説されている内容をベースに.NETでの開発テクノロジの選択をどう行うかという大きな枠組みでの一連のセッションで、『Web アプリケーション パターンと .NET』というセクションでVS2013をベースにASP.NET最新状況の解説があります。要チェック!

 

 後者『最新 .NET 技術によるアプリケーション開発入門』は Visual Studio 2013でのアプリケーション開発の概要を解説するという切り口のセッションで、ASP.NETに特化した内容としては「One ASP.NETVisual Studio 2013 による Web アプリケーション開発入門」のセクションがあります。そのほか関連するものとして「クラウド アプリケーション開発入門」もオススメ。

 

 

米Microsoft本社の技術者による、ASP.NETの徹底解説

次におすすめなのは、こちらのコンテンツです。

f:id:kiyokura:20131228000851p:plain

Scott Hanselman , Jon Galloway、Damian Edwardsというキーマンによる直接のセッションビデオとなっていて、デモを中に夫々のテクノロジーについて解説してくれます。9つのトピックに分けて丁寧に解説してくれます。英語ですが、字幕も完備されているので英語が苦手な方も心配無用です。VS2012を使っての解説ですが、一見の価値は必ずあります!

セクションの構成は以下のようになっています。

  • ASP.NET 4.5 の新機能
  • ASP.NET MVC 4 を使用した Web サイトの構築と配置
  • jQuery を使用した HTML5 アプリケーションの作成
  • ASP.NET Web API を使用したサービス層の構築
  • ASP.NET 開発スキルを活用した Office 用アプリの構築
  • ASP.NET でのソーシャル サービスの構築と活用
  • モバイル Web 向けの構築
  • SignalR を使用したリアルタイム通信
  • Windows Azure サービスの利用

 

そのほか、ASP.NETが関係するコンテンツ

そのほかにも、ASP.NETが関連するMVAのコンテンツをピックアップしてみました。どれも未見の方はこの機会にどうぞ。

 

この機会に日頃できないキャッチアップを!

ということで、特に年末年始のこの機会、日頃できないキャッチアップにMicrosoft Virtual Academyを利用されてみてはいかがでしょうか?

*1:Visual Studio 2013 Update 1に含まれてる予定。2013.12.06にRCが公開されています:http://www.forest.impress.co.jp/docs/news/20131210_627000.html