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
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 roleMember | 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/
Nhận xét
Đăng nhận xét