1. 程式人生 > 實用技巧 >Authentication and authorization (three)

Authentication and authorization (three)

前言

在寫三上是在一的基礎上寫的,所以有沒有看過二是沒得關係的,在一中介紹了認證與授權,但是沒有去介紹拿到證書後怎樣去驗證授權。
概念性東西:在這套機制中,把這個許可權認證呢,稱作為policy。這個policy是怎麼樣的過程呢?
就像我前面說的,證書也認證了,policy做的是檢查你的證書中是否符合我心中的標準。
比如說,我有一個介面實行的是駕駛證檢查。結果你只有一張學生證,你不能那一張學生證當駕駛證去開車吧,肯定就不讓你訪問啊。
也許還有點迷糊,請看正文。正文皆為個人理解,如有錯誤,請指正。

正文

請看下面的:

services.AddAuthorization(config=> {
	var defaultAuthBuilder = new AuthorizationPolicyBuilder();
	var defaultPolicy = defaultAuthBuilder.RequireAuthenticatedUser().Build();
	config.DefaultPolicy = defaultPolicy;
});

這時候就是在配置授權了。
defaultAuthBuilder.RequireAuthenticatedUser().Build() 中,RequireAuthenticatedUser去要求使用者必須經過認證的,也就是不允許匿名使用者,這個很好理解。
設定預設的策略認證就是這個了。
這時候我訪問:
https://localhost:44350/Home/Secret
是正常的,因為我本來就是認證過的。
然後我改了一下:

services.AddAuthorization(config=> {
	var defaultAuthBuilder = new AuthorizationPolicyBuilder();
	var defaultPolicy = defaultAuthBuilder.RequireAuthenticatedUser().RequireClaim(ClaimTypes.Country).Build();
	config.DefaultPolicy = defaultPolicy;
});

和上面不同的,我加了RequireClaim(ClaimTypes.Country),我要求起碼有一個證書你要有國家。

我再次貼下證書的程式碼,在.net core 認證與授權(一)中也有。

var SchoolClaims = new List<Claim>()
{
	new Claim(ClaimTypes.Name,"Jack"),
	new Claim(ClaimTypes.Email,"[email protected]")
};

var LicensClaims = new List<Claim>()
{
	new Claim(ClaimTypes.Name,"Jack.li"),
	new Claim(ClaimTypes.Email,"[email protected]"),
	new Claim("begin","2000.10.1")
};
var SchoolIdentity = new ClaimsIdentity(SchoolClaims,"Student Identity");
var CarManagerIdentity = new ClaimsIdentity(LicensClaims, "Licens Identity");
var userPrincipal = new ClaimsPrincipal(new[] { SchoolIdentity, CarManagerIdentity });

HttpContext.SignInAsync("CookieAuth", userPrincipal);
return RedirectToAction("Index");

在兩個證書中,都沒得國家。
然後訪問:
https://localhost:44350/Home/Secret

自動跳轉到了一個授權沒有通過的網站,恰巧我沒有寫這個網頁,姑且著就是沒有通過的意思。
然後我再一個證書中添加了一個:

new Claim(ClaimTypes.Country,"china")

這樣我又成功了。
好的,這是預設的策略,那麼如何自定義呢?

config.AddPolicy("Claim.Country", policyBuilder =>
{
	policyBuilder.RequireClaim(ClaimTypes.Country);
});

上面我添加了一個策略,名字叫做Claim.Country.
因為不是預設,所以要[Authorize(Policy = "Claim.Country")]
policyBuilder 相當於上面的var defaultAuthBuilder = new AuthorizationPolicyBuilder();。
然後裡面去設定起碼有一個證書中要有國家。
policyBuilder 預設有一些限制可以使用,那麼如何自己來定義呢?究竟是一種什麼模式呢?

public class CustomRequireClaim:IAuthorizationRequirement
{
	public CustomRequireClaim(string ClainType)
	{
		this.ClainType = ClainType;
	}

	public string ClainType{ get; }
}

public class CustomRequireClaimHandler : AuthorizationHandler<CustomRequireClaim>
{
	protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequireClaim requirement)
	{
		var hasClaim = context.User.Claims.Any(u=>u.Type== requirement.ClainType);
		if (hasClaim)
		{
			context.Succeed(requirement);

		}
		return Task.CompletedTask;
	}
}

CustomRequireClaim 是什麼呢?繼承IAuthorizationRequirement。其實是模擬生活中的什麼呢,比如說你要向批發商進一匹東西,有一些需求,比如說蘋果要通紅的,你就可以在這裡填寫。
CustomRequireClaimHandler 繼承自AuthorizationHandler,批發商帶一批貨(程式也就是使用者帶著證書來了),你可以這個檢查合格不合格,拿著你的需求表對照。
那麼註冊進入:

config.AddPolicy("Claim.Country", policyBuilder =>
{
	policyBuilder.AddRequirements(new CustomRequireClaim(ClaimTypes.Country));
});

同樣需要:

services.AddScoped<IAuthorizationHandler, CustomRequireClaimHandler>();

這裡就有疑問了,你這個policyBuilder.AddRequirements(new CustomRequireClaim(ClaimTypes.Country));就能呼叫到CustomRequireClaimHandler。
這個疑問簡單化就是,你有一個需求,就只調用到檢查類?
其實是在軟體設計中,有這樣一種套路,就是根據實體類,能自動呼叫執行類,這是一種設計模式。其實也能理解,就是呢,比如說公司有一張請假單,都標明是請假單了,肯定交給部門經理啊,AuthorizationHandler
就是標記,處理CustomRequireClaim的。
在裡面其實只有IAuthorizationHandler,但是根據CustomRequireClaim能例項出CustomRequireClaimHandler。所以也需要加上:

services.AddScoped<IAuthorizationHandler, CustomRequireClaimHandler>();

同樣,回過頭來,看下面的。

policyBuilder.RequireClaim(ClaimTypes.Country);

如何做到直接點出來,不用這種add的方式呢?也就是說我也想實現這樣的。

policyBuilder.RequireCustomClaim(ClaimTypes.Country);

我要能RequireCustomClaim然後可以呼叫到我給的限制.
下面是使用擴充套件的方式實現:

public static class AuthorizationPolicyBuilderExtensions
{
	public static AuthorizationPolicyBuilder RequireCustomClaim(this AuthorizationPolicyBuilder policyBuilder,string claimType)
	{
		policyBuilder.AddRequirements(new CustomRequireClaim(claimType));
		return policyBuilder;
	}
}

擴充套件一下AuthorizationPolicyBuilder 就可以,這樣方便複用性。
以前的時候我們這樣寫:

[Authorize(Roles  = "admin")]

在這裡就會檢查我們的證書中是否有Roles 為admin。
然後呢,我們同樣可以通過policy去實現。

config.AddPolicy("Claim.Role", policyBuilder =>
{
	policyBuilder.RequireClaim(ClaimTypes.Role, "admin");
});

限制需要admin也是可以的,然後[Authorize(Policy= "Claim.Role")]。
具體看自己需求。

總結

後續繼續寫自己對認證授權理解。以上均為個人理解,如有誤差,請指正。