This is the 21st day of my participation in the August Text Challenge.More challenges in August

Developed an in-house system using asp.net core 3.1. In the development of user authentication and authorization using a simple cookie authentication, and then developed to write several interfaces to other system call data. Instead of redeploying a site with just a few simple interfaces, we added an API area to the MVC project to write interfaces. At this time, because it is an interface, cookie authentication cannot be used. Instead, JWT authentication must be added and multiple authentication schemes adopted for authentication and authorization.

Certificate authority

Authentication is the process of determining the identity of a user. Authorization is the process of determining whether a user has access to a resource. In ASP.NET Core, authentication is handled by the IAuthenticationService, which is used by the authentication middleware. The authentication service uses a registered authentication handler to perform authentication-related operations.

Authentication --> AuthorizationCopy the code

Regarding authentication and authorization, we need to distinguish between authentication and authorization. For details, please check the MSDN official documentation and search for other articles. OAuth 2.0 and JWT have a lot of information and are well explained.

The identity authentication

Authentication scheme by Startup. ConfigureServices registry authentication service specified: Is the call services. After AddAuthentication call plan specific extension method (such as AddJwtBearer or AddCookie). These extension methods using AuthenticationBuilder. AddScheme registered scheme with the appropriate Settings.

  • Add cookie JwtBearer validation scheme
public void ConfigureServices(IServiceCollection services)
{
    services.AddSession();
    services.AddMvc(o =>
    {
        o.Filters.Add(typeof(MyExceptionFilterAttribute));// Global exception Filter
    }).AddRazorRuntimeCompilation();
    // Add an authentication scheme
    var jwtConfig= Configuration.GetSection("Jwt").Get<JwtConfig>();
    services.AddAuthentication
        (authoption =>{
            // Specify the default options
            authoption.DefaultChallengeScheme= CookieAuthenticationDefaults.AuthenticationScheme;
            authoption.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            authoption.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            authoption.DefaultSignInScheme= CookieAuthenticationDefaults.AuthenticationScheme;
        })
   .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, option =>
   {
       option.Cookie.Name = "adCookie";// Set the Cookie name that stores user login information (user Token information)
       option.Cookie.HttpOnly = true;// Set the Cookie that stores the user login information (user Token information), which cannot be accessed by the client browser script (such as JavaScript)
       option.ExpireTimeSpan = TimeSpan.FromDays(3);// Expiration time
       option.SlidingExpiration = true;// Whether to automatically extend when more than half of the time is expired
       option.LoginPath = "/Account/Login";
       option.LogoutPath = "/Account/LoginOut";
   })
   .AddJwtBearer(option =>
   {
       option.TokenValidationParameters = new TokenValidationParameters
       {
           ValidIssuer = jwtConfig.Issuer,
           ValidAudience = jwtConfig.Audience,
           ValidateIssuer = true,
           ValidateLifetime = jwtConfig.ValidateLifetime,
           IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SigningKey)),
           Buffer expiration time, the total valid time is equal to this time plus JWT expiration time
           ClockSkew = TimeSpan.FromSeconds(0)}; }); }Copy the code
  • JwtBearer authenticated configuration parameter classesJwtConfig
public class JwtConfig
{
    /// <summary>
    ///Who issued the
    /// </summary>
    public string Issuer { get; set; }

    /// <summary>
    ///Who issued to
    /// </summary>
    public string Audience { get; set; }

    /// <summary>
    ///Token password
    /// a secret that needs to be at least 16 characters long
    /// </summary>
    public string SigningKey { get; set; }

    /// <summary>
    ///Expiration Time (minutes)
    /// </summary>
    public int Expires { get; set; }

    /// <summary>
    ///Whether to verify the expiration time
    /// </summary>
    public bool ValidateLifetime { get; set; }}Copy the code
  • Appsettings. json configuration parameter
  "Jwt": {
    "Issuer": "issuer"."Audience": "audience"."SigningKey": "c0d32c63-z43d-4917-bbc2-5e726d087452".// Expiration time (minutes)
    "Expires": 10080.// Whether to verify the expiration time
    "ValidateLifetime": true
  }
