C# cơ bản .NET Core

Bài này tiếp tục thực hành, phát triển trên dự án của ví dụ Album trước: Identity (6) - Role trong Identity

Cấu hình Cookie cho Identity

Thực hiện thêm cấu hình sau vào ConfigureServices, thêm sau AddIdentity

services.ConfigureApplicationCookie (options => {
    // options.Cookie.HttpOnly = true;
    // options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
    options.LoginPath = $"/login/";
    options.LogoutPath = $"/logout/";
    options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
});

Trong đó /login/ là URL để login, thay bằng Url ứng dụng của bạn (Areas/Identity/Pages/Account/Login.cshtml), trang này để ứng dụng chuyển hướng đến nếu truy cập chức năng nào đó cần User phải đăng nhập. /Identity/Account/AccessDenied trang ứng dụng chuyển hướng đến nếu User truy cập chức năng nào đó mà không được phân quyền (không có role được phép truy cập)

Role-based Authorization trong ASP.NET

Role-based Authorization - chứng thực quyền dựa theo Role (vai trò) là cách xác định quyền đơn giản - cơ bản nhất của ASP.NET, cơ bản quyền của User dựa vào Role được gán cho User đó.

Để sử dụng ta dùng đến thuộc tính [Authorize], chỉ thị này áp dụng được cho Controller, Action của Controller, PageModel để xác định ai được quyền truy cập Controller, Action của Controller hay PageModel.

Lưu ý: Với Razor Page - thuộc tính [Authorize] chỉ áp dụng ở mức độ lớp PageModel - không đến mức các Handler. Còn với Controller thì có từ mức cả Controller hay chi tiết đến Action.

[Authorize] đơn giản

Sử dụng [Authorize] có vài tham số, ở trường hợp đơn giản nhất là không có tham số nào. Ở trường hợp này nó cho biết controller, action hay Page Razor chỉ cho các user đã xác thực (đăng ký và đăng nhập) được phép truy cập.

  • Thuộc tính [Authorize] áp dụng cho lớp Controller - thì mọi Action trong Controller ảnh hưởng
  • [Authorize] áp dụng cho Action thì Action bị ảnh hưởng
  • [Authorize] áp dụng cho PageModel thì tất cả các Handler bị ảnh hưởng (không áp dụng được cho từng Handler)

Ví dụ tạo ra một Razor Page như sau: TestAuthorize1

Pages/TestAuthorize1.cshtml

namespace Album.Pages
{
    [Authorize]
    public class TestAuthorize1Model : PageModel
    {
        public void OnGet()
        {
        }
    }
}

Pages/TestAuthorize1.cshtml.cs

@page "/test-authorize-1/"
@inject Microsoft.AspNetCore.Identity.UserManager<Album.Models.AppUser> UserManager
@model Album.Pages.TestAuthorize1Model
<p class="alert alert-danger">@UserManager.GetUserName(User),  Bạn đang truy cập TestAuthorize1</p>

Do khai báo [Authorize]. Truy cập vào /test-authorize-1/ nếu đang đăng nhập thì truy cập được trang

Nếu không đăng nhập thì mọi truy vấn đến các handler của PageModel trên bị không thực hiện được mà nó sẽ tự chuyển về trang login đã cấu hình của Identity

Đối với mô hình MVC khi áp dụng [Authorize] ở tên lớp Controller thì mọi action chịu tác động chứng thực quyền. Đôi khi muốn một Action nào đó bỏ qua kiểm tra quyền thì sử dụng thuộc tính [AllowAnonymous], Ví dụ:

[Authorize] // Mọi action đều kiểm tra user đăng nhập mới thực hiện
public class AccountController : Controller
{
    [AllowAnonymous] // Cho phép User truy cập Action này mà không cần đăng nhập
    public ActionResult Action1()
    {
    }

    public ActionResult Action2()
    {
    }
    /...
}

Hiên nay ASP.NET không có kế hoạch hỗ trợ [Authorize] trên các Handler của Razor Page, trong trường hợp như vậy bạn có thể chuyển sang dùng Controller của MVC hoặc xây dựng một thuộc tính đặt tên AuthorizePageHandler, mã nguồn của nó tại AuthorizeIndexPageHandlerFilter, thì lúc này bạn có thể khai báo chứng thực vào các Handler với [AuthorizePageHandler]

[Authorize] chứng thực theo Role

Bằng cách sử dụng thuộc tính Roles của [Authorize] gán bằng tên các role, thì nó sẽ kiểm tra User phải đăng nhập - user phải có Role liệt kê trong Roles thì mới được truy cập

Ví dụ:

[Authorize(Roles = "Admin")]
public class TestAuthorize1Model : PageModel
{
    / ..
}

Truy cập Page trên User phải được gán Role là Admin

Bạn có thể liệt kê các Role ví dụ: những User có Role Admin và Editor được truy cập

[Authorize(Roles = "Admin,Editor")]

Nếu bạn sử dụng nhiều dòng [Authorize(Roles = "RoleName")] thì User phải được gán tất cả các role đó (thuộc nhóm). Ví dụ User vừa thuộc nhóm Admin vừa thuộc Editor mới được truy cập

[Authorize(Roles = "Admin")]
[Authorize(Roles = "Editor")]
public class TestAuthorize1Model : PageModel
{
}

Trong Controller bạn có thể thiết lập bổ sung [Authorize] cho Action. Ví dụ:

[Authorize(Roles = "Admin, Editor")]
public class MyController : Controller
{
    public ActionResult ActionA()
    {
    }

    [Authorize(Roles = "Admin")]
    public ActionResult ActionnB()
    {
    }
}

User thuộc nhóm Admin, Editor được truy cập Controller MyController, nhưng đến Action ActionB thì chỉ nhóm Admin được truy cập

Nếu một User không có Role chứng thực được truy cập thì nó sẽ chuyển hướng về trang thiết lập trong AccessDeniedPath đã cấu hình trong ConfigureServices của Startup

services.ConfigureApplicationCookie (options => {
    /...
    options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
});

Lưu ý: Khi bạn cập nhật Role cho một User nào đó - nếu User đó đang đăng nhập, chưa hết phiên làm việc thì thông tin mới vẫn chưa tác động vào User. Nếu muốn - thời gian nạp lại thông tin User từ Db ngắn hơn thì cấu hình thêm về SecurityStampValidatorOptions trong ConfigureServices như sau:

Tham khảo code ASP_NET_CORE/Album

services.Configure<SecurityStampValidatorOptions>(options =>
{
    // Trên 30 giây truy cập lại sẽ nạp lại thông tin User (Role)
    // SecurityStamp trong bảng User đổi -> nạp lại thông tinn Security
    options.ValidationInterval = TimeSpan.FromSeconds(30);
});

Đăng ký nhận bài viết mới