Record asp.net core authentication and authorization as a whole

In the process of using asp.net core to develop an application system, the authentication of user identity and authorization access control are basically involved. Therefore, it is also very important to understand the authentication and authorization process. The following analyzes the authentication and authorization in the asp.net core framework Source code to analyze the principles of authentication and authorization and the relationship between authentication and authorization.

What is certification?

Authentication is a process for the application system to identify the identity of the current visitor. When the application system receives the request from the browser, it usually identifies the identity of the currently logged-in user according to some key information of the user carried in the request. By parsing the information , to verify the validity of the user and decrypt it. If the verification is passed, it means that the authentication is passed. The application system will store the authenticated user information in the Http request context for subsequent business use and authorization process.

In asp.net core, the authentication information is usually encrypted and stored in a cookie, and the cookie information is sent to the application system every time a page requiring authentication is accessed, so that the application system can identify the identity of the visitor, which is the classic cookie authentication.

It should be noted that authentication only identifies the identity of the current access user, and is not responsible for the specific access control logic. If you do not have access to a resource, return 403, and return 401 if you are not logged in. These are all controlled by the authorization process. .

The middleware responsible for the authentication process in asp.net core is the AuthenticationMiddleware class . The following is the source code of asp.net core 3.1. As you can see, first traverse all the authentication schemes that implement the IAuthenticationRequestHandler interface, and call the HandleRequestAsync method of the IAuthenticationRequestHandler interface. If If the authentication is passed, it will not continue to execute, and at this time HttpContext.User already contains the authenticated user information. If all authentication schemes that implement the  IAuthenticationRequestHandler  interface fail to authenticate the current access user, the default authentication will be used Scheme for authentication (that is: the authentication scheme returned by GetDefaultAuthenticateSchemeAsync ), you can see that even if the authentication process fails to identify the user identity of the current visitor, it will continue to execute the next process (tail: await _next(context); )

public class AuthenticationMiddleware
{
	private readonly RequestDelegate _next;

	public IAuthenticationSchemeProvider Schemes
	{
		get;
		set;
	}

	public AuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes)
	{
		if (next == null)
		{
			throw new ArgumentNullException("next");
		}
		if (schemes == null)
		{
			throw new ArgumentNullException("schemes");
		}
		_next = next;
		Schemes = schemes;
	}

	public async Task Invoke(HttpContext context)
	{
		context.Features.Set((IAuthenticationFeature)new AuthenticationFeature
		{
			OriginalPath = context.Request.Path,
			OriginalPathBase = context.Request.PathBase
		});
		IAuthenticationHandlerProvider handlers = 
                          context.RequestServices.
                          GetRequiredService<IAuthenticationHandlerProvider>();
		foreach (AuthenticationScheme item in await Schemes.GetRequestHandlerSchemesAsync())
		{
			IAuthenticationRequestHandler authenticationRequestHandler = 
                                                (await handlers.
                                                GetHandlerAsync(context, item.Name))
                                                 as IAuthenticationRequestHandler;
			bool flag = authenticationRequestHandler != null;
			if (flag)
			{
				flag = await authenticationRequestHandler.HandleRequestAsync();
			}
			if (flag)
			{
				return;
			}
		}
                
		AuthenticationScheme authenticationScheme = 
                                        await Schemes.
                                              GetDefaultAuthenticateSchemeAsync();
		if (authenticationScheme != null)
		{
                        //内部调用IAuthenticationService进行认证。
			AuthenticateResult authenticateResult = 
                                     await context.
                                     AuthenticateAsync(authenticationScheme.Name);
			if (authenticateResult?.Principal != null)
			{
				context.User = authenticateResult.Principal;
			}
		}
		await _next(context);
	}
}

What is authorization?

