Location>code7788 >text

NET Core Multi-Identity Authentication with Policy Patterns

Popularity:618 ℃/2024-08-30 15:06:58


Contextual needs:

  The system needs to be connected to the official API of XXX, but therefore the official docking and management are very strict. My department's system contains many sub-systems, in order to stabilize the system, most of the program between the fixed Token + special verification to call, and later to be provided to other brother departments to call the system together.

  In principle: each system must be individually accessed to the official, but the official access to the complexity of the official, but also the official designation of certificates certified by the agency and other conditions, the cost of this approach is large.

so:

  In order to solve the problem of docking XXX official API, we built a set of relay system, as the name suggests: it is a set of relay system for request relay. When building the system, Leader proposed to do more than one set of authentication programs, which must do the followingcombine motion and static (idiom); fig. the dynamics of a situationIdentity authentication.

  Movers and Shakers:Dynamic Token and Static Fixed Token.

    Dynamic Token: Token used for sister department system or external access to this relay system application for later calling the corresponding API.

    Fixed Token: Used for many subsystems in the current department to provide a super Token, which is valid for a long time and will not be changed at will.

Into the Pit:

  Because I just took over the project in my first week here. The project is in the application account stage and is about to go into development. The docking is full English documentation (application/docking process/development API ....) The documentation was complex. At that time my feeling: OMG, this shall not run? The whole project can be described as difficult. Then because of the internal business is also not familiar with, on the hands of the micro-services and other related system code, note: between each set of systems documentation is pathetic, it can be said thatSystem Undocumented Status

  When the project was handed over, the Leader said that I should familiarize myself with the project and gradually enter the development, and asked me to consult my colleagues. Well, I asked my coworkers. Colleagues also took over the former departure of the document only, we are not very familiar with. So my colleague let me start a new project is also a direct interface to the form of microservices development, an operation as fierce as a tiger.

  The second week of project development, has played the framework model and docking part of the API. at this time, the Leader meeting to ask about the progress, the results come to a sentence: this project uses independent API way to run, deployed to the Docker, do not access the company's microservice architecture. Well, a few days of effort in vain, really take the dregs and remove the essence ~, back to WebAPI.

Technology Realization:

  Because before the authentication and forensics this piece did not do too much in-depth understanding of the Leader schedule is also in the ass chase, in a word: how fast how to come, first on the iteration. Well, for the convenience of the project, at the same time, in order to comply with theCombined dynamic and static authentication forensics .So, I used theJWT+Custom AuthenticationDemand is realized.

Scheme 1: Multi-authentication + middleware mode realization

Add service: JWT is used by default

 //Multiple Authentication
//Default useJWT,in the event thatControllerutilization AuthenticationSchemes then the specified authentication is used
(options =>
{
    <CustomAuthenticationHandler>(, );
     = ;
     = ;
})
.AddJwtBearer(options =>
{
     = false;//Set whether metadata addresses or permissions are requiredHTTPs
     = true;
     = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = ["Jwt:Issuer"],
        ValidAudience = ["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.(["Jwt:SecretKey"]!))
    };
     = new CustomJwtBearerEvents();
});

