ASP.NET 5の変更
ASP.NET 5のコンテキストでは、スタックのすべてのコンポーネントが更新されているか、完全に書き換えられています。 変更はASP.NET Identityを渡しませんでした。これは朗報です。 特に、認可システムが更新されました。これらは本当に質的な変更(IMHO)です。 次に、認証および承認システムにどのような変更が行われたかを正確に検討します。
ASP.NET ID
サブジェクトのASP.NETアイデンティティに関するHabrのレビュー記事が既にあったため、紹介部分はスキップします。
ASP.NET Identityは、ユーザーとその認証を操作するためのツールを提供します。 このシステムは、 Microsoft.AspNet.Identityライブラリと、メインクラスの説明とユーザーストアインターフェイスなどの形式の抽象化で構成されています。 Microsoft.AspNet.Identity.EntityFramework -Entity Framework 7に基づいたユーザー/ロールおよびストレージ実装を含むライブラリ。
最初に注意したいのは、ユーザーとロールのインターフェースが不足していることです。 アイデンティティは、ユーザーがどうあるべきかを決定するものではありません。 セットアップ時に、使用するユーザークラスとロールを指定するだけで十分です。 これは、UserManagerクラスがユーザープロパティの読み取り/書き込みをストアに委任するという事実により実現されます。 たとえば、
GetUserName
、
SetUserName
ChangePassword
UserManager
の
UserManager
クラスの操作
ChangePassword
UserStoreで同じタイプのメソッドの呼び出しにつながります。
ユーザーとロールのリポジトリは、考えられるすべての操作を記述する必要はありません。 あるアプリケーションでは、ユーザーは名前、パスワード、およびそれらを使用してログインする能力さえあればよく、別のアプリケーションでは、さまざまな認証オプションがあり、他の多くの機能をサポートする必要があります。 このアプリケーションでは、適切なストレージインターフェースを選択して実装するだけで十分です。
ユーザーリポジトリのインターフェイスのリスト:
-
IUserLoginStore
IUserRoleStore
IUserClaimStore
IUserPasswordStore
IUserSecurityStampStore
IUserEmailStore
IUserLockoutStore
IUserPhoneNumberStore
IQueryableUserStore
IUserTwoFactorStore
単純な場合、IUserStore
だけで十分ですIUserStore
IUserStore
およびIUserPasswordStore
。
- IUserStore-名前とユーザーIDの管理。
- IUserPasswordStore-パスワードの設定と読み取り。
ASP.NET 5は、 アプリケーションパイプラインとそのサービス(組み込みのDIコンテナー )を構成するためのインフラストラクチャを提供します。
サービス接続:
public void ConfigureServices(IServiceCollection services) { // more here // Identity services.AddIdentity<ApplicationUser, IdentityRole>() // UserStore/RoleStore EF .AddEntityFrameworkStores<ApplicationDbContext>() // .AddDefaultTokenProviders(); // more here }
ミドルウェア認証のアクティブ化:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { // more here app.UseIdentity(); // Cookie Auth Middleware // more here }
これがデフォルト設定です。
モジュール性とASP.NET自体のDIの使用により、Identityは完全に書き換えることなくコンポーネントを置き換えることができます。 たとえば、パスワードハッシュ、ユーザーバリデーター(一意性の確認など)を変更し、ユーザーリポジトリとロールリポジトリを接続し、ロールを格納するためのクレームタイプを指定します。
services.AddScoped<IPasswordHasher<User>, MyPasswordHasher>(); services.AddIdentity<ApplicationUser, IdentityRole>(options => { options.Password.RequireDigit = true; options.Password.RequiredLength = 8; options.SignIn.RequireConfirmedEmail = true; options.ClaimsIdentity.RoleClaimType = "MyRoleClaimType"; }) .AddUserStore<MyStore>() .AddRoleStore<MyRoleStore>() .AddUserValidator<MyUserValidator>() .AddDefaultTokenProviders();
多くのIdentityコンポーネントには、カスタマイズ用の個別の拡張メソッドがありますが、すべてではありません。IPasswordHasher
と同様に、拡張メソッドはありません。 この場合、登録のために、Identityがこれを行う前にパスワードハッシュサービスを登録する必要があります。
Action, .
, . UserManager , , , , . ,IdentityOptions
.
Identity: Identity 2 ,NameNormilized
EmailNormalized
.UserManager
,IUserStore
. ,UPPER()
. , , .
, Identity . / ASP.NET 5. , , , . : ,MembershipProvider
(.NET 2.0): - ,UserManager
NotSupportedException .
IDはAction, .
, . UserManager , , , , . ,IdentityOptions
.
Identity: Identity 2 ,NameNormilized
EmailNormalized
.UserManager
,IUserStore
. ,UPPER()
. , , .
, Identity . / ASP.NET 5. , , , . : ,MembershipProvider
(.NET 2.0): - ,UserManager
NotSupportedException .
構成Action, .
, . UserManager , , , , . ,IdentityOptions
.
Identity: Identity 2 ,NameNormilized
EmailNormalized
.UserManager
,IUserStore
. ,UPPER()
. , , .
, Identity . / ASP.NET 5. , , , . : ,MembershipProvider
(.NET 2.0): - ,UserManager
NotSupportedException .
Action, .
, . UserManager
, , , , . ,IdentityOptions
.
Identity: Identity 2 ,NameNormilized
EmailNormalized
.UserManager
,IUserStore
. ,UPPER()
. , , .
, Identity . / ASP.NET 5. , , , . : ,MembershipProvider
(.NET 2.0): - ,UserManager
NotSupportedException .
次に、認証に移りましょう。
上記の例では、Configure
メソッドで、app.UseIdentity()
拡張メソッドを呼び出します。 このメソッドの内部では、ミドルウェアが接続され、認証Cookieパイプラインがセットアップされます。
var options = app.ApplicationServices.GetRequiredService<IOptions<IdentityOptions>>().Value; app.UseCookieAuthentication(options.Cookies.ExternalCookie); app.UseCookieAuthentication(options.Cookies.TwoFactorRememberMeCookie); app.UseCookieAuthentication(options.Cookies.TwoFactorUserIdCookie); app.UseCookieAuthentication(options.Cookies.ApplicationCookie);
次に、UseCookieAuthentication
はミドルウェアを接続します。
app.UseMiddleware<CookieAuthenticationMiddleware>(options);
ミドルウェア認証と対話するには、HttpContext
でハングするAuthenticationManager
クラスを使用する必要があります。 Identityを使用してユーザーを管理したくないが、自分でそれを行うことを好むと仮定します。
// SignOut var authenticationDescriptions = HttpContext.Authentication.GetAuthenticationSchemes(); foreach (var authenticationDescription in authenticationDescriptions) { HttpContext.Authentication.SignOutAsync(authenticationDescription.AuthenticationScheme); } // ASP.NET ClaimsIdentity // ClaimsIdentity var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, "123"), new Claim(ClaimsIdentity.DefaultNameClaimType, "MyUserName"), new Claim(ClaimTypes.Role, "Administrators"), new Claim("Lastname", "MyLastName"), new Claim("email", "bob@smith.com") }; var id = new ClaimsIdentity(claims, "MyCookiesScheme", ClaimsIdentity.DefaultNameClaimType, ClaimTypes.Role); var principal = new ClaimsPrincipal(id); // ClaimsPrincipal HttpContext.Authentication.SignInAsync("MyCookiesScheme", new ClaimsPrincipal(id));
ASP.NET IdentityのSignInManager
は、ほぼ同じ方法で機能します。 認証中に、ユーザーにログインする認証スキームを指定する必要があります。AuthenticationManager
は、このパラメーターに基づいて、対話するミドルウェアを決定します。
操作スキームは、MVC 5およびOWINとほぼ同じです。
ログイン
アイデンティティと認証の一般的な使用原則がほぼ同じままである場合、認可ははるかに重要な変更を受けています。 そして、より良いために!
多数のロールを持つアプリケーションでは、[Authorize]
属性の使用とuser.IsInRole
の形式のチェックの問題があります。 セキュリティ要件を変更する場合、アプリケーションに変更を加えることは非常に時間がかかり、エラーが発生しやすいプロセスです。 この役割またはその役割が使用されているすべての場所を見つける必要があるか、特定のアプリケーション機能へのアピールが発生するすべての場所を見つけて変更する必要があります。 つまり 認証コードはアプリケーションによってぼやけています。
始めましょう。 ユーザーメンバーシップのチェックに加えて、ロールには新しい承認ツールがあります。 デフォルトでは、[Authorize]
属性はロールのリストではなく「ポリシー」を受け入れます。
([Authorize(Roles=””)]
属性は後方互換性のためにサポートされています)
services.AddAuthorization(options => { options.AddPolicy("SuperAdministrationPolicy", policy => { policy.RequireRole("Admins"); policy.RequireClaim("adminlevel", "Level1", "Level2"); }); options.AddPolicy("RegularAdministrationPolicy", policy => { policy.RequireRole("Admins"); }); });
AuthorizationPolicy
は、Requirements
セットが含まれています。 デフォルトでは、ロール、ブランド、ユーザー名をチェックするためのRequirements
があります。Requirement
を別のクラスとして記述することもできます。
public class AdminLevelRequirement : AuthorizationHandler<AdminLevelRequirement>, IAuthorizationRequirement { private readonly string _adminLevel; public AdminLevelRequirement(string adminLevel) { _adminLevel = adminLevel; } protected override void Handle(AuthorizationContext context, AdminLevelRequirement requirement) { var user = context.User; if (user.IsInRole("Admins") && user.HasClaim("adminlevel", _adminLevel)) { context.Succeed(requirement); } } } // requirement options.AddPolicy("AdminLevel1Policy", policy => { policy.AddRequirements(new AdminLevelRequirement("Level1")); });
この例では、AuthorizationHandler<>
およびIAuthorizationRequirement
を継承していますが、これは必須ではありません。 ここで、AdminLevelRequirement
はリクエストとリクエストハンドラの両方を記述しています。 単純な場合、これは完全に適切なソリューションです。 しかし、より柔軟性が必要な場合は、プロセッサから要件を分離できます。 要件のクラスはIAuthorizationRequirement
(トークンインターフェイス)から継承する必要があり、要件パラメーターの説明のみを保持します。 たとえば、管理者レベル、そのアクセスレベル。 同様に、要件を処理するクラスはAuthorizationHandler<>
を継承し、Handle()
メソッドのみを含みます。
例:
// public class AdminLevelRequirement : IAuthorizationRequirement { public string AdminLevel { get; } public AdminLevelRequirement(string adminLevel) { AdminLevel = adminLevel; } } // public class AdminLevelRequirementHandler : AuthorizationHandler<AdminLevelRequirement> { protected override void Handle(AuthorizationContext context, AdminLevelRequirement requirement) { var user = context.User; if (user.IsInRole("Admins") && user.HasClaim("adminlevel", requirement.AdminLevel)) { context.Succeed(requirement); } } } // policy.AddRequirements(new AdminLevelRequirement("Level1")); // services.AddInstance<IAuthorizationHandler>(new AdminLevelRequirementHandler());
ハンドラーはAuthorizationContext.Suceed()
またはAuthorizationContext.Fail()
返すことができます。 1つの要件に対して複数のハンドラーを記述した場合、ORまたはANDの原則に従ってそれらを実行できます。 ORスタイルの実行の場合、ハンドラーはSucceed()
のみを返すことができます。 ANDの処理ロジックが必要な場合は、戻り値Fail()
使用できます。 つまり ハンドラーでFail()
呼び出されない場合、いずれかのハンドラーが正常に検証されると、ユーザーにアクセスが許可されます。
使用に渡します。
属性を使用して宣言的な方法で許可ポリシーを使用します。
[Authorize("PoliyName")] public IActionResult ActionName()
認可サービスがツールキットに追加されました。これは、クラスで依存関係を宣言することで取得できます。
// public class HomeController : Controller { private readonly IAuthorizationService _authorizationService; public HomeController(IAuthorizationService authorizationService) { _authorizationService = authorizationService; } }
サービスを使用して、ユーザーの承認ポリシーへの準拠を確認します。
if (await _authorizationService.AuthorizeAsync(User, "MyPolicy"))
セキュリティポリシーに加えて、リソースベースの承認を利用できます。 認証は、アクセスが行われるオブジェクトに依存する場合に使用されます。 このタイプの認可を使用するには、特定の要件/ポリシーおよびリソースを処理するハンドラーを実装する必要があります。
public class TenantAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Tenant> { protected override void Handle(AuthorizationContext context, OperationAuthorizationRequirement requirement, Tenant resource) { if (requirement.Name == "Update") { if (!context.User.HasClaim("adminlevel", "Level1")) { context.Fail(); return; } if (!context.User.HasClaim("region", resource.Region)) { context.Fail(); return; } context.Succeed(requirement); return; } context.Fail(); } }
このようなハンドラーは、アクセスが行われるリソースのインスタンスも受け入れるという点でのみ通常のハンドラーと異なります。
OperationAuthorizationRequirement
は、Nameプロパティのみを持つデフォルトの要件クラスです。 このクラスは、単純なスクリプトに使用でき、Read
、Update
、Delete
などの名前付き操作へのユーザーアクセスをチェックします。 もちろん、この場合、要件の独自のクラスを記述することができます。
この新しいアプローチにより、認可の集中管理が可能になり、セキュリティ要件を変更する際にメインアプリケーションのコードを変更する必要性が最小限に抑えられます。 アクセスルールを含むすべてのコードは、個別のアセンブリおよびカプセル化された承認ロジックに取り出すことができます。
便利なリンク: