重新整理 .net core 實踐篇——— 許可權中介軟體原始碼閱讀[四十六]

敖毛毛發表於2021-11-28

前言

前面介紹了認證中介軟體,下面看一下授權中介軟體。

正文

app.UseAuthorization();

授權中介軟體是這個,前面我們提及到認證中介軟體並不會讓整個中介軟體停止。

認證中介軟體就兩個作用,我們的認證方案如果實現了IAuthenticationRequestHandler,那麼會呼叫HandleRequestAsync判斷是否繼續執行。

然後我們設定預設的認證方案,那麼會呼叫其認證方案的具體的處理,如果認證成功,那麼會賦予context.User。

但是在這個認證中介軟體如果認證不過,那麼也會繼續往下執行,因為我們還有其他一些不需要認證就可以訪問的方法,所以中介軟體不能一棒子打死。

這個認證中介軟體的作用主要部分是如果這個使用者能通過預設的認證方案,那麼就給他打上了一個標籤,這個標籤就是context.User。

context.user 是public abstract ClaimsPrincipal User { get; set; },裡面存著使用者的一些資訊。 認證中介軟體做的是這件事,那麼看下授權中介軟體乾的是什麼吧。

public AuthorizationMiddleware(
  RequestDelegate next,
  IAuthorizationPolicyProvider policyProvider)
{
  RequestDelegate requestDelegate = next;
  if (requestDelegate == null)
	throw new ArgumentNullException(nameof (next));
  this._next = requestDelegate;
  IAuthorizationPolicyProvider authorizationPolicyProvider = policyProvider;
  if (authorizationPolicyProvider == null)
	throw new ArgumentNullException(nameof (policyProvider));
  this._policyProvider = authorizationPolicyProvider;
}

裡面有一個IAuthorizationPolicyProvider,這個就是用來管理授權策略的管理的,裡面不出所料應該就是授權策略的增刪改查了。

  public interface IAuthorizationPolicyProvider
  {
    Task<AuthorizationPolicy?> GetPolicyAsync(string policyName);

    Task<AuthorizationPolicy> GetDefaultPolicyAsync();

    Task<AuthorizationPolicy?> GetFallbackPolicyAsync();
  }

好吧,裡面只有查詢,並沒有增加,看下增加應該在別的地方。

經過我們前面的原始碼檢視呢,我們發現provider 結尾的是提供的意思,xxProvoder,就是為我們提供什麼,那麼裡面就一定有get之類的東西。

可能xxProvoder裡面也有增加和刪除和改,但是也不一定,看設計複雜性,如果設計複雜一點,那麼增加刪除和改會放到另外一個地方,那麼這個就是設計到另外一個地方,看了是相對來說複雜一點。

那麼來看一下invoke:

public async Task Invoke(HttpContext context)
{
  Endpoint endpoint;
  AuthorizationPolicy policy;
  IPolicyEvaluator policyEvaluator;
  try
  {
	if (context == null)
	  throw new ArgumentNullException(nameof (context));
	endpoint = context.GetEndpoint();
	if (endpoint != null)
	  context.Items[(object) "__AuthorizationMiddlewareWithEndpointInvoked"] = AuthorizationMiddleware.AuthorizationMiddlewareWithEndpointInvokedValue;
	Endpoint endpoint1 = endpoint;
	TaskAwaiter<AuthorizationPolicy> awaiter1 = AuthorizationPolicy.CombineAsync(this._policyProvider, (IEnumerable<IAuthorizeData>) ((endpoint1 != null ? (object) endpoint1.Metadata.GetOrderedMetadata<IAuthorizeData>() : (object) null) ?? (object) Array.Empty<IAuthorizeData>())).GetAwaiter();
	int num;
	if (!awaiter1.IsCompleted)
	{
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003E1__state = num = 0;
	  TaskAwaiter<AuthorizationPolicy> taskAwaiter = awaiter1;
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthorizationPolicy>, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter1, this);
	  return;
	}
	policy = awaiter1.GetResult();
	TaskAwaiter taskAwaiter1;
	if (policy == null)
	{
	  TaskAwaiter awaiter2 = this._next(context).GetAwaiter();
	  if (!awaiter2.IsCompleted)
	  {
		// ISSUE: explicit reference operation
		// ISSUE: reference to a compiler-generated field
		(^this).\u003C\u003E1__state = num = 1;
		taskAwaiter1 = awaiter2;
		// ISSUE: explicit reference operation
		// ISSUE: reference to a compiler-generated field
		(^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter2, this);
		return;
	  }
	  awaiter2.GetResult();
	}
	else
	{
	  policyEvaluator = context.RequestServices.GetRequiredService<IPolicyEvaluator>();
	  TaskAwaiter<AuthenticateResult> awaiter2 = policyEvaluator.AuthenticateAsync(policy, context).GetAwaiter();
	  if (!awaiter2.IsCompleted)
	  {
		// ISSUE: explicit reference operation
		// ISSUE: reference to a compiler-generated field
		(^this).\u003C\u003E1__state = num = 2;
		TaskAwaiter<AuthenticateResult> taskAwaiter2 = awaiter2;
		// ISSUE: explicit reference operation
		// ISSUE: reference to a compiler-generated field
		(^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthenticateResult>, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter2, this);
		return;
	  }
	  AuthenticateResult result1 = awaiter2.GetResult();
	  if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)
	  {
		TaskAwaiter awaiter3 = this._next(context).GetAwaiter();
		if (!awaiter3.IsCompleted)
		{
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003E1__state = num = 3;
		  taskAwaiter1 = awaiter3;
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter3, this);
		  return;
		}
		awaiter3.GetResult();
	  }
	  else
	  {
		bool isEnabled;
		object resource = !(AppContext.TryGetSwitch("Microsoft.AspNetCore.Authorization.SuppressUseHttpContextAsAuthorizationResource", out isEnabled) & isEnabled) ? (object) context : (object) endpoint;
		TaskAwaiter<PolicyAuthorizationResult> awaiter3 = policyEvaluator.AuthorizeAsync(policy, result1, context, resource).GetAwaiter();
		if (!awaiter3.IsCompleted)
		{
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003E1__state = num = 4;
		  TaskAwaiter<PolicyAuthorizationResult> taskAwaiter2 = awaiter3;
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<PolicyAuthorizationResult>, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter3, this);
		  return;
		}
		PolicyAuthorizationResult result2 = awaiter3.GetResult();
		TaskAwaiter awaiter4 = context.RequestServices.GetRequiredService<IAuthorizationMiddlewareResultHandler>().HandleAsync(this._next, context, policy, result2).GetAwaiter();
		if (!awaiter4.IsCompleted)
		{
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003E1__state = num = 5;
		  taskAwaiter1 = awaiter4;
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter4, this);
		  return;
		}
		awaiter4.GetResult();
	  }
	}
  }
  catch (Exception ex)
  {
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003E1__state = -2;
	endpoint = (Endpoint) null;
	policy = (AuthorizationPolicy) null;
	policyEvaluator = (IPolicyEvaluator) null;
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003Et__builder.SetException(ex);
	return;
  }
  // ISSUE: explicit reference operation
  // ISSUE: reference to a compiler-generated field
  (^this).\u003C\u003E1__state = -2;
  endpoint = (Endpoint) null;
  policy = (AuthorizationPolicy) null;
  policyEvaluator = (IPolicyEvaluator) null;
  // ISSUE: explicit reference operation
  // ISSUE: reference to a compiler-generated field
  (^this).\u003C\u003Et__builder.SetResult();
}

