Talking about ASP.NET The flow principle of JWT authorization and authentication in core

Time:2020-9-10

1. Quickly realize authorization verification

What is JWT? Why use JWT? Composition of JWT?

These Baidu can be found directly, here no longer repeat.

In fact, we only need to know that JWT authentication mode is a means to use a token as the basis of authentication.

Let’s take a look at where postman sets the token.

So, how to use C ා httpclient to access a JWT certified web API?

Let’s create one ASP.NET Core project, try to add JWT verification function.

1.1 add JWT service configuration

In Startup.cs OfConfigureServicesMethod, add a service

//Set the verification mode to bear token
   //You can also add using Microsoft.AspNetCore.Authentication .JwtBearer;
   //Use JwtBearerDefaults.AuthenticationScheme  Instead of the string "brewer"
   services.AddAuthentication("Bearer")
    .AddJwtBearer(options =>
    {
     options.TokenValidationParameters = new TokenValidationParameters
     {
      ValidateIssuerSigningKey = true,
      IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8 . GetBytes ("abcdabcd1234abcdabcd1234")), // encrypt and decrypt the key of the token

      //Verify publisher
      ValidateIssuer = true,
      //Publisher name
      ValidIssuer = "server", 

      //Verify subscriber
      //Subscriber name
      ValidateAudience = true,
      ValidAudience = "client007",

      //Verify token validity
      ValidateLifetime = true,
      //Each time a token is issued, the token is valid
      ClockSkew = TimeSpan.FromMinutes(120)
     };
    });

modifyConfigureMiddleware in

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication (); // note here
app.UseAuthorization();

This is so simple. Through the above settings, it is required to verify whether the request has permission.

1.2 issue token

Issued token, ASP.NET The core will not be saved.

ASP.NET The core has enabled token authentication. You can put the generated token code into the console of different programs. As long as the key is consistent with the issuer and audience, the generated token can log in to this ASP.NET Core。

In other words, you can create a console program at will to generate a token, and the generated token can be logged in ASP.NET Core program.

As for the reason, we will talk about it later,

In Program.cs Add a method like this

static void ConsoleToke()
  {

   //Define user information
   var claims = new Claim[]
   {
    new Claim( ClaimTypes.Name "The fool works well"),
    new Claim(JwtRegisteredClaimNames.Email, "[email protected]"),
   };

   //It is consistent with the configuration in startup
   SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("abcdABCD1234abcdABCD1234"));

   JwtSecurityToken token = new JwtSecurityToken(
    issuer: "server",
    audience: "client007",
    claims: claims,
    notBefore: DateTime.Now,
    expires: DateTime.Now.AddMinutes(30),
    signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
   );

   string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
   Console.WriteLine(jwtToken);
  }

Main()Call this method


 public static void Main(string[] args)
  {
   ConsoleToke();
   CreateHostBuilder(args).Build().Run();
  }

1.3 add API access

We add an API.

[Authorize]Attribute is used to identify this controller or action. A compliant token is required to log in.

[Authorize]
 [Route("api/[controller]")]
 [ApiController]
 public class HomeController : ControllerBase
 {
  public string Get()
  {
   Console.WriteLine(User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Name));
   Return "visit succeeded";
  }
 }

Then it starts ASP.NET Core, test access in postman https://localhost/api/home 。

401 (no permission) status code is found. This is because the request does not carry a token, which will lead to the inability to access the API.

Copy the generated token code from the console terminal and copy it to postman. After visiting again, it is found that the response status code is 200 and the response is successful.

ASP.NET The core comes with JWT certification, which is about this.

So, ASP.NET How is the core implemented internally? What are the characteristics and pitfalls? Please look down~

2. Explore authorization and authentication middleware

In the above operation, we configured two Middleware in the pipeline.


app.UseAuthentication();
app.UseAuthorization();

app.UseAuthentication();The role of ASP.NET For the authorization authentication configured in the core, read the identity ID (cookie, token, etc.) in the client, parse it out, and store it in thecontext.UserMedium.

app.UseAuthorization();Is used to determine the current accessEndpointIs (controller or action) used[Authorize]And configure roles or policies, and then verify whether the cookie or token is valid.

Use the feature settings to access through authentication. Generally, there are the following situations.

//Not applicable features, which can be accessed directly
 public class AController : ControllerBase
 {
  public string Get() { return "666"; }
 }

 /// <summary>
 ///The entire controller needs authorization to access
 /// </summary>
 [Authorize]
 public class BController : ControllerBase
 {
  public string Get() { return "666"; }
 }

 public class CController : ControllerBase
 {
  //Only get needs authorization
  [Authorize]
  public string Get() { return "666"; }
  public string GetB() { return "666"; }
 }

 /// <summary>
 ///Authorization is required for the entire controller, but not for get
 /// </summary>
 [Authorize]
 public class DController : ControllerBase
 {
  [AllowAnonymous]
  public string Get() { return "666"; }
 }

2.1 implement token parsing

as for ASP.NET In the core,app.UseAuthentication();andapp.UseAuthorization();A variety of source code using a project to write, more code. To understand the role of these two middleware, we might as well implement their functions manually.