Custom Authentication Code

    public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        public const string AuthenticationSchemeName = "CustomAuthenticationHandler";
        private readonly IConfiguration _configuration;
        public CustomAuthenticationHandler(
            IOptionsMonitor<AuthenticationSchemeOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock,
            IConfiguration configuration)
            : base(options, logger, encoder, clock)
        {
            _configuration = configuration;
        }
        /// <summary>
        /// set rigidly in placeTokenaccreditation
        /// </summary>
        /// <returns></returns>
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            string isAnonymous = ["IsAnonymous"].ToString();
            if (!(isAnonymous))
            {
                bool isAuthenticated = (isAnonymous);
                if (isAuthenticated)
                    return ();
            }

            string authorization = ["Authorization"].ToString();
            // "Bearer " --> BearerFollowed by a space.
            string token = ("Bearer ") ? (0, "Bearer ".Length) : authorization;
            if ((token))
                return ("request headerAuthorizationNot allowed to be empty。");

            //By key,encrypt、declassificationtreat (sb a certain way)比accreditation
            if (!VerifyAuthorization(token))
                return ("incomingAuthorizationAuthentication failure。");


            return (GetTicket());
        }
        private AuthenticationTicket GetTicket()
        {
            // Verification Success,Creating Authentication Tickets
            var claims = new[]
            {
                new Claim(, "Admin"),
                new Claim(, "Public"),
            };
            var identity = new ClaimsIdentity(claims, );
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), );
            return ticket;
        }
        private bool VerifyAuthorization(string token)
        {
            //token: [0]randomly generated64bit string,[1]Load data,[2]adoptionHashtreat (sb a certain way)[0]+[1]autographs
            var tokenArr = ('.');
            if ( != 3)
            {
                return false;
            }
            try
            {
                //1、先比treat (sb a certain way)签名串是否一致
                string signature = tokenArr[1].Hmacsha256HashEncrypt().ToLower();
                if (!(tokenArr[2].ToLower()))
                {
                    return false;
                }

                //declassification
                var aecStr = tokenArr[1].Base64ToString();
                var clientId = ();
                //2、再验证Load data的有效性
                var clientList = _configuration.GetSection("FixedClient").Get<List<FixedClientSet>>();
                var clientData = (it => (clientId));
                if (clientData == null)
                {
                    return false;
                }
            }
            catch (Exception)
            {
                throw;
            }

            return true;
        }
    }

Using Middleware: UseMiddleware

();
//Middleware mode: custom authentication middleware: dual authentication choose one
//If you are using a policy, you need to comment out the middleware.
<FallbackAuthenticationMiddleware>(); //Use the middleware implementation
().

Middleware Code Implementation

   public class FallbackAuthenticationMiddleware
  {
      private readonly RequestDelegate _next;
      private readonly IAuthenticationSchemeProvider _schemeProvider;

      public FallbackAuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemeProvider)
      {
          _next = next;
          _schemeProvider = schemeProvider;
      }
      /// <summary>
      /// Authentication program
      /// default (setting)JWT。JWTfail (e.g. experiments),Perform customized authentication
      /// </summary>
      /// <param name="context"></param>
      /// <returns></returns>
      public async Task InvokeAsync(HttpContext context)
      {
          var endpoints = ();
          if (endpoints == null || !<IAuthorizeData>().Any() || <IAllowAnonymous>().Any())
          {
              await _next(context);
              return;
          }

          //default (setting)JWT。JWTfail (e.g. experiments),Perform customized authentication
          var result = await Authenticate_JwtAsync(context);
          if (!)
              result = await Authenticate_CustomTokenAsync(context);

          // Set up authentication tickets toHttpContextcenter
          if ()
               = ;

          await _next(context);
      }
      /// <summary>
      /// JWTapprovals
      /// </summary>
      /// <param name="context"></param>
      /// <returns></returns>
      private async Task<dynamic> Authenticate_JwtAsync(HttpContext context)
      {
          var verify = ?.Identity?.IsAuthenticated ?? false;
          string authenticationType = ;
          if (verify && authenticationType != null)
          {
              return new { Succeeded = verify, Principal = , Message = "" };
          }

          await ;

          // can't findJWTAuthentication program,or inaccessible handler。
          return new { Succeeded = false, Principal = new ClaimsPrincipal { }, Message = "JWT authentication scheme not found or handler could not be obtained." };
      }

      /// <summary>
      /// Customized Authentication
      /// </summary>
      /// <param name="context"></param>
      /// <returns></returns>
      private async Task<dynamic> Authenticate_CustomTokenAsync(HttpContext context)
      {
          // Customized Authentication方案的名称
          var customScheme = "CustomAuthenticationHandler";

          var fixedTokenHandler = await <IAuthenticationHandlerProvider>().GetHandlerAsync(context, customScheme);
          if (fixedTokenHandler != null)
          {
              var Res = await ();
              return new { , , ?.Message };
          }

          //can't findCustomAuthenticationHandlerAuthentication program,or inaccessible handler。
          return new { Succeeded = false, Principal = new ClaimsPrincipal { }, Message = "CustomAuthenticationHandler authentication scheme not found or handler could not be obtained." };

      }
  }