一段一段來看。

  Endpoint endpoint;
  AuthorizationPolicy policy;
  IPolicyEvaluator policyEvaluator;
  try
  {
	if (context == null)
	  throw new ArgumentNullException(nameof (context));
	endpoint = context.GetEndpoint();
	if (endpoint != null)
	  context.Items[(object) "__AuthorizationMiddlewareWithEndpointInvoked"] = AuthorizationMiddleware.AuthorizationMiddlewareWithEndpointInvokedValue;
	Endpoint endpoint1 = endpoint;
	TaskAwaiter<AuthorizationPolicy> awaiter1 = AuthorizationPolicy.CombineAsync(this._policyProvider, (IEnumerable<IAuthorizeData>) ((endpoint1 != null ? (object) endpoint1.Metadata.GetOrderedMetadata<IAuthorizeData>() : (object) null) ?? (object) Array.Empty<IAuthorizeData>())).GetAwaiter();

這一段呢,我們可以看到主要是獲取我們的授權策略。

首先通過endpoint = context.GetEndpoint(); 獲取我們的路由終點。

不知道各位對endpoint 的瞭解有多少呢? 後面會整理一下進行介紹,主要介紹一下app.UseRouting();和app.UseEndpoints();這兩個東西來介紹一下的,現在只需要知道Endpoint裝載這一些我們的路由資訊以及屬性特徵。

endpoint1.Metadata.GetOrderedMetadata() 獲取關於IAuthorizeData 的後設資料,不知道大家對後設資料瞭解多少哈,這裡科普一下,這裡我們可以理解為屬性資料即可哈。

後設資料(Metadata),又稱中介資料、中繼資料,為描述資料的資料(data about data),主要是描述資料屬性(property)的資訊,用來支援如指示儲存位置、歷史資料、資源查詢、檔案記錄等功能。後設資料算是一種電子式目錄,為了達到編制目錄的目的,必須在描述並收藏資料的內容或特色,進而達成協助資料檢索的目的。都柏林核心集(Dublin Core Metadata Initiative,DCMI)是後設資料的一種應用,是1995年2月由國際圖書館電腦中心(OCLC)和美國國家超級計算應用中心(National Center for Supercomputing Applications,NCSA)所聯合贊助的研討會,在邀請52位來自圖書館員、電腦專家,共同制定規格,建立一套描述網路上電子檔案之特徵。

這裡就是獲取到我們的屬性了,獲取到我們的IAuthorizeData 屬性,看到這個東西是不是特別熟系?

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
  public class AuthorizeAttribute : Attribute, IAuthorizeData
  {
    public AuthorizeAttribute()
    {
    }

    public AuthorizeAttribute(string policy)
    {
      this.Policy = policy;
    }

    public string? Policy { get; set; }

    public string? Roles { get; set; }

    public string? AuthenticationSchemes { get; set; }
  }

是的,它就會獲取到我們配置的許可權方案。

好的,那麼我們看下AuthorizationPolicy.CombineAsync這個東西。

public static async Task<AuthorizationPolicy?> CombineAsync(
  IAuthorizationPolicyProvider policyProvider,
  IEnumerable<IAuthorizeData> authorizeData)
{
  // ISSUE: explicit reference operation
  // ISSUE: reference to a compiler-generated field
  int num1 = (^this).\u003C\u003E1__state;
  AuthorizationPolicyBuilder policyBuilder;
  AuthorizationPolicy result1;
  try
  {
	if (policyProvider == null)
	  throw new ArgumentNullException(nameof (policyProvider));
	if (authorizeData == null)
	  throw new ArgumentNullException(nameof (authorizeData));
	bool flag1 = false;
	if (authorizeData is IList<IAuthorizeData> authorizeDataList)
	  flag1 = authorizeDataList.Count == 0;
	policyBuilder = (AuthorizationPolicyBuilder) null;
	TaskAwaiter<AuthorizationPolicy> taskAwaiter;
	if (!flag1)
	{
	  IEnumerator<IAuthorizeData> enumerator = authorizeData.GetEnumerator();
	  try
	  {
		while (enumerator.MoveNext())
		{
		  IAuthorizeData authorizeDatum = enumerator.Current;
		  if (policyBuilder == null)
			policyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
		  bool flag2 = true;
		  TaskAwaiter<AuthorizationPolicy> awaiter;
		  if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy))
		  {
			awaiter = policyProvider.GetPolicyAsync(authorizeDatum.Policy).GetAwaiter();
			if (!awaiter.IsCompleted)
			{
			  // ISSUE: explicit reference operation
			  // ISSUE: reference to a compiler-generated field
			  (^this).\u003C\u003E1__state = num1 = 0;
			  taskAwaiter = awaiter;
			  // ISSUE: explicit reference operation
			  // ISSUE: reference to a compiler-generated field
			  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthorizationPolicy>, AuthorizationPolicy.\u003CCombineAsync\u003Ed__9>(ref awaiter, this);
			  return;
			}
			AuthorizationPolicy result2 = awaiter.GetResult();
			if (result2 == null)
			  throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound((object) authorizeDatum.Policy));
			policyBuilder.Combine(result2);
			flag2 = false;
		  }
		  string[] strArray1 = authorizeDatum.Roles?.Split(',', StringSplitOptions.None);
		  if (strArray1 != null && strArray1.Length != 0)
		  {
			policyBuilder.RequireRole(((IEnumerable<string>) strArray1).Where<string>((Func<string, bool>) (r => !string.IsNullOrWhiteSpace(r))).Select<string, string>((Func<string, string>) (r => r.Trim())));
			flag2 = false;
		  }
		  string[] strArray2 = authorizeDatum.AuthenticationSchemes?.Split(',', StringSplitOptions.None);
		  if (strArray2 != null && strArray2.Length != 0)
		  {
			foreach (string str in strArray2)
			{
			  if (!string.IsNullOrWhiteSpace(str))
				policyBuilder.AuthenticationSchemes.Add(str.Trim());
			}
		  }
		  if (flag2)
		  {
			AuthorizationPolicyBuilder authorizationPolicyBuilder = policyBuilder;
			awaiter = policyProvider.GetDefaultPolicyAsync().GetAwaiter();
			if (!awaiter.IsCompleted)
			{
			  // ISSUE: explicit reference operation
			  // ISSUE: reference to a compiler-generated field
			  (^this).\u003C\u003E1__state = num1 = 1;
			  taskAwaiter = awaiter;
			  // ISSUE: explicit reference operation
			  // ISSUE: reference to a compiler-generated field
			  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthorizationPolicy>, AuthorizationPolicy.\u003CCombineAsync\u003Ed__9>(ref awaiter, this);
			  return;
			}
			authorizationPolicyBuilder.Combine(awaiter.GetResult());
			authorizationPolicyBuilder = (AuthorizationPolicyBuilder) null;
		  }
		  authorizeDatum = (IAuthorizeData) null;
		}
	  }
	  finally
	  {
		if (num1 < 0 && enumerator != null)
		  enumerator.Dispose();
	  }
	  enumerator = (IEnumerator<IAuthorizeData>) null;
	}
	if (policyBuilder == null)
	{
	  TaskAwaiter<AuthorizationPolicy> awaiter = policyProvider.GetFallbackPolicyAsync().GetAwaiter();
	  if (!awaiter.IsCompleted)
	  {
		int num2;
		// ISSUE: explicit reference operation
		// ISSUE: reference to a compiler-generated field
		(^this).\u003C\u003E1__state = num2 = 2;
		taskAwaiter = awaiter;
		// ISSUE: explicit reference operation
		// ISSUE: reference to a compiler-generated field
		(^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthorizationPolicy>, AuthorizationPolicy.\u003CCombineAsync\u003Ed__9>(ref awaiter, this);
		return;
	  }
	  AuthorizationPolicy result2 = awaiter.GetResult();
	  if (result2 != null)
	  {
		result1 = result2;
		goto label_43;
	  }
	}
	result1 = policyBuilder?.Build();
  }
  catch (Exception ex)
  {
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003E1__state = -2;
	policyBuilder = (AuthorizationPolicyBuilder) null;
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003Et__builder.SetException(ex);
	return;
  }