The resolved token is a claim principal object, which is given tocontext.UserAssignment, which can be used in the APIUserInstance to get user information.

In the middleware, the following code can be used to obtain the token resolution of the client request.


context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, JwtBearerDefaults.AuthenticationScheme);

So, how can we manually parse out native HTTP requests? Let’s see how I break down the steps.

First, create a testmiddleware file to be used as middleware.

public class TestMiddleware
 {
  private readonly RequestDelegate _next;
  jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
  public TestMiddleware(RequestDelegate next)
  {
   _next = next;
  }
  public async Task Invoke(HttpContext context)
  {
   if (context == null)
   {
    throw new ArgumentNullException(nameof(context));
   }

   //The area where we write code


   //The area where we write code
   await _next(context);
  }
 }

2.1.1 get token from http

The following code can get the token of the header in the HTTP request.

Of course, the client may not carry a token. It may get a null result and add a judgment by itself.

Paste to the code area.


 string tokenStr = context.Request.Headers["Authorization"].ToString();

The authorization key of the header is set by theBreaer {Token}A string composed of.

2.1.2 judge whether it is a valid token

After you get the token, you need to judge whether the token is valid.

Because authorization is made byBreaer {Token}Composition, so we need to get rid of the frontBrearTo get token.

/// <summary>
  ///Is token a standard JSON web token that meets the requirements
  /// </summary>
  /// <param name="tokenStr"></param>
  /// <returns></returns>
  public bool IsCanReadToken(ref string tokenStr)
  {
   if (string.IsNullOrWhiteSpace(tokenStr) || tokenStr.Length < 7)
    return false;
   if (!tokenStr.Substring(0, 6).Equals(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme))
    return false;
   tokenStr = tokenStr.Substring(7);
   bool isCan = jwtSecurityTokenHandler.CanReadToken(tokenStr);

   return isCan;
  }

After obtaining the token, theJwtSecurityTokenHandler.CanReadToken(tokenStr);To determine whether the token conforms to the protocol specification.

Paste the following judgment to the code area.


if (!IsCanReadToken(ref tokenStr))
    return ;

2.1.3 parsing token

The following code can turn the authorization content of the header into a jwtsecuritytoken object.

(there are many ways to intercept strings, which one you like… )

/// <summary>
  ///Decrypt jwtsecuritytoken from token, jwtsecuritytoken: securitytoken
  /// </summary>
  /// <param name="tokenStr"></param>
  /// <returns></returns>
  public JwtSecurityToken GetJwtSecurityToken(string tokenStr)
  {
   var jwt = jwtSecurityTokenHandler.ReadJwtToken(tokenStr);
   return jwt;
  }

But this oneGetJwtSecurityTokenIt’s not what we focus on, we’re going to get claims.

JwtSecurityToken.Claims

Paste the following code to the code area


JwtSecurityToken jst = GetJwtSecurityToken(tokenStr);
IEnumerable<Claim> claims = jst.Claims;

2.1.4 generation context.User

context.User It is a claim principal type. We generate claims principal through the resolved claim.


JwtSecurityToken jst = GetJwtSecurityToken(tokenStr);
IEnumerable<Claim> claims = jst.Claims;

List<ClaimsIdentity> ci = new List<ClaimsIdentity>() { new ClaimsIdentity(claims) };
context.User = new ClaimsPrincipal(ci);

The final code block looks like this

//The area where we write code
   string tokenStr = context.Request.Headers["Authorization"].ToString();
   string requestUrl = context.Request.Path.Value;
   if (!IsCanReadToken(ref tokenStr))
    return;
   JwtSecurityToken jst = GetJwtSecurityToken(tokenStr);
   IEnumerable<Claim> claims = jst.Claims;
   List<ClaimsIdentity> ci = new List<ClaimsIdentity>() { new ClaimsIdentity(claims) };

   context.User = new ClaimsPrincipal(ci);
   var x = new ClaimsPrincipal(ci);
   //The area where we write code

2.2 verification and authentication

app.UseAuthentication();The general implementation process has been explained. Now let’s continue to implementapp.UseAuthorization();Functions in.

Continue to use the middleware above to add new areas to the original code block area.

//The area where we write code

//The code blocks we write

 22.2.1 Endpoint

Endpoint identifies the route information and controller, action and its characteristics information accessed by an HTTP request.

[Authorize]Feature inheritsIAuthorizeData[AllowAnonymous]Feature inheritsIAllowAnonymous

The following code can get the information of the node accessed.


var endpoint = context.GetEndpoint();

How to judge whether the controller and action accessed use authentication related features?


 var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();

Metadata is a ASP.NET The collection object implemented by the core,GetOrderedMetadata<T>You can find the required feature information.

This collection does not distinguish between contrller and action[Authorize]characteristic.

Then judge whether there is[AllowAnonymous]Properties can be used in this way.


if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)
   {
    await _next(context);
    return;
   }

Here is a brief introduction to this article ASP.NET The article on the process principle of JWT authorization and authentication in core is introduced here, more relevant ASP.NET For core JWT authentication content, please search the previous articles of developeppaer or continue to browse the related articles below. I hope you can support developeppaer more in the future!