Authorization is the process of determining whether the current access user has access to a certain system resource. For system resources that require authorization to access, they are usually identified by the [Authorize] feature . Through this feature, you can specify which user role the resource requires to access. Which authorization policy must be met to access, and what is the user authentication scheme used when accessing this resource? When a user accesses an API or page of the system, the authorization process will check whether the current user has access to the API or page. If If the authorization check fails, it will judge whether the current user has passed the authentication. If the authentication passes, but there is no permission to access the resource, then return 403 (forbidden), if not authenticated, then directly return 401 (unauthenticated), indicating that the user is required When accessing after login authentication, it should be noted that user identity authentication will be performed before checking whether the access authority is available. As for the authentication scheme to be used, it depends on whether the AuthorizeAttribute specifies a specific authentication scheme. If not, the authentication process will be adopted directly. The identity information of successful authentication.

In asp.net core, the authorization process is executed through the AuthorizationMiddleware class . The following is the source code in asp.net core 3.1.

public class AuthenticationMiddleware
{
	private readonly RequestDelegate _next;

	public IAuthenticationSchemeProvider Schemes
	{
		get;
		set;
	}

	public AuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes)
	{
		if (next == null)
		{
			throw new ArgumentNullException("next");
		}
		if (schemes == null)
		{
			throw new ArgumentNullException("schemes");
		}
		_next = next;
		Schemes = schemes;
	}

	public async Task Invoke(HttpContext context)
	{
		context.Features.Set((IAuthenticationFeature)new AuthenticationFeature
		{
			OriginalPath = context.Request.Path,
			OriginalPathBase = context.Request.PathBase
		});
		IAuthenticationHandlerProvider handlers =             
                   context.RequestServices.
                   GetRequiredService<IAuthenticationHandlerProvider>();
		foreach (AuthenticationScheme item in await Schemes.GetRequestHandlerSchemesAsync())
		{
			IAuthenticationRequestHandler authenticationRequestHandler = 
                    (await handlers.GetHandlerAsync(context, item.Name))
                     as IAuthenticationRequestHandler;
			bool flag = authenticationRequestHandler != null;
			if (flag)
			{
				flag = await authenticationRequestHandler.HandleRequestAsync();
			}
			if (flag)
			{
				return;
			}
		}
                
		AuthenticationScheme authenticationScheme = await Schemes.GetDefaultAuthenticateSchemeAsync();
		if (authenticationScheme != null)
		{
             //内部调用IAuthenticationService进行认证。
			AuthenticateResult authenticateResult = await context.AuthenticateAsync(authenticationScheme.Name);
			if (authenticateResult?.Principal != null)
			{
				context.User = authenticateResult.Principal;
			}
		}
		await _next(context);
	}
}

The IPolicyEvaluator interface implementation class PolicyEvaluator class code is as follows, this class is mainly responsible for authentication and authorization in the authorization process.

// Microsoft.AspNetCore.Authorization.Policy.PolicyEvaluator
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Internal;

public class PolicyEvaluator : IPolicyEvaluator
{
	private readonly IAuthorizationService _authorization;

	public PolicyEvaluator(IAuthorizationService authorization)
	{
		_authorization = authorization;
	}