label_43:
  // ISSUE: explicit reference operation
  // ISSUE: reference to a compiler-generated field
  (^this).\u003C\u003E1__state = -2;
  policyBuilder = (AuthorizationPolicyBuilder) null;
  // ISSUE: explicit reference operation
  // ISSUE: reference to a compiler-generated field
  (^this).\u003C\u003Et__builder.SetResult(result1);
}

先貼一下的確有點長,但是我們只看正常授權部分其他不看哈。

  int num1 = (^this).\u003C\u003E1__state;
  AuthorizationPolicyBuilder policyBuilder;
  AuthorizationPolicy result1;
  try
  {
	if (policyProvider == null)
	  throw new ArgumentNullException(nameof (policyProvider));
	if (authorizeData == null)
	  throw new ArgumentNullException(nameof (authorizeData));
	bool flag1 = false;
	if (authorizeData is IList<IAuthorizeData> authorizeDataList)
	  flag1 = authorizeDataList.Count == 0;
	policyBuilder = (AuthorizationPolicyBuilder) null;
	TaskAwaiter<AuthorizationPolicy> taskAwaiter;

這一段是做一些邏輯判斷,判斷我們是否配置了授權策略。

if (!flag1)
{
  IEnumerator<IAuthorizeData> enumerator = authorizeData.GetEnumerator();
  try
  {
	while (enumerator.MoveNext())
	{
	  IAuthorizeData authorizeDatum = enumerator.Current;
	  if (policyBuilder == null)
		policyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
	  bool flag2 = true;
	  TaskAwaiter<AuthorizationPolicy> awaiter;
	  if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy))
	  {
		awaiter = policyProvider.GetPolicyAsync(authorizeDatum.Policy).GetAwaiter();
		if (!awaiter.IsCompleted)
		{
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003E1__state = num1 = 0;
		  taskAwaiter = awaiter;
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthorizationPolicy>, AuthorizationPolicy.\u003CCombineAsync\u003Ed__9>(ref awaiter, this);
		  return;
		}
		AuthorizationPolicy result2 = awaiter.GetResult();
		if (result2 == null)
		  throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound((object) authorizeDatum.Policy));
		policyBuilder.Combine(result2);
		flag2 = false;
	  }
	  string[] strArray1 = authorizeDatum.Roles?.Split(',', StringSplitOptions.None);
	  if (strArray1 != null && strArray1.Length != 0)
	  {
		policyBuilder.RequireRole(((IEnumerable<string>) strArray1).Where<string>((Func<string, bool>) (r => !string.IsNullOrWhiteSpace(r))).Select<string, string>((Func<string, string>) (r => r.Trim())));
		flag2 = false;
	  }
	  string[] strArray2 = authorizeDatum.AuthenticationSchemes?.Split(',', StringSplitOptions.None);
	  if (strArray2 != null && strArray2.Length != 0)
	  {
		foreach (string str in strArray2)
		{
		  if (!string.IsNullOrWhiteSpace(str))
			policyBuilder.AuthenticationSchemes.Add(str.Trim());
		}
	  }
	  if (flag2)
	  {
		AuthorizationPolicyBuilder authorizationPolicyBuilder = policyBuilder;
		awaiter = policyProvider.GetDefaultPolicyAsync().GetAwaiter();
		if (!awaiter.IsCompleted)
		{
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003E1__state = num1 = 1;
		  taskAwaiter = awaiter;
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthorizationPolicy>, AuthorizationPolicy.\u003CCombineAsync\u003Ed__9>(ref awaiter, this);
		  return;
		}
		authorizationPolicyBuilder.Combine(awaiter.GetResult());
		authorizationPolicyBuilder = (AuthorizationPolicyBuilder) null;
	  }
	  authorizeDatum = (IAuthorizeData) null;
	}
  }
  finally
  {
	if (num1 < 0 && enumerator != null)
	  enumerator.Dispose();
  }
  enumerator = (IEnumerator<IAuthorizeData>) null;
}

