ASP.NET Core 2.2:二十七.JWTとユーザ認証(Actionにドリルダウン)
17716 ワード
ASP.NET Core 2.2:二十七.JWTとユーザ認証(Actionにドリルダウン)
前章ではASP.NET CoreではJWTを適用してユーザ認証およびTokenのリフレッシュを行い,本章では次のステップに進み,ユーザ認証を行う.触れた例も以上の章に基づいている.(ASP.NET Coreシリーズカタログ)
一、概説
まず、認証(authentication)と認証(authorization)についてお話しします.それらはよく一緒に働いているので、区別がつかないことがあります.そしてこの2つの英語の単語も兄弟に似ています.例えば、私は門禁カードを使って会社に入って、門禁【認証】は私がここの従業員で、入ることができます;しかし、会社に入ってから、私はすべての部屋に入ることができるわけではありません.例えば、「機械室は重くて、暇な人は入らないでください」など、私はどの部屋に入ることができて、会社の「授権」が必要です.これが認証とライセンスの違いです.
ASP.NET Coreが提唱しているのは声明(Claim)に基づくライセンスで、このClaimについては前章で使用したことがありますが、以下のようなコードがありますが、紹介されていません.Claim[] claims = new Claim[] { new Claim(ClaimTypes.NameIdentifier, user.Code), new Claim(ClaimTypes.Name, user.Name) };
これは、ユーザーの一意のIDとユーザー名を保存するための2つの宣言を含む宣言の集合です.もちろん、より多くのClaimを追加することもできます.Claimに対応し、ClaimsIdentityとClaimsPrincipalの2種類があります.
ClaimsIdentityは、前例のゲートカードなどの証明書に相当します.ClaimsPrincipalは証明書の所有者で、私自身です.では、対応するClaimは、証明書番号、所有者の名前など、ゲートカードに格納されている情報です.
私はゲートカードのほかに身分証明書、銀行カードなどがあります.つまり、1つのClaimsPrincipalには複数のClaimsIdentityがあり、1つのClaimsIdentityには複数のClaimがあります.ASP.NET Coreのライセンスモデルはたぶんこのようなシステムです.
ASP.NET Coreは、互換性のある前のロールライセンスなど、さまざまなライセンス方式をサポートしています.以下、いくつかの例で説明する(例は依然として上記の章のコードに基づいている).
二、役割に基づく授権
ASP.NET Coreは以前のロールライセンスモードと互換性がありますが、どのように使用しますか?本文の重点ではないので、ここでは簡単に述べるだけです.FlyLoloを変更します.JWT.ServerのTokenHelperは一時的に張三に「TestPutBookRole」という権限を追加した(実際の権限ソースはここでは示さない). public ComplexToken CreateToken(User user)
{
Claim[] claims = new Claim[] { new Claim(ClaimTypes.NameIdentifier, user.Code), new Claim(ClaimTypes.Name, user.Name) };
// code 001 Claim, Token , FlyLolo.JWT.API BookController Put ,
if (user.Code.Equals("001"))
{
claims = claims.Append(new Claim(ClaimTypes.Role, "TestPutBookRole")).ToArray();
}
return CreateToken(claims);
}
FlyLoloを変更します.JWT.APIのBookControllerは、次のアクションを追加しました. ///
/// JWT token , TokenHelper
///
///
[HttpPut]
[Authorize(Roles = "TestPutBookRole")]
public JsonResult Put()
{
return new JsonResult("Put Book ...");
}
まず、認証(authentication)と認証(authorization)についてお話しします.それらはよく一緒に働いているので、区別がつかないことがあります.そしてこの2つの英語の単語も兄弟に似ています.例えば、私は門禁カードを使って会社に入って、門禁【認証】は私がここの従業員で、入ることができます;しかし、会社に入ってから、私はすべての部屋に入ることができるわけではありません.例えば、「機械室は重くて、暇な人は入らないでください」など、私はどの部屋に入ることができて、会社の「授権」が必要です.これが認証とライセンスの違いです.
ASP.NET Coreが提唱しているのは声明(Claim)に基づくライセンスで、このClaimについては前章で使用したことがありますが、以下のようなコードがありますが、紹介されていません.
Claim[] claims = new Claim[] { new Claim(ClaimTypes.NameIdentifier, user.Code), new Claim(ClaimTypes.Name, user.Name) };
これは、ユーザーの一意のIDとユーザー名を保存するための2つの宣言を含む宣言の集合です.もちろん、より多くのClaimを追加することもできます.Claimに対応し、ClaimsIdentityとClaimsPrincipalの2種類があります.
ClaimsIdentityは、前例のゲートカードなどの証明書に相当します.ClaimsPrincipalは証明書の所有者で、私自身です.では、対応するClaimは、証明書番号、所有者の名前など、ゲートカードに格納されている情報です.
私はゲートカードのほかに身分証明書、銀行カードなどがあります.つまり、1つのClaimsPrincipalには複数のClaimsIdentityがあり、1つのClaimsIdentityには複数のClaimがあります.ASP.NET Coreのライセンスモデルはたぶんこのようなシステムです.
ASP.NET Coreは、互換性のある前のロールライセンスなど、さまざまなライセンス方式をサポートしています.以下、いくつかの例で説明する(例は依然として上記の章のコードに基づいている).
二、役割に基づく授権
ASP.NET Coreは以前のロールライセンスモードと互換性がありますが、どのように使用しますか?本文の重点ではないので、ここでは簡単に述べるだけです.FlyLoloを変更します.JWT.ServerのTokenHelperは一時的に張三に「TestPutBookRole」という権限を追加した(実際の権限ソースはここでは示さない). public ComplexToken CreateToken(User user)
{
Claim[] claims = new Claim[] { new Claim(ClaimTypes.NameIdentifier, user.Code), new Claim(ClaimTypes.Name, user.Name) };
// code 001 Claim, Token , FlyLolo.JWT.API BookController Put ,
if (user.Code.Equals("001"))
{
claims = claims.Append(new Claim(ClaimTypes.Role, "TestPutBookRole")).ToArray();
}
return CreateToken(claims);
}
FlyLoloを変更します.JWT.APIのBookControllerは、次のアクションを追加しました. ///
/// JWT token , TokenHelper
///
///
[HttpPut]
[Authorize(Roles = "TestPutBookRole")]
public JsonResult Put()
{
return new JsonResult("Put Book ...");
}
public ComplexToken CreateToken(User user)
{
Claim[] claims = new Claim[] { new Claim(ClaimTypes.NameIdentifier, user.Code), new Claim(ClaimTypes.Name, user.Name) };
// code 001 Claim, Token , FlyLolo.JWT.API BookController Put ,
if (user.Code.Equals("001"))
{
claims = claims.Append(new Claim(ClaimTypes.Role, "TestPutBookRole")).ToArray();
}
return CreateToken(claims);
}
///
/// JWT token , TokenHelper
///
///
[HttpPut]
[Authorize(Roles = "TestPutBookRole")]
public JsonResult Put()
{
return new JsonResult("Put Book ...");
}
访问这个Action,只有用张三登录后获取的Token能正常访问。
三、声明に基づく授権
对于上例来说,本质上也是基于声明(Claim)的授权,因为张三的"TestPutBookRole"角色也是作为一个Claim添加到证书中的。只不过采用了特定的ClaimTypes.Role。那么是否可以将其他的普通Claim作为授权的依据呢?当然是可以的。
这里涉及到了另一个单词“Policy”,翻译为策略?也就是说,可以把一系列的规则(例如要求姓名为李四,账号为002,国籍为中国等等)组合在一起,形成一个Policy,只有满足这个Policy的才可以被授权访问。
下面我们就新建一个Policy,在Startup的ConfigureServices中添加授权代码:
services.AddAuthorization(options=>options.AddPolicy("Name",policy=> {
policy.RequireClaim(ClaimTypes.Name, " ");
policy.RequireClaim(ClaimTypes.NameIdentifier,"001");
}));
BookControlに次のアクションを追加します.
[HttpDelete]
[Authorize(Policy = "TestPolicy")]
public JsonResult Delete()
{
return new JsonResult("Delete Book ...");
}
張三と李四のアカウントでテストすることができますが、張三のアカウントを使って取得したTokenだけがアクセスできます.
四、ポリシーに基づくカスタム授権
上記の2つのライセンス方式について説明しましたが、ロールライセンスを通じて、いくつかの小さなプロジェクトにのみ適しており、いくつかの機能をロールで区切ることができます.
宣言により、実際のプロジェクトではStartupで一連のPolicyを宣言し、ControllerまたはActionで使用する必要があります.
どちらも気分が悪い.例えば、1人のユーザが複数のロールを有し、各ロールが複数のアクセス可能なAPIアドレスに対応する(特定のActionに権限を細分化する)というニーズがしばしば存在する.ユーザはまた、特定のAPIアドレスに権限を付与することもできる.
このようなニーズは上記の2つの方法で実現するのは面倒で、ASP.NET Coreは便利な拡張方式を提供しています.
1.サンプルデータ
上記の要件を要約すると、最終的には次のような形式のデータが形成されます.///
/// ,
///
public static class TemporaryData
{
public readonly static List UserPermissions = new List {
new UserPermissions {
Code = "001",
Permissions = new List {
new Permission { Code = "A1", Name = "student.create", Url = "/api/student",Method="post" },
new Permission { Code = "A2", Name = "student.delete", Url = "/api/student",Method="delete"}
}
},
new UserPermissions {
Code = "002",
Permissions = new List {
new Permission { Code = "B1", Name = "book.create", Url = "/api/book" ,Method="post"},
new Permission { Code = "B2", Name = "book.delete", Url = "/api/book" ,Method="delete"}
}
},
};
public static UserPermissions GetUserPermission(string code)
{
return UserPermissions.FirstOrDefault(m => m.Code.Equals(code));
}
}
///
/// ,
///
public static class TemporaryData
{
public readonly static List UserPermissions = new List {
new UserPermissions {
Code = "001",
Permissions = new List {
new Permission { Code = "A1", Name = "student.create", Url = "/api/student",Method="post" },
new Permission { Code = "A2", Name = "student.delete", Url = "/api/student",Method="delete"}
}
},
new UserPermissions {
Code = "002",
Permissions = new List {
new Permission { Code = "B1", Name = "book.create", Url = "/api/book" ,Method="post"},
new Permission { Code = "B2", Name = "book.delete", Url = "/api/book" ,Method="delete"}
}
},
};
public static UserPermissions GetUserPermission(string code)
{
return UserPermissions.FirstOrDefault(m => m.Code.Equals(code));
}
}
涉及到的两个类如下:
public class Permission
{
public string Code { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Method { get; set; }
}
public class UserPermissions
{
public string Code { get; set; }
public List Permissions { get; set; }
}
2.カスタムハンドラ
次に、サンプルデータに基づいて対応する処理手順を作成します.これはIAuthorizationRequirementとAuthorizationHandlerの2つの内容に関連している.
IAuthorizationRequirementは、主にライセンスに必要な「要件」、または「ルール」を提供するための空のインタフェースです.AuthorizationHandlerは,要求と「要求」の連携処理である.
新しいPermissionRequirement実装IAuthorizationRequirementインタフェース.
public class PermissionRequirement: IAuthorizationRequirement
{
public List UsePermissionList { get { return TemporaryData.UserPermissions; } }
}
簡単な内容です.その「要求」はユーザーの権限リストであり、ユーザーの権限リストに現在アクセスしているAPIが含まれている場合、許可は通過します.そうしないと通過しません.
判断ロジックは新しいPermissionHandlerに置かれます.
public class PermissionHandler : AuthorizationHandler
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var code = context.User.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.NameIdentifier));
if (null != code)
{
UserPermissions userPermissions = requirement.UsePermissionList.FirstOrDefault(m => m.Code.Equals(code.Value.ToString()));
var Request = (context.Resource as AuthorizationFilterContext).HttpContext.Request;
if (null != userPermissions && userPermissions.Permissions.Any(m => m.Url.ToLower().Equals(Request.Path.Value.ToLower()) && m.Method.ToLower().Equals(Request.Method.ToLower()) ))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
論理は簡単で説明しない.
3.カスタムハンドラの使用
StartupのConfigureServicesに認証コードを追加する
services.AddAuthorization(options => options.AddPolicy("Permission", policy => policy.Requirements.Add(new PermissionRequirement())));
services.AddSingleton();
BookControllerのDelete Actionを変更します.
[HttpDelete]
//[Authorize(Policy = "TestPolicy")]
[Authorize(Policy = "Permission")]
public JsonResult Delete()
{
return new JsonResult("Delete Book ...");
}
李四だけがこのActionにアクセスできることをテストします.
コードアドレス:https://github.com/FlyLolo/JWT.Demo/releases/tag/2.0