ASP.NET Identity là 1 công nghệ ra đời sau ASP.NET Membership vốn đã khá quen thuộc với các tín đồ của ASP.NET trong bài toán xác thực và phân quyền người dùng trên website. ASP.NET Identity khắc phục một số yếu điểm của ASP.NET Membership và bổ sung thêm nhiều tính năng hấp dẫn để bắt kịp xu thế như hỗ trợ đăng nhập qua mạng xã hội, đăng nhập thông qua 2 bước, gửi mail xác nhận khi đăng ký ... Series hướng dẫn này sẽ giúp bạn làm việc với công nghệ này trong nền tảng ASP.NET MVC 5, giúp bạn không chỉ làm việc thuần thục với nó mà còn hiểu rõ cơ chế hoạt động của nó. Mình sẽ cố gắng hoàn thành chuỗi Series này sớm nhất có thể. Trong quá trình viết bài mình không tránh được sai sót. Mọi thắc mắc, góp ý các bạn comment dưới bài viết hộ mình nhé.
Nội dung chính
- Tạo project sử dụng ASP.NET Identity
- Giới thiệu về cấu trúc tổ chức thư mục của ASP.NET Identity
- Chức năng đăng ký thành viên (Register)
- Chức năng đăng nhập (Login)
- Chức năng đăng xuất (Log off)
- Sự hiển thị của menu
- Xác thực email khi đăng ký thành viên
- Tổng kết
1. Tạo project sử dụng ASP.NET Identity
Lưu ý: Vì ASP.NET Identity chỉ hỗ trợ từ ASP.NET MVC5 trở lên nên các bạn phải cài Visual Studio 2013 trở lên nhé. Series này mình sử dụng Visual Studio 2015Đây là các bước để tạo 1 project có tích hợp sẵn ASP.NET Identity:
Bước 1: Đầu tiên các bạn mở Visual studio lên và chọn New Project -> Templates -> Visual C# -> Web -> ASP.NET Web Application sau đó các bạn chọn nơi chứa Project (Location), nhập tên Project và tên Solution tùy ý và nhấn OK
Bước 2: Khung New ASP.NET Project hiện ra, các bạn chọn MVC và check vào checkbox MVC.
2. Giới thiệu về cấu trúc tổ chức thư mục của ASP.NET Identity
- App_Start/IdentityConfig.cs: chứa các lệnh để cấu hình ASP.NET Identity
- Controller/AccountController: controller chứa các action method có tác dụng xác thực người dùng như Login, Register, ForgotPassword, ...
- Controller/ManageController: controller chứa các action method có tác dụng quản lý user (khi user đã login vào web) như ChangePassword, SetPassword, ...
- Model/AccountViewModels: chứa các View Model hiển thị trong các view của AccountController
- Model/ManageViewModels: chứa các View Model hiển thị trong các view của ManageController
- Model/IdentityModels: chứa class ApplicationUser để quản lý thông tin user và class ApplicationDbContext để quản lý kết nối với database ở dạng Entity Framework Code First (các bạn nên có kiến thức căn bản về Entity Framework Code First) để có thể bổ sung thêm các field cho user hoặc loại bỏ bớt các field mà bạn không cần thiết 1 cách dễ dàng và ít bỡ ngỡ.
3. Chức năng đăng ký thành viên (Register)
Ok chúng ta đã hiểu khái quát về cấu trúc tổ chức của ASP.NET Identity trong 1 project ASP.NET MVC 5, cách tổ chức này mình nghĩ cũng tương tự ở ASP.NET WebForm. Bây giờ chúng ta thử đăng ký 1 tài khoản xem sao, nhưng trước khi đăng ký thì các bạn phải cấu hình chuỗi kết nối (connectionString) trong file Web.config ở thư mục gốc của project nhé. Bởi vì khi đăng ký thành viên thành công thì Visual Studio sẽ tự tạo mới 1 database (nếu database chưa tồn tại) chứa các table của ASP.NET Identity hoặc add thêm các table của ASP.NET Identity vào database (nếu database đó đã tồn tại) trong SQL Server của bạn. Tùy theo server SQL Server của các bạn là gì và tên database các bạn là gì thì các bạn sửa cho đúng trong file Web.config nhé. Ở đây mình muốn kết nối đến server .\sqlexpress với database là TestIdentity (database này mình chưa tạo và mình muốn Visual Studio tự tạo khi mình sử dụng chức năng đăng ký thành viên thành công).Đây là đoạn connectionStrings của mình:
Bây giờ chúng ta tiến hành thử dùng chức năng đăng ký thành viên (register) trong ASP.NET Identity nào
Bước 1: Chạy website và click vào link Register ở góc phải menu. Giao diện mặc định của trang Register hiện ra như hình dưới. Các bạn có thể tùy chỉnh giao diện theo ý muốn nhưng tránh sai sót ở code của Form trong View nhé
Bước 2: Nhập thử thông tin vào. Ở đây mình cố tính nhập Email không hợp lệ và nhấn nút Register luôn thì thấy thông báo lỗi: "The Email field is not a valid e-mail address" hoặc khi mình không nhập đủ thông tin thì trang cũng hiển thị lỗi. Như vậy các bạn có thể thấy rằng ASP.NET Identity mặc định đã thiết lập 1 số nguyên tắc (rule) khi kiểm tra dữ liệu Form trước khi submit phải không nào. Chúng ta có thể tùy chỉnh thông báo lỗi bằng tiếng Việt nếu muốn. Test bắt lỗi như vậy là đủ rồi bây giờ mình sẽ nhập thông tin register như sau:
Email: tuannguyen@gmail.com
Password: 123456
Confirm password: 123456
Sau đó mình ấn Register thì nhận được thông báo như hình bên dưới:
Ô HAY vẫn chưa thành công sao 😮. OK các bạn không cần lo bởi vì ASP.NET Identity mặc định thiết lập ràng buộc password rất "chặt chẽ". Nó yêu cầu user phải nhập password tối thiểu 6 ký tự bao gồm cả ký tự hoa, ký tự thường, ký tự số và cả ký tự đặc biệt . Đoạn code cấu hình password được tìm thấy ở dòng thứ 54 trong file IdentityConfig.cs
Đây là nội dung đoạn lệnh cấu hình ràng buộc password:
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
- RequiredLength: độ dài tối thiểu của password
- RequireNonLetterOrDigit: bắt buộc password chứa ký tự đặc biệt hoặc ký tự số
- RequireDigit: bắt buộc chứa ký tự số
- RequireLowercase: bắt buộc chứa ký tự in thường
- RequireUppercase: bắt buộc chứa ký tự in HOA
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false
RequireDigit = true,
RequireLowercase = false,
RequireUppercase = false,
};
Sau khi sửa xong mình tiến hành build lại project và load lại trang register. Sau đó nhập thông tin như sau:
Email: tuannguyen@gmail.com
Password: iloveyouok123
Confirm password: iloveyouok123
Chúng ta cùng tìm hiểu cơ chế hoạt động của chức năng đăng ký trong ASP.NET Identity nào. Đầu tiên khi mọi người click vào link Register thì ASP.NET sẽ gọi tới action Register (dòng 140) trong AccountController. Action này có attribute [AlowAnonymous] định nghĩa trên đầu để biểu thị rằng action cho phép cả người dùng vô danh (khách mới đến website) truy cập vào, điều này rất dễ hiểu bởi nếu chức năng đăng ký thành viên mà chỉ cho phép người dùng đã đăng nhập truy cập vào thì rất vô lý phải không nào. Nội dung action này đơn giản chỉ trả về 1 view Register. Nội dung của view Register được tìm thấy file Views/Account/Register.cshtml.
Mở view này lên chúng ta sẽ thấy rằng view định nghĩa model là 1 đối tượng của class RegisterViewModel được định nghĩa trong file Models/AccountViewModel. Đoạn lệnh thể hiện cho điều này: @model Demo_ASP.NET_Identity.Models.RegisterViewModel. Đây là đoạn code định nghĩa class RegisterViewModel:
- Required: biểu thị rằng thuộc tính có bắt buộc phải nhập (khác null) hay không
- EmailAddress: biểu thị rằng thuộc tính này phải đúng định dạng Email
- Display: tên được hiển thị ra View
- StringLength: độ dài ký tự tối thiểu, tối đa của 1 thuộc tính có kiểu string
- DataType: định dạng của thuộc tính
- Compare: so sánh giá trị thuộc tính này với 1 thuộc tính khác phải khớp
Thuộc tính | Quy tắc |
---|---|
| |
Password |
|
ConfirmPassword |
|
Các bạn thấy rằng mặc định tên hiển thị và thông báo lỗi ở dạng tiếng Anh. Bây giờ mính muốn tùy chỉnh nó sang tiếng Việt thì đơn giản là chúng ta thay đổi giá trị của thuộc tính ErrorMessage trong các atrribute và giá trị thuộc tính Name trong attribute [Display]. Mình sẽ thay đoạn lệnh thành như sau:
Form sử dụng các HTML Helper trong ASP.NET MVC (cái này mình không giải thích bởi vì những kiến thức này là căn bản trong ASP.MVC ^^) . Khi người dùng ấn Register thì form sẽ được đệ trình về phía server sau đó server sẽ gọi đến action Register có attribute [HttpPost] trong AccountController. Đây là nội dung của action này:
Từ khóa async và Task để biểu thị rằng đây là 1 action xử lý không đồng bộ, tham số của action là 1 đối tượng model được truyền từ form về. Đầu tiên đoạn code kiểm tra xem thông tin user nhập có hợp lệ hay không, nếu hợp lệ thì thực hiện tạo 1 đối tượng user của class ApplicationUser. Tiếp theo là gọi đến phương thức CreateAsync trong class UserManager (sau này các bạn dùng class này thường xuyên trong việc quản lý User) để thực hiện tạo 1 user mới. Lệnh if (result.Success) kiểm tra nếu việc tạo user thành công thì sẽ thực hiện đăng nhập vào website bằng cách gọi đến phương thức SignInAsync trong class SignInManager. Tất cả 2 lợi gọi đều ở dạng không đồng bộ (async) để tránh việc website bị đơ. Sau khi thực hiện gọi method SignInAsync xong thì website sẽ trả về lại trang chủ bằng lệnh RedirectToAction("Index","Home");
Giả sử nếu thông tin user nhập ở form là không hợp lệ thì đoạn lệnh if sẽ không được thực hiện thay vào đó là nó sẽ gọi tới phương thức AddErrors(result) và trả về chính View Register kèm theo model để hiển thị những lỗi ra view. Chi tiết về phương thức AddErrors các bạn tìm đến dòng 439 nhé. Các bạn thấy rằng ASP.NET Identity có 1 đoạn chú thích rất dài trong action này đúng không? Chúng ta sẽ tìm hiểu về nó khi thực hiện bổ sung chức năng xác thực user đăng ký qua email sau nhé.
OK bây giờ thử build lại website và vào lại trang Register để thử đăng ký thêm 1 user nữa nào. Dưới đây là 1 số hình ảnh test báo lỗi các bạn thử test xem có giống mình không ^^
Màn hình khi mình không nhập đủ thông tin |
Tất cả đều OK hết đúng không nào. Nếu bạn nhập chính xác thì sẽ đăng ký thành công bình thường và table dbo.AspNetUsers sẽ có thêm 1 record nữa.
4. Chức năng đăng nhập (Login)
1 user khi đăng ký thành công thì phải đăng nhập được vào website phải không nào. Bây giờ các bạn thử đăng xuất tài khoản hiện tại bằng cách click vào link Log off bên góc phải menu nhé. Sau đó các bạn click vào link Log in để thực hiện đăng nhập nào. Hình bên dưới cho thấy giao diện mặc định của trang Login, tất nhiên là các bạn có thể tùy chỉnh lại giao diện Login theo ý các bạn. Luôn luôn có thể như vậy.Giờ chúng ta cùng tìm hiểu lõi bên trong của chức năng này nhé. Đầu tiên khi các bạn click vào link Log in thì server sẽ gọi đến action Login (dòng 58) trong AccountController. Action này nhận 1 tham số string returnUrl để trả về Url này khi user log in thành công. Nội dung bên trong chỉ có 2 dòng, dòng đấu gán tham số returnUrl qua ViewBag.ReturnUrl để sử dụng bên trong view Login, dòng thứ hai để trả ra 1 view Login. Nội dung view này được tìm thấy tại file Views/Account/Login.cshtml. Hình bên dưới thể hiện nội dung của view này:
Khi user ấn nút Log in thì form sẽ đệ trình về server sau đó server gọi đến action Login có attribute [HttpPost] tại dòng 66 trong AccountController. Hình dưới thể hiện nội dung của action này:
- SignInStatus.Success: tức là đăng nhập thành công. Action sẽ trả về URL mà user vừa đứng trước đó.
- SignInStatus.LockedOut: user này đã bị lock (khóa) (Chúng ta sẽ tìm hiểu cách tùy chỉnh về việc khóa user ở bài viết khác)
- SignInStatus.RequiresVerification: khi nhảy vào case này tức là chúng ta đã cấu hình yêu cầu user đăng nhập thông qua 2 bước bao gồm bước nhận code từ số điện thoại đăng ký. Tuy nhiên series của mình không hướng dẫn về cách thức này. Các bạn có thể tìm hiểu về nó ở link này nếu muốn ^^ https://www.asp.net/identity/overview/features-api/two-factor-authentication-using-sms-and-email-with-aspnet-identity
- SignInStatus.Failure hoặc default: xảy ra 1 lỗi khi đăng nhập. Có thể do user nhập thông tin không tồn tại trong CSDL hoặc do 1 lỗi bất thường nào đó ở server mà ta không biết trước. Các bạn có thể thay chuỗi "Invalid login attempt" thành chuỗi tiếng Việt như "Email hoặc mật khẩu không chính xác". Nếu nhảy vào case này thì action sẽ trả về view Login kèm theo model để hiển thị lỗi ra view này.
Vậy là mình đã giải thích đầy đủ cơ chế chức năng đăng nhập trong ASP.NET Identity. Bây giờ bạn hãy build lại website và thử log in nhé.
5. Chức năng đăng xuất (Log off)
Cơ chế xử lý của chức năng này thì cực kỳ đơn giản . Khi các bạn click vào link Log off thì server sẽ gọi tới action LogOff (dòng 391) trong AccountController. Action này gọi tới phương thức SignOut trong class AuthenticationManager kèm theo cookie của ứng dụng để ASP.NET Identity remove những cookie đã lưu khi user đăng nhập, cuối cùng thì redirect về trang chủ.6. Sự hiển thị của menu
Nhiều bạn sẽ thắc mắc vì sao menu hiển thị 2 link Register và Login khi user chưa đăng nhập và khi đã đăng nhập thì hiển thị link Hello và Log off ? Để hiểu điều này thì các bạn mở view _Layout.cshtml ở thư mục Views/Shared lên sẽ thấy đoạn lệnh @Html.Partial("_LoginPartial") sau thẻ ul của menu. Tiếp tục mở view _LoginPartial ở thư mục Shared luôn thì sẽ thấy đoạn lệnh sau:7. Xác thực email khi đăng ký thành viên
Một chức năng phổ biến thường gặp ở các website đó là xác thực tài khoản thông qua email khi đăng ký. ASP.NET Identity hỗ trợ rất tốt developer cho việc cấu hình chức năng này. Tuy nhiên chức năng này "không bắt buộc" phải có. Nếu các bạn không có nhu cầu sử dụng thì có thể bỏ qua phần này. Sau đây là các bước cấu hình để gửi mail thông qua tài khoản gmail:Bước 1: Mở file IdentityConfig.cs trong folder App_Start. Tiếp theo bạn using thêm 2 namespace System.Net.Mail và System.Net, sau đó thay thế phương thức SendAsync trong class EmailService thành như sau:
Các bạn thay thế your_email@gmail.com thành địa chỉ gmail của bạn, password thành password gmail của bạn, DisplayName thành tên muốn hiển thị mà user sẽ thấy khi nhận được mail.
Bước 2: Truy cập vào link https://www.google.com/settings/security/lesssecureapps và bật quyền truy cập cho các ứng dụng kém an toàn.
Bước 3: Tìm đến action Register trong AccountController và uncomment đoạn chú thích và thay đổi chút xíu code để hiển thị thông báo cho user biết rằng họ cần vào email để xác thực tài khoản. Nội dung như sau:
Bước 5: Mở view ConfirmEmail ở file Views/Account/ConfirmEmail.cshtml. Đây là view hiển thị thông báo khi user xác thực email thành công. Các bạn có thể tùy chỉnh thành thông báo tiếng Việt nhé
Bước 6: Build lại website.
Dưới đây là kết quả của mình khi cấu hình email diepanhblog@gmail.com là email gửi link xác nhận và email nguyenaituan@yahoo.com là email đăng ký thành viên.
8. Tổng kết
Qua bài viết này các bạn có thể thấy được sự lợi hại của ASP.NET Identity trong việc bảo mật website rồi chứ. Còn rất nhiều điều thú vị chờ đợi bạn khám phá. Ở bài viết tiếp theo chúng ta sẽ cùng thực hiện bổ sung các chức năng sau:- Khóa (Lock) user
- Đổi mật khẩu
- Lấy lại mật khẩu
- Bổ sung thông tin của User
EmoticonEmoticon