這一段是如果我們配置了授權策略,那麼如何處理哈,裡面通過while 會處理每一個授權策略。

IAuthorizeData authorizeDatum = enumerator.Current;
  if (policyBuilder == null)
	policyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
  bool flag2 = true;
  TaskAwaiter<AuthorizationPolicy> awaiter;
  if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy))
  {
	awaiter = policyProvider.GetPolicyAsync(authorizeDatum.Policy).GetAwaiter();
	if (!awaiter.IsCompleted)
	{
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003E1__state = num1 = 0;
	  taskAwaiter = awaiter;
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthorizationPolicy>, AuthorizationPolicy.\u003CCombineAsync\u003Ed__9>(ref awaiter, this);
	  return;
	}
	AuthorizationPolicy result2 = awaiter.GetResult();
	if (result2 == null)
	  throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound((object) authorizeDatum.Policy));
	policyBuilder.Combine(result2);
	flag2 = false;
  }

這個是找出策略模式的授權方案,然後儲存到AuthorizationPolicyBuilder中。

string[] strArray1 = authorizeDatum.Roles?.Split(',', StringSplitOptions.None);
  if (strArray1 != null && strArray1.Length != 0)
  {
	policyBuilder.RequireRole(((IEnumerable<string>) strArray1).Where<string>((Func<string, bool>) (r => !string.IsNullOrWhiteSpace(r))).Select<string, string>((Func<string, string>) (r => r.Trim())));
	flag2 = false;
  }

這裡是找出基於角色的授權,然後放入到AuthorizationPolicyBuilder 中。

  string[] strArray2 = authorizeDatum.AuthenticationSchemes?.Split(',', StringSplitOptions.None);
  if (strArray2 != null && strArray2.Length != 0)
  {
	foreach (string str in strArray2)
	{
	  if (!string.IsNullOrWhiteSpace(str))
		policyBuilder.AuthenticationSchemes.Add(str.Trim());
	}
  }

這上面是找出認證方式,然後加入到AuthorizationPolicyBuilder 的AuthenticationSchemes中。

if (flag2)
  {
	AuthorizationPolicyBuilder authorizationPolicyBuilder = policyBuilder;
	awaiter = policyProvider.GetDefaultPolicyAsync().GetAwaiter();
	if (!awaiter.IsCompleted)
	{
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003E1__state = num1 = 1;
	  taskAwaiter = awaiter;
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthorizationPolicy>, AuthorizationPolicy.\u003CCombineAsync\u003Ed__9>(ref awaiter, this);
	  return;
	}
	authorizationPolicyBuilder.Combine(awaiter.GetResult());
	authorizationPolicyBuilder = (AuthorizationPolicyBuilder) null;
  }

這裡就是如果我們沒有指明我們的授權方式,那麼將使用預設的授權驗證方式。

if (policyBuilder == null)
{
  TaskAwaiter<AuthorizationPolicy> awaiter = policyProvider.GetFallbackPolicyAsync().GetAwaiter();
  if (!awaiter.IsCompleted)
  {
	int num2;
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003E1__state = num2 = 2;
	taskAwaiter = awaiter;
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthorizationPolicy>, AuthorizationPolicy.\u003CCombineAsync\u003Ed__9>(ref awaiter, this);
	return;
  }
  AuthorizationPolicy result2 = awaiter.GetResult();
  if (result2 != null)
  {
	result1 = result2;
	goto label_43;
  }
}
result1 = policyBuilder?.Build();

這裡如果我們沒有設定IAuthorizeData 屬性,那麼將會使用FallbackPolicy。

