転んで、また起き上がる:ASP.NET 5 Identity


「転んだ」とは、愛と憎しみの選択:ASP.NET 5+EntityFramework 7
ASPを知りたいならNET Identityの「歴史」と「原理」を強くお勧めします.MVC 5-ASP.NET Identityログイン原理-Claims-based認証とOWIN、時間があればJesse LiuのMembership三部作を読むこともできます.
  • Membership三段階曲の入門編-Membership基礎例
  • Membership三段階曲の進級編-Provider Model
  • を深く分析する
  • Membership三歩曲のプレミアム編-MembershipからASP.NET Identity

  • 実は耻ずかしくて、私自身はASPに対してNET Identityの理解と運用は、AuthorizeAttribute、FormsAuthenticationのみを使用する.SetAuthCookieなどいくつかの操作、背後の原理とその発展過程はあまり理解していないので、私は当時ASPにいました.NET 5では認証操作を行ってこそ、「無力」な感じがします.週末にJesse Liuのブログを読んで、関連資料を探して、自分では少し知っているようですが、完全に理解していないようです.言えない以上、「ペン」でメモしてください.
    ASP.NET Identity GitHubアドレス:https://github.com/aspnet/Identity
    ASP.NET 5では、認証に関する変更は大きくなく、MVC 5のセットであり、構成の変更にすぎない.VS 2015を使用してMVCプロジェクトを作成する場合は、「Change Authentication」をクリックすると、次の4つの選択肢が表示されます.
    ASPを作成する場合NET 5プロジェクト、Authenticationのデフォルトは変更できません:
    VS 2015を用いるMVC 5およびASPをそれぞれ作成する.NET 5のサンプルプロジェクトでは、MVC 5の認証に関するコードや構成が非常に複雑であることがわかりますが、ASP.NET 5では相対的に簡略化する、まずStartup.csファイルのConfigureServicesメソッドには、次のような構成があります.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add EF services to the services container.
        services.AddEntityFramework(Configuration)
            .AddSqlServer()
            .AddDbContext();
    
        // Add Identity services to the services container.
        services.AddDefaultIdentity(Configuration);
        services.AddIdentityEntityFramework(Configuration);
        services.AddIdentity(Configuration);
    
        // Add MVC services to the services container.
        services.AddMvc();
    }

    上記のコードでは、AddDefaultIdentityとAddIdentityEntityFrameworkは、プログラムセット:Microsoft.AspNet.Identity.Entity Framework、AddEntity Framework、およびAddIdentityEntity Frameworkは、同じDbContextを使用しています.もちろん、複数のアプリケーションが1つの認証コンテキストを共有する可能性があるなど、認証コンテキストを別々に管理することもできます.ConfigureServicesメソッドの解釈は、This method gets called by the runtimeは、このメソッドがアプリケーションの実行時に登録して使用するサービスを表し、ASP.のようなコンポーネント化されたアプリケーションに似ている.NET 5は基礎的なWebサイトにすぎません.このアプリケーションに必要なコンポーネントやモジュールを追加することができます.例えば、WebAPIを使用したい場合は、projectだけです.jsonにMicrosoftを追加します.AspNet.Mvc.WebApiCompatShimパッケージ、そしてConfigureServicesメソッドでサービス登録すればいいです:services.AddWebApiConventions();.
    AddDefaultIdentity登録の3つの基礎タイプ:
  • IdentityDbContext:ApplicationDbContext継承実装.
  • IdentityUser:ApplicationUser継承実装.
  • IdentityRole

  • 登録が完了すると、Startupで構成が使用されます.csのConfigureメソッドでの構成使用:app.UseIdentity();,アプリケーションが認証を有効にしていることを示します.このコードを注釈すると、アプリケーション全体の認証が失効していることがわかります.Configureメソッドの解釈は、Configure is called after ConfigureServices is calledは、上のAddDefaultIdentity登録には多くの内容が含まれています.認証については基本的にこの3つのタイプ、ASP.NET Identityの直接的な操作は、登録されている3つのタイプによって注入されます.例えば、後で遭遇するUserManagerやSignInManagerなどですが、この部分のソースコードはMicrosoftで表示されます.AspNet.Identity.EntityFrameworkには入っていません.
    次にASPに基づいてNET Identityのソースコード、認証の流れを見て、ASP.NET 5の認証は、以前と同様に、認証が必要なアクションにAuthorizeを追加するだけでよい、上にStartup.csの認証構成は簡単で、有効にするとappしか必要ありません.UseIdentity(); 前のMVCプログラムのWeb.configには多くのものを構成する必要があります.I d e n t i t y S v e r i c e C o r e C o n t i onExtensionsソースコードには、アプリケーションCookieAuthenticationType登録などのデフォルト構成が含まれています.
    services.Configure(options =>
    {
        options.AuthenticationType = IdentityOptions.ApplicationCookieAuthenticationType;
        options.LoginPath = new PathString("/Account/Login");
        options.Notifications = new CookieAuthenticationNotifications
        {
            OnValidateIdentity = SecurityStampValidator.ValidateIdentityAsync
        };
    }, IdentityOptions.ApplicationCookieAuthenticationType);

    コンフィギュレーションでカスタム構成を行うこともできます.構成方法:app.UseCookieAuthentication.Actionにアクセスする認証が無効になった後、「/ACcount/LOgin」にジャンプしてログインし、AccountControllerのサンプルコードを表示すると、次のことがわかります.
    public class AccountController : Controller
    {
        public AccountController(UserManager userManager, SignInManager signInManager)
        {
            UserManager = userManager;
            SignInManager = signInManager;
        }
    
        public UserManager UserManager { get; private set; }
        public SignInManager SignInManager { get; private set; }
    }

    アプリケーション全体のコードを見ると、UserManager、SignInManagerタイプの依存注入は登録されていませんが、どのように注入されていますか?実際に注入されるタイプはUserManagerとSignInManagerではなく、IdentityUserです.コンフィギュレーションサービスにこのようなコードを追加しました.services.AddDefaultIdentity(Configuration);です.これが最も重要です.その後、すべての認証操作で使用されるベースタイプはここから来ています.IdentityServiceCollectionExtensionsのAddIdentity操作では、次のようなコードが見つかりました.
    services.TryAdd(describe.Scoped, UserManager>());
    services.TryAdd(describe.Scoped, SignInManager>());
    services.TryAdd(describe.Scoped, RoleManager>());

    Scopedが存在するプログラムセット:Microsoft.Framework.DependencyInjection,DependencyInjectionはASP.NET 5の依存注入は、関連するタイプのソースコードをよく見ると、IoC管理が行われていることがわかります.次に、Login操作を見てみましょう.
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task Login(LoginViewModel model, string returnUrl = null)
    {
        if (ModelState.IsValid)
        {
            var signInStatus = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
            switch (signInStatus)
            {
                case SignInStatus.Success:
                    return RedirectToLocal(returnUrl);
                case SignInStatus.Failure:
                default:
                    ModelState.AddModelError("", "Invalid username or password.");
                    return View(model);
            }
        }
    
        // If we got this far, something failed, redisplay form
        return View(model);
    }

    最も主要な操作は、ASP.NET IdentityのSignInManager.PasswordSignInAsync操作を行い、認証パスワードを行い、SignInStatusタイプの認証結果を返します.
    public enum SignInStatus
    {
        Success = 0,
        LockedOut = 1,
        RequiresVerification = 2,
        Failure = 3
    }

    SignInManagerを見てみましょうPasswordSignInAsyncで何をしたのか:
    public virtual async Task PasswordSignInAsync(TUser user, string password, 
        bool isPersistent, bool shouldLockout, CancellationToken cancellationToken = default(CancellationToken))
    {
        if (user == null)
        {
            throw new ArgumentNullException(nameof(user));
        }
        var error = await PreSignInCheck(user, cancellationToken);
        if (error != null)
        {
            return error;
        }
        if (await IsLockedOut(user, cancellationToken))
        {
            return SignInResult.LockedOut;
        }
        if (await UserManager.CheckPasswordAsync(user, password, cancellationToken))
        {
            await ResetLockout(user, cancellationToken);
            return await SignInOrTwoFactorAsync(user, isPersistent, cancellationToken);
        }
        if (UserManager.SupportsUserLockout && shouldLockout)
        {
            // If lockout is requested, increment access failed count which might lock out the user
            await UserManager.AccessFailedAsync(user, cancellationToken);
            if (await UserManager.IsLockedOutAsync(user, cancellationToken))
            {
                return SignInResult.LockedOut;
            }
        }
        return SignInResult.Failed;
    }

    PasswordSignInAsyncには、ユーザー情報を取得するための書き換え方法があります.UserManager.FindByNameAsync(userName, cancellationToken);、FindByName Asyncの定義を確認すると、このコードが見つかります.Store.FindByNameAsync(userName, cancellationToken)、Storeは何ですか.タイプは、IUserStore Storeと定義されています.これは倉庫のようなもので、ユーザー検証のためにクエリーおよびストレージサービスを提供しています.IUserStoreのほかに、UserManagerでは、IUserLoginStore、IUserRoleStore、IUserClaimStoreなど、多くの「Store」が見つかりますが、いずれもIUserStoreに継承され、コンフィギュレーションサービスを行うときにservices.AddIdentityには、TContextというタイプのAddEntityFrameworkStoresメソッドがあります.上のすべてのStoreコンテキストが継承されています.AddEntityFrameworkStoresの実装を確認します.
    public static IdentityBuilder AddEntityFrameworkStores(this IdentityBuilder builder)
        where TContext : DbContext
    {
        builder.Services.Add(IdentityEntityFrameworkServices.GetDefaultServices(builder.UserType, builder.RoleType, typeof(TContext)));
        return builder;
    }

    builder.Services.Addが果たす役割はIoCにタイプを注入することであり、このタイプの参照はすべて構造関数注入方式でその実現を得ることができ、GetDefaultServicesの具体的な実現を確認することができる.コードが分からないので貼らないが、実際には3つのタイプが操作されている.Tuser、TRole、TContextであり、これもASPである.NET Identityオペレーションの3つの基本タイプは、Identityオペレーションでは、基本的に2つの大きなオペレーションクラスで、1つはSignInManagerで、もう1つはUserManagerです.実際にSignInManagerの具体的な実装コードを見てみると、ユーザーの取得とストレージについては、UserManagerを通じて操作されているのに対し、UserManagerはIUserStoreの具体的な実装クラスを通じて操作されていることがわかります.SignInManagerは、最初に述べたSignInManagerのようなユーザー認証の操作クラスにすぎません.PasswordSignInAsync、コードが貼られていますが、基本的にはUserManagerです.UserManagerとかCheckPasswordAsync、UserManager.SupportsUserLockout、UserManager.AccessFailedAsyncなど、PasswordSignInAsyncコード実装では、ユーザーの操作については触れません.最も核心的なのは、このコード:SignInOrTwoFactorAsync(user, isPersistent, cancellationToken);です.具体的な実装を確認します.
    private async Task SignInOrTwoFactorAsync(TUser user, bool isPersistent,
        CancellationToken cancellationToken, string loginProvider = null)
    {
        if (UserManager.SupportsUserTwoFactor && 
            await UserManager.GetTwoFactorEnabledAsync(user, cancellationToken) &&
            (await UserManager.GetValidTwoFactorProvidersAsync(user, cancellationToken)).Count > 0)
        {
            if (!await IsTwoFactorClientRememberedAsync(user, cancellationToken))
            {
                // Store the userId for use after two factor check
                var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
                Context.Response.SignIn(StoreTwoFactorInfo(userId, loginProvider));
                return SignInResult.TwoFactorRequired;
            }
        }
        // Cleanup external cookie
        if (loginProvider != null)
        {
            Context.Response.SignOut(IdentityOptions.ExternalCookieAuthenticationType);
        }
        await SignInAsync(user, isPersistent, loginProvider, cancellationToken);
        return SignInResult.Success;
    }

    再び多くのUserManager操作を捨てて、最も核心的なものを見つけます:Context.Response.SignIn(StoreTwoFactorInfo(userId, loginProvider))、StoreTwoFactorInfoメソッドの戻りタイプはClaimsIdentityで、戻る前にuserIdに基づいてClaimオブジェクトを作成し、ClaimsIdentityセットに追加します.次の操作は:Context.Response.SignInで、ユーザーのアイデンティティ情報を現在のコンテキストに入力し、次にHttpResponse抽象クラスのSignInに関する定義を表示します.
    public virtual void SignIn(IEnumerable identities);
    public virtual void SignIn(ClaimsIdentity identity);
    public abstract void SignIn(AuthenticationProperties properties, IEnumerable identities);
    public virtual void SignIn(AuthenticationProperties properties, params ClaimsIdentity[] identities);
    public virtual void SignIn(AuthenticationProperties properties, ClaimsIdentity identity);

    以前はSignInを使用する場合、その呼び出し方式はSystem.Web.Security.FormsAuthentication.SetAuthCookie("userName", false);であり、Forms認証を採用していたが、ASP.NET 5では、すでにSetAuthCookieにアクセスできなくなりましたが、元のSetAuthCookieの実現方式はどうなっているのか、ASP.NET 5でSetAuthCookieのような効果を実現するには、どうすればいいのでしょうか.StartupでcsのConfigureメソッドでは、次の構成を行います.
    //app.UseIdentity();
    app.UseCookieAuthentication((cookieOptions) =>
    {
        cookieOptions.AuthenticationType = IdentityOptions.ApplicationCookieAuthenticationType;
        cookieOptions.AuthenticationMode = AuthenticationMode.Active;
        cookieOptions.CookieHttpOnly = true;
        cookieOptions.CookieName = ".CookieName";
        cookieOptions.LoginPath = new PathString("/Account/Login");
        //cookieOptions.CookieDomain = ".mysite.com";
    }, "AccountAuthorize");

    実際には、以下でカスタマイズする構成は、上記の注記のUseIdentityと同じ効果ですが、Microsoftでの操作もあります.AspNet.Identity.IdentityServiceCollectionExtensionsでデフォルトで完了しています.上記の構成では、前のservices.AddDefaultIdentity(Configuration);コードをコメントしています.AccountのLoginコードを見てみましょう.
    [AllowAnonymous]
    public void Login(string returnUrl = null)
    {
        var userId = "xishuai";
        var identity = new ClaimsIdentity(IdentityOptions.ApplicationCookieAuthenticationType);
        identity.AddClaim(new Claim(ClaimTypes.Name, userId));
        Response.SignIn(identity);
    }

    上の操作は実は前のSignInManager.PasswordSignInAsyncと同じで、簡略化されたバージョンにすぎません.また、IdentityOptions.ApplicationCookieAuthenticationTypeも不思議なところはありません.タイプ文字列です.
    public static string ApplicationCookieAuthenticationType { get; set; } = typeof(IdentityOptions).Namespace + ".Application";
    public static string ExternalCookieAuthenticationType { get; set; } = typeof(IdentityOptions).Namespace + ".External";
    public static string TwoFactorUserIdCookieAuthenticationType { get; set; } = typeof(IdentityOptions).Namespace + ".TwoFactorUserId";
    public static string TwoFactorRememberMeCookieAuthenticationType { get; set; } = typeof(IdentityOptions).Namespace + ".TwoFactorRemeberMe";

    Loginログイン検証効果:
    まとめ:上にも多くの内容が述べられていますが、本当のことを言うと、実は私も自分が何を言ったのか分かりません.いくつかの感想をまとめる必要があります.複数のアプリケーションが認証を共有しているとき(CookieDomain)、FormsAuthenticationを使っても、SignInManagerを使っても.SignInAsync、またはUserStoreを使用してユーザー管理を行うが、ユーザーが検証を行うプログラムは1つしかない.これは自分の考えで、どのように実現したいのか、どのように実現したいのか、他のアプリケーションはユーザーが検証要求を通過したか、ユーザーIDを取得したかを判断するにすぎない.この2つの操作について、ユーザーの検証は上記のような実現にかかわらず、私たちはUserを通過することができる.Identityユーザー認証の情報を取得します.タイプはIidentityです.
    知らない者は无罪、知っているのに贖罪しない、それは有罪だ!!!