Copy the code
  • Add authentication middleware

Add authentication middleware to startup. Configure by calling the UseAuthentication extension method on your application’s IApplicationBuilder. If UseAuthentication is called, an intermediate section of the previously registered authentication scheme is registered. Call UseAuthentication before any middleware that depends on the user to authenticate. If endpoint routing is used, UseAuthentication must be called in the following order:

● Called after UseRouting so that routing information can be used for authentication decisions. ● Called before UseEndpoints so that users can access endpoints only after they are authenticated.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseSession();
    app.UseRouting();

    // Enable authentication middleware
    app.UseAuthentication();
    // Enable authorized middleware
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {

        endpoints.MapControllerRoute(
        name: "areas",
        pattern: "{area:exists}/{controller=Home}/{action=Index}/{id? }");

        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id? }");
    });
}
Copy the code
  • Cookie authentication
[HttpPost]
public async Task<NewtonsoftJsonResult> LoginIn(string userName, string userPassword, string code)
{
    AjaxResult objAjaxResult = new AjaxResult();
    var user = _userBll.GetUser(userName, userPassword);
    if (user == null)
    {
        objAjaxResult.Result = DoResult.NoAuthorization;
        objAjaxResult.PromptMsg = "Wrong username or password";
    }
    else
    {
        var claims = new List<Claim>
        {   
            new Claim("userName", userName),
            new Claim("userID",user.Id.ToString()),
        };
        await HttpContext.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity(claims,CookieAuthenticationDefaults.AuthenticationScheme)));
        objAjaxResult.Result = DoResult.Success;
        objAjaxResult.PromptMsg = "Login successful";
    }
    return new NewtonsoftJsonResult(objAjaxResult);
}
Copy the code
  • JWT certification
[HttpPost]
public NewtonsoftJsonResult Token([FromBody] UserInfo model)
{
    AjaxResult objAjaxResult = new AjaxResult();
    var user = _userBll.GetUser(model.UserName, model.Password);
    if (user == null)
    {
        objAjaxResult.Result = DoResult.NoAuthorization;
        objAjaxResult.PromptMsg = "Wrong username or password";
    }
    else
    {
        //jwtTokenOptions is used to obtain the parameter information configured above
        var jwtTokenOptions = BaseConfigModel.jwtConfig;
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtTokenOptions.SigningKey));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        / / identity
        var claims = new List<Claim>
            {
                new Claim("userID",user.Id.ToString()),
                new Claim("userName",user.UserName),
            };
        / / token
        var expires = DateTime.Now.AddMinutes(jwtTokenOptions.Expires);
        var token = new JwtSecurityToken(
            issuer: jwtTokenOptions.Issuer,
            audience: jwtTokenOptions.Audience,
            claims: claims,
            notBefore: DateTime.Now,
            expires: expires,
            signingCredentials: credentials
            );
        string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
        objAjaxResult.Result = DoResult.Success;
        objAjaxResult.RetValue = new
        {
            token = jwtToken
        };
        objAjaxResult.PromptMsg = "Login successful";
    }
    return new NewtonsoftJsonResult(objAjaxResult);
}
Copy the code

authorization

At authorization time, the application indicates the handler to be used. Select the handlers that the application passes the coming authorization through a comma-separated list of authentication schemes. [Authorize] The [Authorize] attribute specifies the authentication scheme or scheme to use, regardless of whether a default is configured.

  • The default authorization

Because cookie is used as the default configuration in the above authentication configuration, the controller corresponding to the front end does not need to specify the authentication scheme and directly marks [Authorize].

  • Select authorized

For the API we use Jwt authorization to specify the scheme on the Controller.

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Copy the code

conclusion

The process for authenticating and authorizing multiple authentication schemes is as follows: 1. Configure an authentication scheme (related parameters can be in a configuration file). 2. Add authorization verification middleware. 3. Provide authentication interfaces. 4. Configure the interface authorization scheme to be authorized.