C# Cơ bản .NET Core §1 Cài đặt, chương trình C# đầu tiên §2 Biến, kiểu dữ liệu và nhập/xuất §3 Toán tử số học và gán §4 So sánh, logic và lệnh if, switch §5 Vòng lặp for, while §6 Phương thức - Method §7 Phương thức - Delegate §8 Lớp - Class §9 Namespace §10 Partial, Nested §11 Kiểu giá trị, tham chiếu §12 Kiểu vô danh và dynamic §13 Biểu thức lambda §14 Event §15 Hàm hủy - Quá tải toán tử - thành viên tĩnh - indexer §16 null và nullable §17 Mảng §18 Chuỗi ký tự §19 Tính kế thừa §20 Phương thức khởi tạo §21 Tính đa hình - abstract - interface §22 Struct và Enum §23 Ngoại lệ Exeption §24 IDisposable - using §25 File cơ bản §26 FileStream §27 Generic §28 Collection - List §29 SortedList §30 Queue / Stack §31 Linkedlist §32 Dictionary - HashSet §33 Phương thức mở rộng §34 ObservableCollection §35 LINQ §36 (Multithreading) async - bất đồng bộ §37 Type §38 Attribute Annotation §39 DI Dependency Injection §40 (Multithreading) Parallel §41 Thư viện lớp §42 (Networking) HttpClient §43 (Networking) HttpMessageHandler §44 (Networking) HttpListener §45 (Networking) Tcp TcpListenerr/TcpClient §46 (ADO.NET) SqlConnection §47 (ADO.NET) SqlCommand §48 (EF Core) Tổng quan §49 (EF Core) Tạo Model §50 (EF Core) Fluent API §51 (EF Core) Query §52 (EF Core) Scaffold §53 (EF Core) Migration §54 (ASP.NET CORE) Hello World! §55 (ASP.NET CORE) Middleware §56 (ASP.NET CORE) Map - Request - Response §57 (ASP.NET CORE) IServiceCollection - MapWhen §58 (ASP.NET CORE) Session - ISession §59 (ASP.NET CORE) Configuration §60 (ASP.NET CORE) Gửi Mail §61 (ASP.NET CORE) SASS/SCSS §62 (ASP.NET CORE) LibMan §63 (ASP.NET RAZOR) Khởi tạo và Route §64 (ASP.NET RAZOR) Cú pháp Razor §65 (ASP.NET RAZOR) Layout trong ASP.NET Core §66 (ASP.NET RAZOR) Partial §67 (ASP.NET RAZOR) ViewComponent §68 (ASP.NET RAZOR) TagHelper §69 (ASP.NET RAZOR) PageModel §70 (ASP.NET RAZOR) Model Binding §71 (ASP.NET RAZOR) HTML Form, Validation §72 (ASP.NET RAZOR) Upload File §73 (ASP.NET RAZOR) HtmlHelper §74 (ASP.NET RAZOR) Entity Framework §75 (ASP.NET RAZOR) Paging §76 (ASP.NET RAZOR) Identity (1) - Register, Login, Logout §77 (ASP.NET RAZOR) Identity (2) Lockout, Reset Password §78 (ASP.NET RAZOR) Identity (3) Google Login §79 (ASP.NET RAZOR) Identity (4) Facebook Login §80 (ASP.NET RAZOR) Identity (5) profile, password, email ... §81 (ASP.NET RAZOR) Identity (6) Role §82 (ASP.NET RAZOR) Identity (7) Role-based Authorization §83 (ASP.NET RAZOR) Identity (8) RoleClaim §84 (ASP.NET RAZOR) Identity (9) Authorization Handler §85 (ASP.NET RAZOR) IAuthorizationService §86 (ASP.NET MVC) Controller - View §87 (ASP.NET MVC) Route §88 (ASP.NET MVC) EF, Identity §89 (ASP.NET MVC) Binding, Validation §90 (ASP.NET MVC) Xây dựng Website(1) §91 (ASP.NET MVC) Xây dựng Website(2) §92 (ASP.NET MVC) Xây dựng Website(3) §93 (ASP.NET MVC) Xây dựng Website(4) §94 (ASP.NET MVC) Giỏ hàng - Cart (5) §95 (ASP.NET MVC) elFinder (5) §96 (ASP.NET MVC) SB Admin (6)

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: Chứng thực quyền của User theo RoleClaim

