Chuyển đến nội dung chính

ASP.NET Identity: Làm quen với Role trong ASP.NET Core - Part 3

Giới thiệu

Trong bài viết trước, chúng ta tìm hiểu về Authentication (setup, login, register). Authentication là quá trình xác thực danh tính của người dùng để đảm bảo họ có quyền truy cập vào tài nguyên hoặc chức năng trong một hệ thống hoặc ứng dụng nào đó.

Nếu hệ thống chỉ dùng Authentication, user có thể truy cập toàn bộ tài nguyên trong ứng dụng. Để giới hạn quyền sử dụng, chúng ta sẽ cung cấp khả năng access vào hệ thống dựa trên Role/Policy của từng user.

Trong bài viết này, mình chỉ trình bày phần Role.Bài viết này sẽ chia làm 2 phần

  • Seeding default roles
  • Role based Authorization

IdentityRole

Trong ASP.NET Core, IdentityRole là một class được cung cấp bởi namespace Microsoft.AspNetCore.Identity, đại diện cho một role trong hệ thống identity. IdentityRole thường được sử dụng trong hệ thống ASP.NET Core Identity, cung cấp phương thức quản lý user và role trong hệ thống.

Seeding default roles

Mặc định, ASP.NET Identity Migration chỉ tạo database bao gồm các table (AspNetUsers, AspNetRoles, …) nhưng không có bao gồm data. Để tạo role nhanh chóng, chúng ta viết hàm seeding.

Vấn đề khó khăn tiếp theo là làm sao giải quyết được Dependency Injection. Chúng ta quay về xem lại IServiceProvider. IServiceProvider là một Built-in Container đơn giản được cung cấp sẵn trong ASP.NET Core. Nó hỗ trợ Constructor Injection mặc định, dùng để giải quyết instance cho các kiểu dữ liệu lúc runtime.

Có 2 cách để giải quyết DI: method injection và sử dụng trực tiếp IServiceProvider

Method Injection:

public HomeController(ILogger<HomeController> logger, UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
{
	_logger = logger;
	_userManager = userManager;
	_signInManager = signInManager;
}
Hoặc
[HttpPost("Log")]
public IActionResult Log([FromServices] IFileLogger fileLogger)
{
   //Write your code here
    return Ok();
}
Sử dụng IServiceProvider
public class HomeController : Controller
{
    private IServiceProvider _provider;
    public HomeController(IServiceProvider provider)
    {
        _provider = provider;
    }
	
	public IActionResult Index()
	{
		var logger = (IFileLogger)_provider.GetService(typeof(IFileLogger));
		return View();
	}
}
Bạn có thể sử dụng RequestServices của HttpContext
var logger = (IFileLogger)HttpContext.RequestServices.GetService(typeof(IFileLogger));
Trở lại ví dụ, chúng ta sẽ gọi hàm SeedData ngay sau khi app được build.
var app = builder.Build();
//add custom data
await SeedData(app);
//…
static async Task SeedData(WebApplication app)
{
    using (var serviceScope = app.Services.GetRequiredService<IServiceScopeFactory>().CreateScope())
    {
        var context = serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
        var userManager = serviceScope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
        var roleManager = serviceScope.ServiceProvider.GetService<RoleManager<IdentityRole>>();
        if (userManager == null || roleManager == null) return;
        var hasRole = roleManager.Roles.Any();
        if (!hasRole)
        {
            await ContextSeed.SeedRolesAsync(userManager, roleManager);
        }
        var hasUser = userManager.Users.Any();
        if (!hasUser)
        {
            await ContextSeed.SeedSuperAdminAsync(userManager, roleManager);
        }
    } 
}
Tạo class SeedData
public static class ContextSeed
{
	public static async Task SeedRolesAsync(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
	{
		//Seed Roles
		foreach (var role in (Roles[])Enum.GetValues(typeof(Roles)))
		{
			await roleManager.CreateAsync(new IdentityRole(role.ToString()));
		}
	}

	public static async Task SeedSuperAdminAsync(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
	{
		//Seed Default User
		var defaultUser = new ApplicationUser
		{
			UserName = "superadmin",
			Email = "superadmin@nhatkyhoctap.com",
			FirstName = "Seta",
			LastName = "Soujiro",
			EmailConfirmed = true,
			PhoneNumberConfirmed = true
		};
		if (userManager.Users.All(u => u.UserName != defaultUser.UserName)
		{
			await userManager.CreateAsync(defaultUser, "123Pa$$word.");
			foreach (var role in (Roles[])Enum.GetValues(typeof(Roles)))
			{
				await userManager.AddToRoleAsync(defaultUser, role.ToString());
			}
		}
	}
}
Sau đó bạn định nghĩa enum Roles:
public enum Roles
{
	Admin,
	Moderator,
	Member,
	Analyst,
	Support
}
Như vậy, mỗi lần khi bạn run application, hệ thống sẽ check data. Nếu chưa có role, hệ thống sẽ tạo role. Chưa có user, hệ thống sẽ tạo user.

Role based Authorization

Identity cung cấp các service RoleManager<IdentityRole> dùng để quản lý, thêm xóa sửa role
Member Mô tả

Roles

Thuộc tính kiểu IQueryable<IdentityRole> - để truy vấn lấy các IdentityRole, ví dụ lấy danh sách các IdentityRole

List<IdentityRole> roles  =  await _roleManager.Roles.ToListAsync();

CreateAsync

Tạo mới IdentityRole (chèn vào Database)

await _roleManager.CreateAsync(identityRole);

DeleteAsync

Xóa IdentityRole

await _roleManager.DeleteAsync(identityRole);

RoleExistsAsync

Kiểm tra sự tồn tại của một IdentityRole theo tên của nó

await _roleManager.RoleExistsAsync(roleName);

FindByIdAsync

Lấy Role theo ID của nó

FindByNameAsync

Lấy Role theo tên của nó

Đầu tiên, chúng ta sẽ tạo trang AccessDenied. Khi một anonymous user, hoặc một authenticated user mà không có permission vào 1 page nào đó, hệ thống sẽ redirect sang trang AccessDenied.


Ở file startup.cs, bạn thêm config AccessDeniedPath

builder.Services.ConfigureApplicationCookie(config =>
{
    config.Cookie.Name = "NgocTho";
    config.LoginPath = "/Home/Login";
    config.AccessDeniedPath = "/Home/UserAccessDenied";
});
Ở HomeController, thêm action UserAccessDenied()
[HttpGet]
public ActionResult UserAccessDenied()
{
	return View();
}
Thêm đoạn code sau vào View tương ứng
@model List<Microsoft.AspNetCore.Identity.IdentityRole>
@{
    ViewData["Title"] = "Role Manager";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Role Manager</h1>
<form method="post" asp-action="AddRole" asp-controller="RoleManager">
    <div class="input-group">
        <input name="roleName" class="form-control w-25">
        <span class="input-group-btn">
            <button class="btn btn-info">Add New Role</button>
        </span>
    </div>
</form>
<table class="table table-striped">
    <thead>
        <tr>
            <th>Id</th>
            <th>Role</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var role in Model)
        {
            <tr>
                <td>@role.Id</td>
                <td>@role.Name</td>
            </tr>
        }
    </tbody>
</table>
Quay lại class RoleManager, bạn thêm attribute Authorize
[Authorize(Roles = "Admin")]
public class RoleManagerController : Controller
{

}

Sau đó bạn thử đăng nhập với user với role Admin và user với role khác Admin, xem chuyện gì sẽ xảy ra. 

Download source code: https://github.com/anbinhtrong/AuthenticationAndAuthorization/releases/tag/role_identity_v3.0

Tham khảo

https://www.infoworld.com/article/3640368/how-to-resolve-dependencies-in-aspnet-core.html

https://codewithmukesh.com/blog/user-management-in-aspnet-core-mvc/

https://xuanthulab.net/asp-net-razor-xay-dung-chuc-nang-quan-ly-role-gan-role-cho-user-trong-asp-net.html

Music Store - Sample Data

Nhận xét

Bài đăng phổ biến từ blog này

[ASP.NET MVC] Authentication và Authorize

Một trong những vấn đề bảo mật cơ bản nhất là đảm bảo những người dùng hợp lệ truy cập vào hệ thống. ASP.NET đưa ra 2 khái niệm: Authentication và Authorize Authentication xác nhận bạn là ai. Ví dụ: Bạn có thể đăng nhập vào hệ thống bằng username và password hoặc bằng ssh. Authorization xác nhận những gì bạn có thể làm. Ví dụ: Bạn được phép truy cập vào website, đăng thông tin lên diễn đàn nhưng bạn không được phép truy cập vào trang mod và admin.

ASP.NET MVC: Cơ bản về Validation

Validation (chứng thực) là một tính năng quan trọng trong ASP.NET MVC và được phát triển trong một thời gian dài. Validation vắng mặt trong phiên bản đầu tiên của asp.net mvc và thật khó để tích hợp 1 framework validation của một bên thứ 3 vì không có khả năng mở rộng. ASP.NET MVC2 đã hỗ trợ framework validation do Microsoft phát triển, tên là Data Annotations. Và trong phiên bản 3, framework validation đã hỗ trợ tốt hơn việc xác thực phía máy khách, và đây là một xu hướng của việc phát triển ứng dụng web ngày nay.

Tổng hợp một số kiến thức lập trình về Amibroker

Giới thiệu về Amibroker Amibroker theo developer Tomasz Janeczko được xây dựng dựa trên ngôn ngữ C. Vì vậy bộ code Amibroker Formula Language sử dụng có syntax khá tương đồng với C, ví dụ như câu lệnh #include để import hay cách gói các object, hàm trong các block {} và kết thúc câu lệnh bằng dấu “;”. AFL trong Amibroker là ngôn ngữ xử lý mảng (an array processing language). Nó hoạt động dựa trên các mảng (các dòng/vector) số liệu, khá giống với cách hoạt động của spreadsheet trên excel.