同樣,我們常說我們有基於角色授權、基於宣告授權,基於策略授權,那麼其實在原始碼的概念裡面其實就只有基於角色和基於策略,基於宣告其實就是一種特殊的策略授權,由官方封裝了一下。

那麼從這裡我們就拿到了AuthorizationPolicy,那麼繼續在AuthorizationMiddleware 往下看,AuthorizationPolicy 是我們一系列的授權設定,並不是指一個[Authorize],而是指當前訪問介面的組合授權的新策略。

裡面可能是基於幾個角色授權方案,有可能基於幾個策略授權方案,那麼是否必須通過這些全部的策略方案,還是通過一個即可呢。這兩種可能性都有,比如我們要角色是user,而且還要通過積分策略(積分達到一定的標準)。也有可能我們策略是通過user,或者是advanceduser都可以。

int num;
if (!awaiter1.IsCompleted)
{
  // ISSUE: explicit reference operation
  // ISSUE: reference to a compiler-generated field
  (^this).\u003C\u003E1__state = num = 0;
  TaskAwaiter<AuthorizationPolicy> taskAwaiter = awaiter1;
  // ISSUE: explicit reference operation
  // ISSUE: reference to a compiler-generated field
  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthorizationPolicy>, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter1, this);
  return;
}
policy = awaiter1.GetResult();
TaskAwaiter taskAwaiter1;
if (policy == null)
{
  TaskAwaiter awaiter2 = this._next(context).GetAwaiter();
  if (!awaiter2.IsCompleted)
  {
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003E1__state = num = 1;
	taskAwaiter1 = awaiter2;
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter2, this);
	return;
  }
  awaiter2.GetResult();
}

這裡我們可以看到當我們返回的策略是空的時候,那麼直接執行下一個中介軟體,繼續往下看,如果我們設定的授權策略不為空呢。

policyEvaluator = context.RequestServices.GetRequiredService<IPolicyEvaluator>();
  TaskAwaiter<AuthenticateResult> awaiter2 = policyEvaluator.AuthenticateAsync(policy, context).GetAwaiter();
  if (!awaiter2.IsCompleted)
  {
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003E1__state = num = 2;
	TaskAwaiter<AuthenticateResult> taskAwaiter2 = awaiter2;
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<AuthenticateResult>, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter2, this);
	return;
  }
  AuthenticateResult result1 = awaiter2.GetResult();

這一塊,那麼就是我們有我們設定的授權方案裡面的認證方案,進行認證一下。

if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)
  {
	TaskAwaiter awaiter3 = this._next(context).GetAwaiter();
	if (!awaiter3.IsCompleted)
	{
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003E1__state = num = 3;
	  taskAwaiter1 = awaiter3;
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter3, this);
	  return;
	}
	awaiter3.GetResult();
  }
  else
  {
	bool isEnabled;
	object resource = !(AppContext.TryGetSwitch("Microsoft.AspNetCore.Authorization.SuppressUseHttpContextAsAuthorizationResource", out isEnabled) & isEnabled) ? (object) context : (object) endpoint;
	TaskAwaiter<PolicyAuthorizationResult> awaiter3 = policyEvaluator.AuthorizeAsync(policy, result1, context, resource).GetAwaiter();
	if (!awaiter3.IsCompleted)
	{
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003E1__state = num = 4;
	  TaskAwaiter<PolicyAuthorizationResult> taskAwaiter2 = awaiter3;
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<PolicyAuthorizationResult>, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter3, this);
	  return;
	}
	PolicyAuthorizationResult result2 = awaiter3.GetResult();
	TaskAwaiter awaiter4 = context.RequestServices.GetRequiredService<IAuthorizationMiddlewareResultHandler>().HandleAsync(this._next, context, policy, result2).GetAwaiter();
	if (!awaiter4.IsCompleted)
	{
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003E1__state = num = 5;
	  taskAwaiter1 = awaiter4;
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, AuthorizationMiddleware.\u003CInvoke\u003Ed__6>(ref awaiter4, this);
	  return;
	}
	awaiter4.GetResult();
  }
}

這一塊就是如果我們有後設資料IAllowAnonymous,那麼我們都直接進行下一個中介軟體即可。

我們的AllowAnonymousAttribute 就繼承IAllowAnonymous。

如果沒有繼承IAllowAnonymous,那麼就進行授權處理:TaskAwaiter awaiter3 = policyEvaluator.AuthorizeAsync(policy, result1, context, resource).GetAwaiter();

TaskAwaiter awaiter4 = context.RequestServices.GetRequiredService().HandleAsync(this._next, context, policy, result2).GetAwaiter();。這個處理授權結果。

這就是我們看到的授權的抽象過程,為什麼是抽象過程呢?這個我們很多看到的都是介面,瞭解了其流程,但是具體實現細節我們不知道。

那麼我們得去看注入服務services.AddAuthorization();。

看之前我們要有我們看的一個目的,上面有的一些疑問是什麼是defaultPolicy還有FallbackPolicy確定的規則是什麼,這讓我們得關心一下IAuthorizationPolicyProvider的具體實現,還有需要知道到底是處理我們認證和授權的,那麼得關心一下IPolicyEvaluator和IAuthorizationMiddlewareResultHandler的具體實現。懷抱中這樣的目的接著往下看吧。

public static IServiceCollection AddAuthorization(
  this IServiceCollection services)
{
  if (services == null)
	throw new ArgumentNullException(nameof (services));
  services.AddAuthorizationCore();
  services.AddAuthorizationPolicyEvaluator();
  return services;
}

一下子就能看到core,那麼我們知道結尾帶core的,那麼是這個服務的核心實現了,看下AddAuthorizationCore。