	public virtual async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
	{
             //这里去判断当前资源是否有要求特定的认证方案进行认证,如果有指定特定的认证方案
             //则分别对每个认证方案进行认证,并把认证后的用户信息进行合并
             //最终存储到HttpContext.User属性中,并返回认证成功,如果没有指定认证方案
             //则使用认证流程中已经认证的用户信息作为认证结果返回,
            //从这里可以看出,认证流程还是很有必要的,在资源没有指定认证方案的前提下
            //认证流程为授权流程提供当前访问者的身份信息
            //以便执行是否具备相应资源的访问权限检查
            //否则就直接进入Challenge流程将要求用户先进行身份认证了
		if (policy.AuthenticationSchemes != null && policy.AuthenticationSchemes.Count > 0)
		{
			ClaimsPrincipal newPrincipal = null;
			foreach (string authenticationScheme in 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(";", policy.AuthenticationSchemes)));
			}
			context.User = new ClaimsPrincipal(new ClaimsIdentity());
			return AuthenticateResult.NoResult();
		}
		return (context.User?.Identity?.IsAuthenticated).GetValueOrDefault() ? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User")) : AuthenticateResult.NoResult();
	}
        //resource为EndPoint对象。
	public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
	{
		if (policy == null)
		{
			throw new ArgumentNullException("policy");
		}
                //这里调用IAuthorizationService.AuthorizeAsync方法进行授权检查
                //默认实现类为:DefaultAuthorizationService。
		if ((await _authorization.AuthorizeAsync(context.User, resource, policy)).Succeeded)
		{
			return PolicyAuthorizationResult.Success();
		}
                //下面这句表示如果授权检查失败的情况下是进入Forbid流程还是进入Challenge流程
                //可以看到如果认证成功,那么表示无权限访问进入Forbid流程。
                //如果未认证,则进入Challenge流程,引导用户登录认证。
		return authenticationResult.Succeeded ? PolicyAuthorizationResult.Forbid() : PolicyAuthorizationResult.Challenge();
	}
}

What is the relationship between authentication and authorization?

Before the authorization check, user identity authentication will be performed first, but the authentication process here will only be executed when the resource to be accessed has a specific authentication scheme specified, otherwise the authentication information generated in the unified authentication process will be used directly.

It can be understood that the authentication process is to tell the application system the identity of the current visitor on the one hand, and on the other hand to identify the user's identity information for the authorization check. When the resource does not specify which authentication scheme to use, the authorization process will use unified authentication. If the user information generated by the authentication in the process is not enabled, and the resource being accessed does not specify a specific authentication scheme to authenticate the identity of the visitor, it will still be required to log in for authentication when accessing the resource. Therefore Another purpose of the authentication process is to provide default user authentication information for the authorization process.

To sum it up,

The authentication process mainly has the following functions:

  1. Identify the identity information of system visitors, and provide it for subsequent business use after passing the authentication.
  2. Provide accessor identity information to the authorization process (when the resource does not specify a specific authentication scheme, use the default authentication scheme to authenticate the user information).
  3. Implement the processing logic after authorization failure, such as 401 (unauthenticated) and 403 (forbidden access) returned after the authorization check fails, are finally processed by the ChallengeAsync method and ForbidAsync method of the authentication scheme. These methods are defined in IAuthenticationHandler. The process is called by the authorization process when the authorization failure is 401/403.

The authorization process mainly has the following functions:

  1. The authorization process is mainly to check whether the current user has access rights to the specified resources. If the authorization check fails, such as 401 (unauthenticated) and 403 (forbidden), then the ChallengeAsync and ForbidAsync methods of the authentication scheme will be called respectively. That is to say , The authorization process focuses on the process control after the authorization fails.
  2. Another main task of the authorization process is to check whether the authorization policies can be verified. If a resource specifies one or more authorization policies through the Policy attribute of AuthorizeAttribute, then all authorization policies must be verified to be considered successful. If not specified Authorization strategy, then verify whether the default authorization strategy can pass the test, the default authorization strategy is to require the user to pass the authentication before allowing access to resources.

The authorization process is essentially traversing all IAuthorizationHandlers injected into the container ( Microsoft injected into the container by default when AddAuthorization: PassThroughAuthorizationHandler, this authorization handler traverses all Requirement classes that implement IAuthorizationHandler in AuthorizationHandlerContext.Requirements, and calls its HandleAsync method to Check whether the current Requirement can pass the verification ), and verify the Requirements contained in all the policies that must be satisfied to access the specified resources. If all the Requirements contained in the policies are verified, then the authorization is successful. The Requirement here refers to the implementation of IAuthorizationRequirement The class, this interface is an empty interface, used to mark the Requirement.

Guess you like

Origin blog.csdn.net/qq_41872328/article/details/126834499