ASP.NET Core: Giới thiệu về Identity (Nhận dạng) trên ASP.NET Core


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

ASP.NET Core Identity:

  • Là một API hỗ trợ chức năng đăng nhập giao diện người dùng (UI).
  • Quản lý người dùng, mật khẩu, dữ liệu hồ sơ, vai trò, xác nhận quyền sở hữu, mã thông báo, xác nhận email, v.v.

Người dùng có thể tạo tài khoản với thông tin đăng nhập được lưu trữ trong Danh tính (Identity) hoặc họ có thể sử dụng nhà cung cấp thông tin đăng nhập bên ngoài. Các nhà cung cấp đăng nhập bên ngoài được hỗ trợ bao gồm Facebook, Google, Tài khoản Microsoft và Twitter.

Để biết thông tin về cách yêu cầu tất cả người dùng phải được chứng thực trên toàn cầu, hãy xem Yêu cầu người dùng được chứng thực.

Mã nguồn Identity có sẵn trên GitHub. Scaffold Identity  và xem các tệp được tạo để xem lại sự tương tác của mẫu với Identity.

Danh tính thường được định cấu hình bằng cơ sở dữ liệu SQL Server để lưu trữ tên người dùng, mật khẩu và dữ liệu hồ sơ. Ngoài ra, có thể sử dụng một kho lưu trữ liên tục khác, ví dụ: Azure Table Storage.

Trong bài viết này, bạn tìm hiểu cách sử dụng Danh tính để đăng ký, đăng nhập và đăng xuất người dùng. Lưu ý: các mẫu coi tên người dùng và email giống nhau đối với người dùng. Để biết hướng dẫn chi tiết hơn về cách tạo ứng dụng sử dụng Danh tính, hãy xem Các bước tiếp theo.

ASP.NET Core Identity không liên quan đến nền tảng nhận dạng của Microsoft. Nền tảng nhận dạng của Microsoft là:

  • Sự phát triển của nền tảng nhà phát triển Azure Active Directory (Azure AD).
  • Một giải pháp nhận dạng thay thế để chứng thực và ủy quyền trong ứng dụng ASP.NET Core.

ASP.NET Core Identity bổ sung chức năng đăng nhập giao diện người dùng (UI) cho các ứng dụng web ASP.NET Core. Để bảo mật Web API và SPA, hãy sử dụng một trong các cách sau:

Máy chủ nhận dạng Duende là một frame OpenID Connect và OAuth 2.0 cho ASP.NET Core. Máy chủ nhận dạng Duende kích hoạt các tính năng bảo mật sau:

  • Chứng thực dưới dạng dịch vụ (AaaS)
  • Đăng nhập một lần/tắt (SSO) trên nhiều loại ứng dụng
  • Kiểm soát quyền truy cập cho API
  • Cổng Federation

Quan trọng

Phần mềm Duende có thể yêu cầu bạn trả phí giấy phép để sử dụng Máy chủ Nhận dạng Duende trong môi trường production. Để biết thêm thông tin, hãy xem Migration từ ASP.NET Core 5.0 sang 6.0.

Để biết thêm thông tin, hãy xem tài liệu về Máy chủ nhận dạng Duende (trang web Phần mềm Duende).

Xem hoặc tải xuống mã mẫu (cách tải xuống).

Tạo một ứng dụng web có chứng thực

Tạo dự án ASP.NET Core Web Application với Tài khoản người dùng cá nhân.

  • Chọn mẫu ASP.NET Core Web App. Đặt tên cho dự án là WebApp1 để có cùng namespace với bản tải xuống dự án. Bấm vào OK.
  • Trong mục nhập Authentication type, chọn Individual User Accounts.

Dự án được tạo cung cấp ASP.NET Core Identity dưới dạng Thư viện lớp Razor. Thư viện lớp Razor nhận dạng hiển thị các điểm cuối cùng với vùng Identity. Ví dụ:

  • /Identity/Account/Login
  • /Identity/Account/Logout
  • /Identity/Account/Manage

Áp dụng migration

Áp dụng các migration để khởi tạo cơ sở dữ liệu.

Chạy lệnh sau trong Package Manager Console (PMC):

Update-Database

Kiểm tra Đăng ký và đăng nhập

Chạy ứng dụng và đăng ký người dùng. Tùy thuộc vào kích thước màn hình của bạn, bạn có thể cần chọn nút chuyển đổi điều hướng để xem các liên kết RegisterLogin.

Xem cơ sở dữ liệu Nhận dạng

  • Từ menu View, chọn SQL Server Object Explorer (SSOX).
  • Điều hướng đến (localdb)MSSQLLocalDB(SQL Server 13). Nhấp chuột phải vào dbo.AspNetUsers > View Data:

Menu ngữ cảnh trên bảng AspNetUsers trong SQL Server Object Explorer

Định cấu hình dịch vụ Nhận dạng

Các dịch vụ được thêm vào Program.cs. Mẫu điển hình là gọi các phương thức theo thứ tự sau:

  1. Add{Service}
  2. builder.Services.Configure{Service}
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.Configure<IdentityOptions>(options =>
{
    // Password settings.
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 1;

    // Lockout settings.
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.AllowedForNewUsers = true;

    // User settings.
    options.User.AllowedUserNameCharacters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
    options.User.RequireUniqueEmail = false;
});

builder.Services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

    options.LoginPath = "/Identity/Account/Login";
    options.AccessDeniedPath = "/Identity/Account/AccessDenied";
    options.SlidingExpiration = true;
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

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

app.MapRazorPages();

app.Run();