public static IServiceCollection AddAuthorizationCore(
  this IServiceCollection services)
{
  if (services == null)
	throw new ArgumentNullException(nameof (services));
  services.AddOptions();
  services.TryAdd(ServiceDescriptor.Transient<IAuthorizationService, DefaultAuthorizationService>());
  services.TryAdd(ServiceDescriptor.Transient<IAuthorizationPolicyProvider, DefaultAuthorizationPolicyProvider>());
  services.TryAdd(ServiceDescriptor.Transient<IAuthorizationHandlerProvider, DefaultAuthorizationHandlerProvider>());
  services.TryAdd(ServiceDescriptor.Transient<IAuthorizationEvaluator, DefaultAuthorizationEvaluator>());
  services.TryAdd(ServiceDescriptor.Transient<IAuthorizationHandlerContextFactory, DefaultAuthorizationHandlerContextFactory>());
  services.TryAddEnumerable(ServiceDescriptor.Transient<IAuthorizationHandler, PassThroughAuthorizationHandler>());
  return services;
}

那麼這裡我們之間看到了IAuthorizationPolicyProvider。那麼這裡我們看一下預設策略和Fallback策略是什麼。

public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
{
  if (this._cachedDefaultPolicy == null || this._cachedDefaultPolicy.Result != this._options.DefaultPolicy)
	this._cachedDefaultPolicy = Task.FromResult<AuthorizationPolicy>(this._options.DefaultPolicy);
  return this._cachedDefaultPolicy;
}

這裡訪問的就是我們的AuthorizationOptions的DefaultPolicy,那麼就看一下這個DefaultPolicy是什麼。

public AuthorizationPolicy DefaultPolicy { get; set; } = new AuthorizationPolicyBuilder(Array.Empty<string>()).RequireAuthenticatedUser().Build();

那麼看一下RequireAuthenticatedUser吧。

public AuthorizationPolicyBuilder RequireAuthenticatedUser()
{
  this.Requirements.Add((IAuthorizationRequirement) new DenyAnonymousAuthorizationRequirement());
  return this;
}

然後繼續看DenyAnonymousAuthorizationRequirement。

  public class DenyAnonymousAuthorizationRequirement : AuthorizationHandler<DenyAnonymousAuthorizationRequirement>, IAuthorizationRequirement
  {
    protected override Task HandleRequirementAsync(
      AuthorizationHandlerContext context,
      DenyAnonymousAuthorizationRequirement requirement)
    {
      ClaimsPrincipal user = context.User;
      if ((user?.Identity == null ? 1 : (!user.Identities.Any<ClaimsIdentity>((Func<ClaimsIdentity, bool>) (i => i.IsAuthenticated)) ? 1 : 0)) == 0)
        context.Succeed((IAuthorizationRequirement) requirement);
      return Task.CompletedTask;
    }

    public override string ToString()
    {
      return "DenyAnonymousAuthorizationRequirement: Requires an authenticated user.";
    }
  }

這裡面就是檢查context.User的一些資訊哈,之間檢查是否認證成功了。也就是這個授權策略就是認證成功了那麼都可以。

那麼我們看一下GetFallbackPolicyAsync。

public Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
{
  if (this._cachedFallbackPolicy == null || this._cachedFallbackPolicy.Result != this._options.FallbackPolicy)
	this._cachedFallbackPolicy = Task.FromResult<AuthorizationPolicy>(this._options.FallbackPolicy);
  return this._cachedFallbackPolicy;
}

那麼看下這個FallbackPolicy。

public AuthorizationPolicy? FallbackPolicy { get; set; }

那麼這個應該是我們設定的,如果我們不設定,那麼就是空的。

那麼繼續看一下這個AddAuthorizationPolicyEvaluator。

public static IServiceCollection AddAuthorizationPolicyEvaluator(
  this IServiceCollection services)
{
  if (services == null)
	throw new ArgumentNullException(nameof (services));
  services.TryAddSingleton<AuthorizationPolicyMarkerService>();
  services.TryAddTransient<IPolicyEvaluator, PolicyEvaluator>();
  services.TryAddTransient<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();
  return services;
}

裡面存在前面需要知道的IPolicyEvaluator 和 IAuthorizationMiddlewareResultHandler。

先看這個PolicyEvaluator,看下如何處理我們授權的。

public virtual async Task<AuthenticateResult> AuthenticateAsync(
  AuthorizationPolicy policy,
  HttpContext context)
{
  if (policy.AuthenticationSchemes == null || policy.AuthenticationSchemes.Count <= 0)
	return context.User?.Identity?.IsAuthenticated.GetValueOrDefault() ? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User")) : AuthenticateResult.NoResult();
  ClaimsPrincipal newPrincipal = (ClaimsPrincipal) null;
  foreach (string authenticationScheme in (IEnumerable<string>) policy.AuthenticationSchemes)
  {
	AuthenticateResult authenticateResult = await context.AuthenticateAsync(authenticationScheme);
	if (authenticateResult != null && authenticateResult.Succeeded)
	  newPrincipal = SecurityHelper.MergeUserPrincipal(newPrincipal, authenticateResult.Principal);
  }
  if (newPrincipal != null)
  {
	context.User = newPrincipal;
	return AuthenticateResult.Success(new AuthenticationTicket(newPrincipal, string.Join(";", (IEnumerable<string>) policy.AuthenticationSchemes)));
  }
  context.User = new ClaimsPrincipal((IIdentity) new ClaimsIdentity());
  return AuthenticateResult.NoResult();
}

一段一段看哈。

if (policy.AuthenticationSchemes == null || policy.AuthenticationSchemes.Count <= 0)
        return context.User?.Identity?.IsAuthenticated.GetValueOrDefault() ? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User")) : AuthenticateResult.NoResult();

這一段就是如果我們沒有設定AuthenticationSchemes,直接返回context.User是否認證成功了。這裡結合上一章來說就是是否預設認證是否成功了。

