Trong bài này mình sẽ dùng .Net 8 cùng thư viện JWT tại đây:
https://www.nuget.org/packages/JWT
Sau đó chúng ta sẽ triển khai thuật toán như sau. Đầu tiên khởi tạo instance RSA với độ dài 2048 bits:
using RSA rsa = RSA.Create(2048);
Console.WriteLine("Public key: ");
Console.WriteLine(rsa.ExportRSAPublicKeyPem());
Console.WriteLine("Private key: ");
Console.WriteLine(rsa.ExportRSAPrivateKeyPem());
Console.WriteLine();
string publicKey = rsa.ExportRSAPublicKeyPem();
C#Giải thích: RSA.Create(2048) sẽ tạo ra biến có kiểu dữ liệu là RSA với độ dài bits là 2048 và sử dụng using để tự động Dispose(). Cách khởi tạo này có thể chạy trên mọi platform hiện có trừ trình duyệt. Xem thêm tại đây.
Trong đoạn code trên chúng ta chỉ cần lưu lại mỗi khoá công khai (publicKey) để dùng xác thực token là được, còn khoá riêng tư (privateKey) đã có sẵn trong biến instance RSA khi chúng ta khởi tạo, chỉ dùng nó một lần duy nhất cho mục đích sign token và xoá bỏ khỏi hệ thống, không cần lưu lại. Để tối ưu hệ thống nên lưu khoá công khai vào memory cache có thời gian ngắn (Redis, …). Tiếp theo là bước tạo token JWT:
JwtBuilder tokenBuilder = JwtBuilder.Create();
tokenBuilder.WithAlgorithm(new RS256Algorithm(rsa, rsa));
tokenBuilder.AddClaim("user_id", 1)
.AddClaim("user_email", "phatvphat@gmail.com")
.AddClaim("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds())
.AddClaim("exp", DateTimeOffset.UtcNow.AddDays(1).ToUnixTimeSeconds());
string token = tokenBuilder.Encode();
Console.WriteLine("token: " + token);
C#Giải thích: Sử dụng thuật toán RS256 (RSA Signature with SHA-256) để ký token trong đó biến “rsa” ở cả 2 tham số trong “new RS256Algorithm(rsa, rsa)” lần lượt là publicKey và privateKey (phải có privateKey mới được phép ký token). Trong thư viện họ sẽ dựa vào instance chúng ta truyền vào để lấy publicKey và privateKey tương ứng, do đó chỉ cần 1 instance là hợp lý.
Sau khi tạo thành công token, chúng ta sẽ thử xác thực xem token có hợp lệ hay không.
using RSA rsaOnlyPublicKey = RSA.Create();
rsaOnlyPublicKey.ImportFromPem(publicKey);
Console.WriteLine();
try
{
JwtBuilder jsonBuilder = JwtBuilder.Create();
jsonBuilder.WithAlgorithm(new RS256Algorithm(rsaOnlyPublicKey));
jsonBuilder.MustVerifySignature();
string json = jsonBuilder.Decode(token);
Console.WriteLine("json: " + json);
}
catch (Exception ex)
{
switch (ex)
{
case InvalidTokenPartsException:
case TokenNotYetValidException:
Console.WriteLine("Token is not valid yet");
break;
case TokenExpiredException:
Console.WriteLine("Token has expired");
break;
case SignatureVerificationException:
Console.WriteLine("Token has invalid signature");
break;
default: throw;
}
}
C#Giải thích: Đoạn code bên trên có thể đóng gói lại như là một hàm dùng để verify và trả về data khi verify thành công. Trong đó chúng ta sẽ khởi tạo 1 instance RSA mới với mục đích chỉ chứa mỗi publicKey và hoàn toàn không cần bận tâm tới privateKey tại bước này nữa. Đơn giản lúc này chỉ cần truyền instance rsaOnlyPublicKey vào “new RS256Algorithm(rsaOnlyPublicKey)” để xác thực token là xong. Có lỗi thì báo lỗi lần lượt theo từng case bạn mong muốn.
Từ bài hướng dẫn trên, chúng ta có thể dễ dàng triển khai xác thực giữa các nền tảng lập trình tối ưu hơn, không cần lưu trữ lại mật khẩu để ký token như trước đây (chẳng hạn nếu có dùng thêm server khác làm api…). Và có thể mở rộng thêm các tính năng như quản lý phiên hoạt động (giống Facebook, Google, …) bằng cách lưu thêm các metadata chung chung kèm với publicKey (Device name, IP, datetime, …) một cách thiết thực hơn. Khi dùng mã hoá đối xứng thông thường (chẳng hạn mật khẩu cố định lưu ở đâu đó trên server) vẫn có thể triển khai tính năng như trên nhưng mình cảm thấy có vẻ không cần thiết, sao phải tạo thêm các session id lum la. :))) Đây sẽ là động lực cho bạn thực hiện tính năng này.
Vậy là chúng ta đã triển khai xong thuật toán kết hợp JWT cùng mã hoá bất đối xứng RSA. Nếu bạn thấy hay để lại một bình luận cho mình nhé!