Trong bài viết này, mình sẽ hướng dẫn các bạn setup Login, Register và Logout page.
Để dễ dàng tiếp cận, mình sẽ chia ra làm 3 phần:
- Setup Login, Register Model và View
- Setup AspNet Identity
- Implement Login và Register logic
Setup Model và View
Register page
Tạo Register model
public class RegisterModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
public string ConfirmPassword { get; set; }
public string ReturnUrl { get; set; }
}
Register.cshtml
@model ClaimExample.Models.RegisterModel;
<div class="row">
<div class="col-md-4">
<form asp-route-returnUrl="ReturnUrl" method="post">
<h4>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="FirstName"></label>
<input asp-for="FirstName" class="form-control" />
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LastName"></label>
<input asp-for="LastName" class="form-control" />
<span asp-validation-for="LastName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
</div>
</div>
Login page
Tương tự các bạn tạo LoginModel
public class LoginModel
{
public string Email { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
public bool RememberMe { get; set; }
}
Login.cshtml
@model ClaimExample.Models.LoginModel;
<div class="row">
<div class="col-md-4">
<section>
<form id="account" method="post">
<h4>Use a local account to log in.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Log in</button>
</div>
</form>
</section>
</div>
</div>
Setup AspNet Identity
ASP.NET Core Identity là một thành phần (built-in) của ASP.NET Core, nó cung cấp cho bạn các tính năng đầy đủ và đa dạng về authentication. Bạn có thể tạo tài khoản, login, logout, update user profile, quản lý role...
Để sử dụng ASPNET Identity, bạn cần cài đặt các thư viện:
- Microsoft.AspNetCore.Identity
- Microsoft.AspNetCore.Identity.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.AspNetCore.Identity
Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore
Install-package Microsoft.EntityFrameworkCore.SqlServer
Trong appSettings.json, bạn thêm đoạn kết nối xuống Database
{
"ConnectionStrings": {
"DefaultConnection": "Server=.;Initial Catalog=TestDb;Persist Security Info=False;User ID=sa;Password=Nhatkyhoctap@@@123456789;TrustServerCertificate=True;MultipleActiveResultSets=False;Connection Timeout=30;"
}
}
Identity Model
Namespace Microsoft.AspNetCore.Identity chứa Identity Models bao gồm các class sau:
IdentityRole
IdentityRoleClaim
IdentityUser
IdentityUserClaim
IdentityUserLogin
IdentityUserRole
IdentityUserToken
public class IdentityUser<TKey> where TKey : IEquatable<TKey>
{
[PersonalData]
public virtual TKey Id { get; set; }
public virtual string UserName { get; set; }
public virtual string NormalizedUserName { get; set; }
[ProtectedPersonalData]
public virtual string Email { get; set; }
public virtual string NormalizedEmail { get; set; }
[PersonalData]
public virtual bool EmailConfirmed { get; set; }
public virtual string PasswordHash { get; set; }
public virtual string SecurityStamp { get; set; }
public virtual string ConcurrencyStamp { get; set; }
[ProtectedPersonalData]
public virtual string PhoneNumber { get; set; }
[PersonalData]
public virtual bool PhoneNumberConfirmed { get; set; }
[PersonalData]
public virtual bool TwoFactorEnabled { get; set; }
public virtual DateTimeOffset? LockoutEnd { get; set; }
public virtual bool LockoutEnabled { get; set; }
public virtual int AccessFailedCount { get; set; }
}
Bạn có thể override lại bằng cách tạo class ApplicationUser và thêm thuộc tính mới
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Khai báo Database Context
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole, string>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
Đăng ký DbContext trong Startup
ASP.NET Core sử dụng framework Dependency Injection. Bạn có thể dễ dàng đăng ký trước khi webapp khởi động trong file Program.cs
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
Trường hợp bạn chứa DbContext khác với Project bạn dự định sử dụng Migration (giả sử Project Name của bạn là Project A, bạn sử dụng đoạn code sau:
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString, option => option.MigrationsAssembly("ProjectA")));
Migration
Mở Package Manager ConsoleAdd-Migration <Migration name>=> tạo ra tập tin Migration mới trong thư mục Migrations
- Configuration.cs: Đây là lớp dùng để cấu hình Migration. Bạn không cần chỉnh sửa file này.
- Tập tin <timestamp>_InitialCreate.cs: Đây là tập tin Migration đầu tiên của chương trình.
Update-Database
add-migration InitialSchema
update-database
Migration Tables Diagram |
Entity | Table Name | Remarks |
---|---|---|
IdentityUser | AspNetUsers | Primary table to store user information |
IdentityUserClaim | AspNetUserClaims | Tables holds the claims associated with the user. |
IdentityUserLogin | AspNetUserLogins | Table holds the information about 3rd party/external logins |
IdentityUserToken | AspNetUserTokens | is for storing tokens received from the external login providers. |
IdentityUserRole | AspNetUserRoles | Table contains the roles assigned to the user |
IdentityRole | AspNetRoles | Tables to store the roles |
IdentityRoleClaim | AspNetRoleClaims | The claims that are assigned to the Role |
Đăng ký Identity Services
Chúng ta cần đăng ký Identity Services trong Startup của Application.var builder = WebApplication.CreateBuilder(args);
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(
options => {
options.SignIn.RequireConfirmedAccount = false;
options.Password.RequiredLength = 4;
options.Password.RequireDigit = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
//Other options go here
}
)
.AddEntityFrameworkStores<ApplicationDbContext>();
Phương thức AddIdentity đăng ký sử dụng User (ApplicationUser) và Roles (IdentityRole). Ngoài ra phương thức này cũng đăng ký các service khác như UserValidator, PasswordValidator, PasswordHasher, UserManager, Cookie-based authentication schemes. SignInManager, ...
Bạn có thể gọi các service đó thông qua Depedency Injection
Authentication Middleware
Authentication Middleware đọc authentication cookies từ request, xây dựng ClaimsPrincipal và cập nhật đối tượng User trong HttpContext. Mọi thứ được cập nhật sau method UseAuthentication()
Chúng ta phải gọi UseAuthentication
- Sau UseRouting, để thông tin routing có sẵn cho các quyết định xác thực.
- Trước UseEndpoints & UseAuthorization, để người dùng được xác thực trước khi truy cập các điểm cuối.
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Implement Login và Register logic
Quay lại HomeController, chúng ta thêm đoạn xử lý Login và Register:private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public HomeController(ILogger<HomeController> logger, UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
{
_logger = logger;
_userManager = userManager;
_signInManager = signInManager;
}
[HttpPost]
public async Task<IActionResult> Login(LoginModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var findUser = await _userManager.FindByEmailAsync(model.Email);
if (findUser != null)
{
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
return RedirectToAction("Secret");
}
if (result.IsLockedOut)
{
return RedirectToAction("Index");
}
if (result.IsNotAllowed)
{
return RedirectToAction("Index");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
return View();
}
public async Task<IActionResult> Register()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Register(RegisterModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, FirstName = model.FirstName, LastName = model.LastName };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction("Secret");
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
return View();
}
Thêm hàm Signout:
public async Task<IActionResult> LogOut()
{
await _signInManager.SignOutAsync();
//Redirect to home page
return RedirectToAction("Index");
}
Source code: https://github.com/anbinhtrong/AuthenticationAndAuthorization/releases/tag/claims_declaration_v2.0
Hi vọng qua bài viết này, các bạn sẽ xây dựng nhanh chóng trang Login và Register.
Nhatkyhoctap's blog
Test email by Papercut: https://github.com/ChangemakerStudios/Papercut-SMTP
Trả lờiXóa