foreach (string authenticationScheme in (IEnumerable<string>) policy.AuthenticationSchemes)
  {
	AuthenticateResult authenticateResult = await context.AuthenticateAsync(authenticationScheme);
	if (authenticateResult != null && authenticateResult.Succeeded)
	  newPrincipal = SecurityHelper.MergeUserPrincipal(newPrincipal, authenticateResult.Principal);
  }
  if (newPrincipal != null)
  {
	context.User = newPrincipal;
	return AuthenticateResult.Success(new AuthenticationTicket(newPrincipal, string.Join(";", (IEnumerable<string>) policy.AuthenticationSchemes)));
  }
  context.User = new ClaimsPrincipal((IIdentity) new ClaimsIdentity());
  return AuthenticateResult.NoResult();

上面看到的這一段就是隻要一個認證策略通過那麼就通過了。

那麼來看一下授權的處理吧。

public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(
  AuthorizationPolicy policy,
  AuthenticateResult authenticationResult,
  HttpContext context,
  object? resource)
{
  if (policy == null)
	throw new ArgumentNullException(nameof (policy));
  AuthorizationResult authorizationResult = await this._authorization.AuthorizeAsync(context.User, resource, policy);
  return !authorizationResult.Succeeded ? (authenticationResult.Succeeded ? PolicyAuthorizationResult.Forbid(authorizationResult.Failure) : PolicyAuthorizationResult.Challenge()) : PolicyAuthorizationResult.Success();
}

這裡是呼叫了IAuthorizationService的AuthorizeAsync。

那麼我看看一下IAuthorizationService的具體實現類DefaultAuthorizationService吧。

public static Task<AuthorizationResult> AuthorizeAsync(
  this IAuthorizationService service,
  ClaimsPrincipal user,
  object? resource,
  AuthorizationPolicy policy)
{
  if (service == null)
	throw new ArgumentNullException(nameof (service));
  if (policy == null)
	throw new ArgumentNullException(nameof (policy));
  return service.AuthorizeAsync(user, resource, (IEnumerable<IAuthorizationRequirement>) policy.Requirements);
}

然後繼續看:

public virtual async Task<AuthorizationResult> AuthorizeAsync(
  ClaimsPrincipal user,
  object? resource,
  IEnumerable<IAuthorizationRequirement> requirements)
{
  // ISSUE: explicit reference operation
  // ISSUE: reference to a compiler-generated field
  int num1 = (^this).\u003C\u003E1__state;
  AuthorizationHandlerContext authContext;
  AuthorizationResult result;
  try
  {
	if (requirements == null)
	  throw new ArgumentNullException(nameof (requirements));
	authContext = this._contextFactory.CreateContext(requirements, user, resource);
	TaskAwaiter<IEnumerable<IAuthorizationHandler>> awaiter1 = this._handlers.GetHandlersAsync(authContext).GetAwaiter();
	if (!awaiter1.IsCompleted)
	{
	  int num2;
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003E1__state = num2 = 0;
	  TaskAwaiter<IEnumerable<IAuthorizationHandler>> taskAwaiter = awaiter1;
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter<IEnumerable<IAuthorizationHandler>>, DefaultAuthorizationService.\u003CAuthorizeAsync\u003Ed__7>(ref awaiter1, this);
	  return;
	}
	IEnumerator<IAuthorizationHandler> enumerator = awaiter1.GetResult().GetEnumerator();
	try
	{
	  while (enumerator.MoveNext())
	  {
		TaskAwaiter awaiter2 = enumerator.Current.HandleAsync(authContext).GetAwaiter();
		if (!awaiter2.IsCompleted)
		{
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003E1__state = num1 = 1;
		  TaskAwaiter taskAwaiter = awaiter2;
		  // ISSUE: explicit reference operation
		  // ISSUE: reference to a compiler-generated field
		  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, DefaultAuthorizationService.\u003CAuthorizeAsync\u003Ed__7>(ref awaiter2, this);
		  return;
		}
		awaiter2.GetResult();
		if (!this._options.InvokeHandlersAfterFailure)
		{
		  if (authContext.HasFailed)
			break;
		}
	  }
	}
	finally
	{
	  if (num1 < 0 && enumerator != null)
		enumerator.Dispose();
	}
	enumerator = (IEnumerator<IAuthorizationHandler>) null;
	AuthorizationResult authorizationResult = this._evaluator.Evaluate(authContext);
	if (authorizationResult.Succeeded)
	  this._logger.UserAuthorizationSucceeded();
	else
	  this._logger.UserAuthorizationFailed(authorizationResult.Failure);
	result = authorizationResult;
  }
  catch (Exception ex)
  {
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003E1__state = -2;
	authContext = (AuthorizationHandlerContext) null;
	// ISSUE: explicit reference operation
	// ISSUE: reference to a compiler-generated field
	(^this).\u003C\u003Et__builder.SetException(ex);
	return;
  }
  // ISSUE: explicit reference operation
  // ISSUE: reference to a compiler-generated field
  (^this).\u003C\u003E1__state = -2;
  authContext = (AuthorizationHandlerContext) null;
  // ISSUE: explicit reference operation
  // ISSUE: reference to a compiler-generated field
  (^this).\u003C\u003Et__builder.SetResult(result);
}

那麼還是一段一段看吧。

if (requirements == null)
  throw new ArgumentNullException(nameof (requirements));
authContext = this._contextFactory.CreateContext(requirements, user, resource);
TaskAwaiter<IEnumerable<IAuthorizationHandler>> awaiter1 = this._handlers.GetHandlersAsync(authContext).GetAwaiter();

這一段就是IAuthorizationHandlerProvider 呼叫GetHandlersAsync 處理AuthorizationHandlerContext 生成IEnumerable

看一下這個AuthorizationHandlerContext。

