Overview

Identity authentication is the most basic function of the website. Recently, due to a requirement of the business department, we need to transform a small tool website that has existed for a long time. As we are gradually migrating some discrete systems to.NET Core, we take this opportunity to upgrade the old.NET Framework 4.0 project

The old project was an MVC project with the requirement of external network access. Because the micro-service platform of large departments was closely related to internal business execution, it was isolated from the external network due to the capital and security requirements. Therefore, this upgrade will not be migrated to this platform for the separation of front and back ends

Frequency of usage is not high, there is no high concurrency, short implementation cycle, so there is no need to use some components and use, so here or to continue to use the MVC framework, for web authentication, the use of the monomer used the most common Cookie authentication, this article is how to implement a basic tutorial, only for reference

Step by Step

When it comes to system permission management, two similar words, authentication and authorization, are bound to be mentioned.

  • Authentication: Use some data to prove that you are you. Login, fingerprint, face unlock is one of the authentication processes
  • Authorization: Authorizes some users to access special resources or functions. The system includes administrators and common users. Only administrators can perform certain operations

Only authentication and authorization work together can the entire system be controlled

preparation

For an MVC or Web API application, the user must log in to access it. The simplest way to access it is to create an ASP.NET Core MVC application. Add Authorize to the Controller or Action that requires authentication, and then add middleware with UseAuthorization in the startup. Configure method

[Authorize]
public class HomeController : Controller
{
    public IActionResult Index()
    {
        returnView(); }}Copy the code
public class Startup
{
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                "default"."{controller=Home}/{action=Index}/{id? }"); }); }}Copy the code

Of course, when the system only contains one or two controllers, it is ok. When the system is more complex, it is more troublesome to add the Authorize features one by one. . So here we can use in the Startup ConfigureServices, global AuthorizeFilter filter is added to the implementation of global authentication controls

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews()
            .AddMvcOptions(options => { options.Filters.Add(newAuthorizeFilter()); }); }}Copy the code

In this case, for some pages that do not require authentication to access, simply add AllowAnonymous

public class AuthenticationController : Controller{[AllowAnonymous]
    public IActionResult Login()
    {
        returnView(); }}Copy the code

Configuring an Authentication Policy

Of course, if only this change, in fact, there is a problem, you can see that when the global filter is added, the system can not be accessed normally

In terms of authorization, it actually takes place after authentication passes, that is to say, we lack the configuration of system authentication. According to the error message, We first need to define the system’s authentication policy by using the AddAuthentication method

AddAuthentication method in Microsoft. AspNetCore. Authentication library, through the search can be found in the Nuget,. NET Core has general specification based on the industry to realize the multiple Authentication strategy

Because the Cookie authentication used here is already included in the default project template, there is no need to reference it

.net-based Core standard service process, first of all, we need to Startup. ConfigureServices defined through AddAuthentication in approach to the entire system USES an authorization strategy, as well as, Based on the way we use Cookie authorization and the current Internet defense requirements against cross-site request forgery (CSRF) attacks, we need to set some cookies for the website

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Define an authorization policy
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(options =>
            {
                // Unauthorized page path
                options.AccessDeniedPath = new PathString("/permission/forbidden");

                // Login path
                options.LoginPath = new PathString("/authentication/login");

                // Log out path
                options.LogoutPath = new PathString("/authentication/logout");

                // Cookie expiration time (20 minutes)
                options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
            });

        // Configure the Cookie policy
        services.Configure<CookiePolicyOptions>(options =>
        {
            // The default user agrees to unnecessary cookies
            options.CheckConsentNeeded = context => true;

            // Define a SameSite policy that allows Cookies to be sent along with top-level navigationoptions.MinimumSameSitePolicy = SameSiteMode.Lax; }); }}Copy the code

As shown in the code, in defining the authorization policy, we defined three redirect page, go tell the Cookie authorization policy where the corresponding page here, at the same time, because the authentication Cookie default expiration time will last until close the browser, that is to say, as long as users do not click the exit button and do not close the browser, The user will always be logged in, so we set an expiration time of 20 minutes to avoid unnecessary risks

Add UseAuthentication middleware to HTTP pipeline in startup. Configure method to enable website authentication. Note that authentication is performed before authorization. Therefore, the order of adding middleware can not be reversed

public class Startup
{
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();

        // Add authentication and authorization (the order cannot be reversed)
        //
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                "default"."{controller=Home}/{action=Index}/{id? }"); }); }}Copy the code