Option 2: AuthenticationSchemes via the [Authorize] tag
Because the middleware has to maintain an additional piece of middleware code, which seems slightly more complicated, it is done by [Authorize(AuthenticationSchemes = "")].

     //Use of specific authentication
    //[Authorize(AuthenticationSchemes = )]
    //Either Identity
    [Authorize(AuthenticationSchemes = $"{},{}")]
    public class DataProcessingController : ControllerBase
    {
    }

Option 2: Policy via the [Authorize] tag

  If there are other authentication, that constantly increasing AuthenticationSchemes spliced in the Controller's header, it does not look good, and if more than one Controller to use, it will also lead to maintenance trouble, so instead of using the policy approach.

  AddAuthorization in the Add Service. the benefit of using policies is increased maintainability.

 //authorization strategy
//Controllerutilization policy then the specified policy configuration is used for authentication
(option =>
{
    (CustomPolicy.Policy_A, policy => policy
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes(, )
            );

    (CustomPolicy.Policy_B, policy => policy
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes()
            );

    (CustomPolicy.Policy_C, policy => policy
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes()
            );
});
     //Using policy-specific authentication
    [Authorize(policy:CustomPolicy.Policy_B)]
    public class DataProcessingController : ControllerBase
    {
    }
     /// <summary>
    /// strategy class
    /// </summary>
    public static class CustomPolicy
    {
        public const string Policy_A= "Policy_A";

        public const string Policy_B = "Policy_B";

        public const string Policy_C = "Policy_C";
    }

Screenshots are attached at the end:

Add service:

Use middleware:

Controller:

In this way, the complete trunking system perfectly meets the Leader's needs and achieves the desired results.

Source Code Demo:/LaoPaoE/
Attached at the end:

AuthorizeAttribute is used in conjunction withPolicy and AuthenticationSchemesand RolesHow is the process of forensics when:

  1. AuthenticationSchemes authentication:
    • AuthenticationSchemes attribute specifies the authentication scheme (e.g., Cookies, Bearer Tokens, etc.) used to verify the user's identity.
    • Core authenticates the user against these authentication schemes. If the user fails to authenticate (i.e., is not logged in or does not provide valid authentication information), the request is rejected and may be redirected to the login page.
  2. Roles authentication (if Roles is specified):
    • If AuthorizeAttribute is also specified in theRoles attribute, then in addition to being authenticated, the user must belong to one of these roles.
    • Core checks the user's role information to determine if the user belongs to theRoles  One or more roles specified in the attribute.
  3. Policy authentication (if Policy is specified):
    • Policy attribute specifies one or more authorization policies that define additional conditions that a user must meet to access the resource.
    • Core calls the appropriate IAuthorizationHandler to evaluate whether the user meets all the requirements in the policy. These requirements can be defined based on roles, claims, resources, and so on.
    • If the user does not meet any of the requirements in the policy, the authorization fails and an HTTP 403 Forbidden response is returned.

Authentication sequences and combinations

  • Typically, AuthenticationSchemes authentication is performed first, as it is a prerequisite for accessing any protected resource.
  • If AuthenticationSchemes authentication passes, further authentication is done next based on whether Roles and Policies are specified.
  • The order of authentication for Roles and Policies may vary depending on the specific version and configuration of Core, but in general they are evaluated as independent conditions.
  • The user must satisfy all of the conditions in AuthenticationSchemes, Roles (if specified), and Policies (if specified) at the same time to successfully access the protected resource.

caveat

  • In some cases, even though both AuthenticationSchemes and Roles authentication passes, the user will still not be able to access the resource if the requirements in the Policy are not met.
  • The IAuthorizationRequirement and IAuthorizationHandler can be customized with the IAuthorizationRequirement and IAuthorizationHandler.to implement complex authorization logic to meet specific business needs.
  • Ensure that AuthenticationSchemes, Roles, and Policies are properly set up in the application's authentication and authorization configuration so that they can work together to provide effective access control.