Đoạn code trên định cấu hình Danh tính với các giá trị tùy chọn mặc định. Các dịch vụ được cung cấp cho ứng dụng thông qua tính năng Dependency Injection.

Danh tính được kích hoạt bằng cách gọi UseAuthenticationUseAuthentication thêm middleware chứng thực vào đường dẫn yêu cầu.

Ứng dụng tạo mẫu không sử dụng ủy quyềnapp.UseAuthorization được đưa vào để đảm bảo nó được thêm vào theo đúng thứ tự nếu ứng dụng thêm ủy quyền. UseRoutingUseAuthentication, và UseAuthorization phải được gọi theo thứ tự hiển thị trong đoạn mã trước.

Để biết thêm thông tin về IdentityOptions, hãy xem IdentityOptions và Khởi động ứng dụng.

Scaffold Register, Login, Logout và RegisterConfirmation

Thêm các file RegisterLoginLogOut và RegisterConfirmation. Thực hiện Scaffold Nhận dạng vào dự án Razor vớ ủy quyền để hướng dẫn tạo mã được hiển thị trong phần này.

Kiểm tra Đăng ký (Register)

Khi người dùng nhấp vào nút Register trên trang Register, thì action RegisterModel.OnPostAsync sẽ được thực hiện. Khi này, người dùng sẽ được tạo bởi CreateAsync(TUser) trên đối tượng _userManager:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Tắt xác minh tài khoản mặc định

Với các mẫu mặc định, người dùng sẽ được chuyển hướng đến Account.RegisterConfirmation nơi họ có thể chọn liên kết để xác nhận tài khoản. Mặc định thì Account.RegisterConfirmation chỉ được sử dụng để thử nghiệm, xác minh tài khoản tự động sẽ bị tắt trong ứng dụng production.

Để yêu cầu một tài khoản được xác nhận và ngăn chặn việc đăng nhập ngay lập tức khi đăng ký, hãy đặt DisplayConfirmAccountLink = false vào  /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs:

[AllowAnonymous]
public class RegisterConfirmationModel : PageModel
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IEmailSender _sender;

    public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
    {
        _userManager = userManager;
        _sender = sender;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return NotFound($"Unable to load user with email '{email}'.");
        }

        Email = email;
        // Once you add a real email sender, you should remove this code that lets you confirm the account
        DisplayConfirmAccountLink = false;
        if (DisplayConfirmAccountLink)
        {
            var userId = await _userManager.GetUserIdAsync(user);
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            EmailConfirmationUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);
        }

        return Page();
    }
}

Log in (Đăng nhập)

Form Đăng nhập được hiển thị khi:

  • Liên kết Đăng nhập được chọn.
  • Người dùng cố gắng truy cập một trang bị hạn chế mà họ không được phép truy cập hoặc khi họ chưa được hệ thống chứng thực.

Khi biểu mẫu trên trang Đăng nhập được gửi, action OnPostAsync sẽ được gọi. PasswordSignInAsync được gọi trên đối tượng _signInManager.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

Để biết thông tin về cách đưa ra quyết định ủy quyền, hãy xem Giới thiệu về ủy quyền trong ASP.NET Core.

Log out (Đăng xuất)

Liên kết Đăng xuất sẽ gọi action LogoutModel.OnPost.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

Trong đoạn mã trên, lệnh return RedirectToPage(); cần phải chuyển hướng để trình duyệt thực hiện yêu cầu mới và danh tính của người dùng được cập nhật.

SignOutAsync sẽ xóa các xác nhận quyền sở hữu của người dùng được lưu trữ trong cookie.

Post được chỉ định trong Pages/Shared/_LoginPartial.cshtml:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

Kiểm tra Identity (Danh tính)

Các mẫu dự án web mặc định cho phép truy cập ẩn danh vào trang chủ. Để kiểm tra Danh tính, hãy thêm [Authorize]:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Nếu bạn đã đăng nhập, hãy đăng xuất. Chạy ứng dụng và chọn liên kết Privacy. Bạn được chuyển hướng đến trang đăng nhập.

Khám phá Danh tính

Để khám phá Danh tính chi tiết hơn:

Thành phần Danh tính

Tất cả các gói NuGet phụ thuộc vào Danh tính đều được bao gồm trong framework chia sẻ ASP.NET Core.

Gói chính cho Identity là Microsoft.AspNetCore.Identity. Gói này chứa bộ giao diện cốt lõi cho ASP.NET Core Identity và được bao gồm bởi Microsoft.AspNetCore.Identity.EntityFrameworkCore.

Di chuyển sang ASP.NET Core Identity

Để biết thêm thông tin và hướng dẫn về cách di chuyển kho danh tính hiện có của bạn, hãy xem Migrate chứng thực và danh tính.

Đặt độ mạnh mật khẩu

Xem Cấu hình để biết mẫu đặt các yêu cầu mật khẩu tối thiểu.

AddDefaultIdentity và AddIdentity

AddDefaultIdentity được giới thiệu trong ASP.NET Core 2.1. Cách gọi AddDefaultIdentity tương tự như cách gọi sau:

Xem nguồn AddDefaultIdentity để biết thêm thông tin.

Ngăn chặn xuất bản nội dung Nhận dạng tĩnh

Để ngăn xuất bản nội dung Nhận dạng tĩnh (bảng định kiểu và tệp JavaScript cho Identity UI) lên web root, hãy thêm property ResolveStaticWebAssetsInputsDependsOn và mục tiêu (target) RemoveIdentityAssets sau vào tệp dự án của ứng dụng:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target> 

Các bước tiếp theo

Nguồn: learn.microsoft.com
» Tiếp: Triển khai ứng dụng web ASP.NET
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!