public Task<IEnumerable<IAuthorizationHandler>> GetHandlersAsync(
  AuthorizationHandlerContext context)
{
  return Task.FromResult<IEnumerable<IAuthorizationHandler>>(this._handlers);
}

然後檢視一下這個GetHandlersAsync,其實跟這個AuthorizationHandlerContext 也沒啥關係,之所以有這個引數,估計是為了讓使用者可以覆寫這個方法,讓使用者獲取到上下文。

IEnumerator<IAuthorizationHandler> enumerator = awaiter1.GetResult().GetEnumerator();
try
{
  while (enumerator.MoveNext())
  {
	TaskAwaiter awaiter2 = enumerator.Current.HandleAsync(authContext).GetAwaiter();
	if (!awaiter2.IsCompleted)
	{
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003E1__state = num1 = 1;
	  TaskAwaiter taskAwaiter = awaiter2;
	  // ISSUE: explicit reference operation
	  // ISSUE: reference to a compiler-generated field
	  (^this).\u003C\u003Et__builder.AwaitUnsafeOnCompleted<TaskAwaiter, DefaultAuthorizationService.\u003CAuthorizeAsync\u003Ed__7>(ref awaiter2, this);
	  return;
	}
	awaiter2.GetResult();
	if (!this._options.InvokeHandlersAfterFailure)
	{
	  if (authContext.HasFailed)
		break;
	}
  }
}
finally
{
  if (num1 < 0 && enumerator != null)
	enumerator.Dispose();
}

這一段就是根據IAuthorizationRequirement匹配AuthorizationHandler 進行處理。

public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
  foreach (TRequirement requirement in context.Requirements.OfType<TRequirement>())
	await this.HandleRequirementAsync(context, requirement);
}

看一下這個HandleAsync 的具體實現哈,就是通過OfType來處理的,這種方式方便解耦哈。ofType 可以看一下:https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.enumerable.oftype?view=net-6.0。

這上面有一個值得注意的是這個配置:

if (!this._options.InvokeHandlersAfterFailure)
{
    if (authContext.HasFailed)
        break;
}

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.authorizationoptions.invokehandlersafterfailure?view=aspnetcore-6.0

這個配置預設是true,也就是說如何我們設定這個為false,只要我們有一個授權處理方案設定HasFailed,那麼將會終止。

接著往下看:

enumerator = (IEnumerator<IAuthorizationHandler>) null;
AuthorizationResult authorizationResult = this._evaluator.Evaluate(authContext);
if (authorizationResult.Succeeded)
  this._logger.UserAuthorizationSucceeded();
else
  this._logger.UserAuthorizationFailed(authorizationResult.Failure);
result = authorizationResult;

這個就是根據上下文得出授權成功或者失敗的結果了,並且會列印log。

Evaluate 看一下哈,授權失敗的標準。

public AuthorizationResult Evaluate(AuthorizationHandlerContext context)
{
  return !context.HasSucceeded ? AuthorizationResult.Failed(context.HasFailed ? AuthorizationFailure.ExplicitFail() : AuthorizationFailure.Failed(context.PendingRequirements)) : AuthorizationResult.Success();
}

就是這個HasSucceeded 。

public virtual bool HasSucceeded
{
  get
  {
	return !this._failCalled && this._succeedCalled && !this.PendingRequirements.Any<IAuthorizationRequirement>();
  }
}

所以其實我們的方案是可以進行組合式的,也就是說我們希望多個授權方案只有一個通過,或者多個授權方案都要通過是我們的授權處理具體設計來處理的。

比如說我們成功了就設定_succeedCalled 為true,失敗了也不設定_failCalled 為true,這樣就只有通過一個即可。

如果我們失敗了就設定_failCalled 為true,那麼就需要通過全部,看具體設計了,還可以組合多種設計。

那麼我們通過返回的結果得出結論AuthorizeAsync的返回結果是:

return !authorizationResult.Succeeded ? (authenticationResult.Succeeded ? PolicyAuthorizationResult.Forbid(authorizationResult.Failure) : PolicyAuthorizationResult.Challenge()) : PolicyAuthorizationResult.Success();

這裡可以說明,即使認證不成功,但是隻要是授權成功了,就依然是成功的。然後如果授權不成功,那麼如果認證失敗,返回 PolicyAuthorizationResult.Forbid(authorizationResult.Failure),否則返回PolicyAuthorizationResult.Challenge()。

這裡說明netcore 的設計思想是授權不一定要認證成功。

那麼我們最後看一下我們對授權的結果是如何進行處理的,IAuthorizationMiddlewareResultHandler的實現類AuthorizationMiddlewareResultHandler。

public async Task HandleAsync(
  RequestDelegate next,
  HttpContext context,
  AuthorizationPolicy policy,
  PolicyAuthorizationResult authorizeResult)
{
  if (authorizeResult.Challenged)
  {
	if (policy.AuthenticationSchemes.Count > 0)
	{
	  foreach (string authenticationScheme in (IEnumerable<string>) policy.AuthenticationSchemes)
		await context.ChallengeAsync(authenticationScheme);
	}
	else
	  await context.ChallengeAsync();
  }
  else if (authorizeResult.Forbidden)
  {
	if (policy.AuthenticationSchemes.Count > 0)
	{
	  foreach (string authenticationScheme in (IEnumerable<string>) policy.AuthenticationSchemes)
		await context.ForbidAsync(authenticationScheme);
	}
	else
	  await context.ForbidAsync();
  }
  else
	await next(context);
}

如果Challenged和Forbidden,那麼會進行相應的處理,如果都沒有,那麼直接通過進入下一個中介軟體。

Challenge 是授權失敗且認證失敗,一般是401,這個看自己怎麼處理了。Forbidden是認證成功,但是授權失敗,一般是403。

下一節介紹一下endpint,上述為個人整理,如有錯誤,往請指點。

相關文章