At this point, when we access the system again, we are automatically redirected to the system login page because we are not authenticated, and the redirected page is the value of the LoginPath property configured in the code above

Login and logout implementation

After the authentication policy is configured, you can implement the login function based on the selected policy. The button on the login page here simulates a login form submission. When clicked, the authentication logic of the system will be triggered. The implementation code is shown below. Don’t forget to add the AllowAnonymous feature to the Action of the login event to AllowAnonymous access

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LoginAsync()
{
    // 1. Todo: check whether the account and password are correct and obtain the required user information

    // 2. Create user declaration
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.Name, "Zhang"),
        new Claim(ClaimTypes.MobilePhone, "13912345678")};// 3. Create a declaration id
    var claimIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

    // 4, create a declaration id card holder
    var claimPrincipal = new ClaimsPrincipal(claimIdentity);

    // 5
    await HttpContext.SignInAsync(claimPrincipal);

    return Redirect("/");
}
Copy the code

In the whole code, there are three main objects, Claim, ClaimsIdentity and ClaimsPrincipal. Through the use of these three objects, the user information required by the system after the user successfully logs in can be included in Cookie

The difference between the three objects is explained in this English blog post that I have to read to understand the ASP.NET Core validation model (Claim, ClaimsIdentity, ClaimsPrincipal)

  • Claim: a statement of the characteristics of the authenticated subject, for example: the login user name is… Email is… The user Id is… Where “login user name”, “email”, and “user Id” are ClaimType
  • ClaimsIdentity: An identity with these claims is a ClaimsIdentity. A driver’s license is a ClaimsIdentity. A driver’s license is a kind of document, and so is a passport
  • ClaimsPrincipal: The owner of a ClaimsIdentity is ClaimsPrincipal. A ClaimsPrincipal can hold multiple ClaimSidEntities, such as a person holding a driver’s license and a passport

Finally, you can log in by calling the httpContext. SignInAsync method. As you can see, when the Cookie is cleared, the user is logged out. We can also log out manually by calling httpContext. SignOutAsync

Obtaining User information

The information added to the Claim can be retrieved by specifying the ClaimType. In View and Controller, we can retrieve the information directly by specifying the ClaimType. The User used here is actually the ClaimsPrincipal mentioned above

varuserName = User.FindFirst(ClaimTypes.Name)? .Value;Copy the code

When we need to obtain stored user information in a separate class library, we need to do the following operations

The first step in the Startup. ConfigureServices method to inject HttpContextAccessor service

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        / / into the HttpContextservices.AddHttpContextAccessor(); }}Copy the code

The second step, in you need to use the class library by Microsoft Nuget reference. AspNetCore. Http, after can be in a specific class by injecting IHttpContextAccessor to obtain user information, of course, can also be realized in the login and logout methods

namespace Sample.Infrastructure
{
    public interface ICurrentUser
    {
        string UserName { get; }

        Task SignInAsync(ClaimsPrincipal principal);

        Task SignOutAsync();

        Task SignOutAsync(string scheme);
    }

    public class CurrentUser : ICurrentUser
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

        private HttpContext HttpContext => _httpContextAccessor.HttpContext;

        public CurrentUser(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
        }

        public stringUserName => HttpContext.User.FindFirst(ClaimTypes.Name)? .Value;public Task SignInAsync(ClaimsPrincipal principal) => HttpContext.SignInAsync(principal);

        public Task SignOutAsync() => HttpContext.SignOutAsync();

        public Task SignOutAsync(string scheme)=> HttpContext.SignOutAsync(scheme); }}Copy the code

So far, the whole block of authentication function has been realized, I hope to help you

Reference

  1. SameSite cookies
  2. Work with SameSite cookies in ASP.NET Core
  3. What does the CookieAuthenticationOptions. LogoutPath property do in ASP.NET 2.1 Core?
  4. Understand the English blog posts that the ASP.NET Core validation model (Claim, ClaimsIdentity, ClaimsPrincipal) has to read
  5. Introduction to Authentication with ASP.NET Core

Signature

Author: Mo Mo Mo Mo Xiaoyu

Biography: Born in 1996, born in a fourth-tier city in Anhui province, graduated from Top 10 million universities. .NET programmer, gunslinger, cat. It will begin in December 2016. NET programmer career, Microsoft. NET technology stalwart, aspired to be the cloud cat kid programming for Google the best. NET programmers

Personal blog: Yuiter.com

Garden blog blog: www.cnblogs.com/danvic712