Authorization handler

Khi tạo ra các chính sách policy dùng trong chứng thực quyền ở các bài trước đã sử dụng RequireRole, RequireClaim ... Thực chất là nó tạo ra các yêu cầu (Requirement) sau đó một lớp thi hành để kiểm tra xem User có đảm bảm yêu cầu đó không (lớp này gọi là AuthorizationHandler)

Trong các trường hợp cần xác thực quyền phức tạp bạn có nhu cầu tạo ra yêu cầu riêng, sau đó xây dựng lớp để kiểm tra yêu cầu đó (handler). Quy trình thực hiện đó là:

  • Xây dựng một lớp chứa thông tin về các yêu cầu cần đáp ứng - ví dụ MyRequirement. Lớp này kế thừa từ giao diện IAuthorizationRequirement (giao diện này không bắt phải triển khai phương thức nào - nó chỉ là tên để hệ thống chứng thực nhận biết đây là yêu cầu)
  • Xây dựng một handler (lớp) triển khai từ abstract AuthorizationHandler<MyRequirement> ví dụ đặt tên là MyHandler, trong đó nạp chồng phương thức HandleRequirementAsync(AuthorizationHandlerContext context, MinimumAgeRequirement requirement), trong phương thức này sẽ sử lý thông tin từ user và yêu cầu tương ứng (requirement) - nếu có đánh dấu thành công (context.Succeed (requirement)) thì User được cấp quyền
  • Handler phải đăng ký vào dịch vụ hệ thống Startup.ConfigureServices
    // Handler tạo mới trên mỗi Request - phù hợp để Inject các dịch vụ
    // mà phạm vi scope trên từng yêu cầu
    services.AddTransient<IAuthorizationHandler, MyHandler>();
    
    // Nếu handler không inject các dịch vụ phạm vi yêu cầu (request) thì có thể dùng
    // services.AddSingleton<IAuthorizationHandler, MyHandler>();
    
  • Muốn tạo policy theo yêu cầu tự xây dựng thì tạo như sau:
    services.AddAuthorization(options =>
    {
        options.AddPolicy("Tên Policy", policy => {
            policy.Requirements.Add(Đối tượng lớp MyRequirement);
        });
        //...
    }
    

    Bằng cách như vậy khi sử dụng thuộc tính Authorize["Tên Policy"] để xác thực, nó biết cần đảm bảo yêu cầu MyRequire, hệ thống sẽ nhận biết việc xử lý MyRequire thuộc về dịch vụ MyHandler - yêu cầu sẽ chuyển về cho MyHandler xử lý để biết có đáp ứng yêu cầu hay không

Ví dụ tạo IAuthorizationRequirement, Authorization handler

Trong ví dụ này, thực hiện lại một hướng dẫn tại Document của Identity. Tạo ra yêu cầu và handler xử lý yêu cầu đó - yêu cầu này đảm bảo User truy cập phải có tuổi hiện tại lớn hơn một giá trị tối thiểu quy định, thứ hai là giới hạn chỉ cho phép truy cập trong khoảng thời gian (từ giờ mở OpenTime đến giờ đóng CloseTime)

Vây ta sẽ tạo ra lớp yêu cầu đặt tên là MinimumAgeRequirement chứa các yêu cầu trên:

public class MinimumAgeRequirement : IAuthorizationRequirement
{
        public int MinimumAge { get; }

        public int CloseTime {set; get;}
        public int OpenTime {set; get;}

        public MinimumAgeRequirement(int minimumAge)
        {
            MinimumAge = minimumAge;
        }

}

Tiếp theo xây dựng handler để xử lý yêu cầu, trong ví dụ này (xem phần trước) bảng User ta có bổ sung lưu thông tin ngày sinh của User ở trường Birthday (chứ chưa áp dụng lưu ở UserClaim) xem - thêm trường dữ liệu bảng User. Vây ta cần đọc được trường này, để đọc có thể đọc từ DbContext hoặc dùng trực tiếp UserManager.

Đơn giản, sẽ dùng dịch vụ UserManager - nên khi xây dựng handler ta sẽ Inject dịch vụ này vào handler. Xây dựng handler đặt tên là MinimumAgeHandler

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement> {

    // UserManager được Inject qua khởi tạo
    private readonly UserManager<AppUser> _userManager;
    // Có Inject Logger để ghi log
    private readonly ILogger<MinimumAgeHandler> _logger;

    public MinimumAgeHandler (
        ILogger<MinimumAgeHandler> logger,
        AppDbContext appDbContext,
        UserManager<AppUser> userManager) {
        _userManager = userManager;
        _logger = logger;
    }
    protected override Task HandleRequirementAsync (AuthorizationHandlerContext context,
                                                    MinimumAgeRequirement requirement) {
         

        var user = _userManager.GetUserAsync (context.User).Result;
        if (user == null)
            return Task.CompletedTask;

        var dateOfBirth = user.Birthday;
        if (dateOfBirth == null) {
            _logger.LogInformation ("Không có ngày sinh");
            // Trả về mà chưa chứng thực thành công
            return Task.CompletedTask;
        }

        int calculatedAge = DateTime.Today.Year - dateOfBirth.Value.Year;
        if (dateOfBirth > DateTime.Today.AddYears (-calculatedAge)) {
            calculatedAge--;
        }

        if (calculatedAge < requirement.MinimumAge) {
            _logger.LogInformation (calculatedAge + ": Không đủ tuổi truy cập");
            return Task.CompletedTask;
        }

        // https://stackoverflow.com/a/12998855/4776710
        TimeSpan start = new TimeSpan (requirement.OpenTime, 0, 0);
        TimeSpan end = new TimeSpan (requirement.CloseTime, 0, 0);
        TimeSpan now = DateTime.Now.TimeOfDay;
        // see if start comes before end
        if (start < end)
            if (!(start <= now && now <= end)) {
                _logger.LogInformation (now.ToString() + " Không trong khung giờ được truy cập");
                return Task.CompletedTask;
            }
        // start is after end, so do the inverse comparison
        if (end < now && now < start) {
            _logger.LogInformation (now.ToString() + " Không trong khung giờ được truy cập");
            return Task.CompletedTask;
        }

        // Thiết lập chứng thực thành công
        context.Succeed (requirement);
        return Task.CompletedTask;
    }
}

Như vậy ta đã có Handler, tiếp theo đăng ký nó vào dịch vụ hệ thống, tại Startup.ConfigureServices

services.AddTransient<IAuthorizationHandler, MinimumAgeHandler>();

Thử tạo ra một policy như sau:

services.AddAuthorization(options =>
{
    options.AddPolicy("MinimumAge", policy => {
        policy.Requirements.Add(new MinimumAgeRequirement(18) {
            OpenTime = 8,
            CloseTime = 22
        });
    });
    //...
}

Đã tạo ra policy có tên MinimumAge: User phải đảm bảo yêu cầu sau mới được truy cập - tuổi từ 18 trở lên, truy cập từ 8h đến 22h hàng ngày.

Lúc này trong code, nếu bạn dùng

[Authorize(Policy="MinimumAge")]
public class TestAuthorize1Model : PageModel
{
}

Thì những User nào đáp ứng yêu cầu về tuổi, thời điểm truy cập thì được truy cập trang TestAuthorize1, nếu không có sẽ bị cấm

Tham khảo code ASP_NET_CORE/Album

Đăng ký theo dõi ủng hộ kênh