ASP.NET Core: Cách thực hiện chứng thực JWT trong ASP.NET Core


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Thật dễ dàng để bảo mật các điểm cuối (endpoint) API tối thiểu trong ASP.NET Core bằng cách sử dụng JSON Web Token (JWT) để xác thực và ủy quyền. Bạn chỉ cần làm theo các bước hướng dẫn dưới đây.

ASP.NET Core giới thiệu một mô hình lưu trữ đơn giản hóa cho phép chúng ta xây dựng các API nhẹ với mức độ phụ thuộc tối thiểu. Đương nhiên, bạn thường sẽ cần bảo mật điểm cuối của các API đó trong ứng dụng của mình. Mục tiêu của bài viết này là giúp bạn có một khởi đầu thuận lợi để thực hiện điều đó.

Bài viết này sẽ thảo luận về cách ta có thể bảo mật các điểm cuối API tối thiểu của mình bằng cách sử dụng xác thực JWT—tức là xác thực dựa trên JSON Web Token.

Để bảo mật API tối thiểu bằng xác thực JWT, chúng ta sẽ làm theo các bước sau:

  1. Tạo một dự án API tối thiểu trong Visual Studio 2022.
  2. Tạo điểm cuối API trong tệp Program.cs.
  3. Thêm gói Microsoft.AspNetCore.Authentication.JwtBearer NuGet vào dự án của ta.
  4. Triển khai xác thực JWT trong tệp Program.cs.
  5. Tạo một lớp model người dùng có tên User để lưu trữ thông tin đăng nhập của người dùng.
  6. Chỉ định khóa bí mật trong tệp appsinstall.json.
  7. Chỉ định cài đặt xác thực JWT trong tệp Program.cs.
  8. Thêm middleware dịch vụ ủy quyền vào ứng dụng của ta trong tệp Program.cs.
  9. Tạo và xác thực Mã thông báo Web JSON trong tệp Program.cs.

Lưu ý rằng tất cả các ví dụ về mã được hiển thị trong bài đăng này, ngoại trừ lớp model User, phải là một phần của Program.cs. Lớp model User phải là một phần của tệp User.cs.

Để làm việc với các ví dụ về mã được cung cấp trong bài viết này, bạn nên cài đặt Visual Studio 2022+ trong hệ thống của mình.

Tạo dự án ASP.NET Core 6 Web API tối thiểu trong Visual Studio 2022+

Trước hết, hãy tạo một dự án ASP.NET Core 6 trong Visual Studio. Hãy làm theo các bước sau sẽ tạo dự án ASP.NET Core 6 Web API mới trong Visual Studio 2022:

  1. Khởi chạy Visual Studio 2022 IDE.
  2. Nhấp vào Create new project.
  3. Trong cửa sổ Create new project, chọn ASP.NET Core Web API từ danh sách các mẫu được hiển thị.
  4. Bấm Next.
  5. Trong cửa sổ Configure your new project, chỉ định tên và vị trí cho dự án mới.
  6. Tùy chọn đánh dấu vào hộp kiểm Place solution and project in the same directory, tùy theo sở thích của bạn.
  7. Bấm Next.
  8. Trong cửa sổ Additional Information hiển thị tiếp theo, hãy bỏ chọn hộp kiểm có nội dung Use controllers… vì ta sẽ sử dụng các API tối thiểu trong ví dụ này. Để Authentication TypeNone (mặc định).
  9. Đảm bảo rằng các hộp kiểm Enable Docker, Configure for HTTPSEnable Open API Support không được chọn vì ta sẽ không sử dụng bất kỳ tính năng nào trong số đó ở đây.
  10. Nhấp vào Create.

Ta sẽ sử dụng dự án ASP.NET Core Web API này để tạo điểm cuối API tối thiểu và triển khai xác thực JWT cho nó trong các phần tiếp theo của bài viết này.

Tạo điểm cuối HTTP Get trong ASP.NET Core 6

Khi bạn tạo một dự án Web API tối thiểu mới trong Visual Studio 2022, file Program.cs sẽ được tạo bằng một vài dòng code mặc định. Bạn có thể thay thế code mặc định bằng đoạn code sau để mọi thứ đơn giản và vẫn cung cấp cách kiểm tra API của bạn.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/security/getMessage",
() => "Hello World!").RequireAuthorization();
app.Run();

Lưu ý việc sử dụng phương thức mở rộng RequireAuthorization tại đây. Điều này giúp bạn bảo vệ các route của mình bằng chính sách ủy quyền và buộc bạn phải cung cấp thông tin chứng thực khi gọi điểm cuối này. Middleware ủy quyền sẽ sử dụng thông tin này để chứng thực yêu cầu cho bối cảnh thực thi hiện tại.

Nếu bạn thực thi điểm cuối này mà không có thông tin này, bạn sẽ gặp phải lỗi HTTP 401 Unauthorized như trong Hình 1.

api tối thiểu an toàn 01

Hình 1. Lỗi trái phép HTTP 401 sẽ được tạo ra nếu yêu cầu ủy quyền và không cung cấp thông tin ủy quyền.

Cài đặt gói JwtBearer NuGet

Bây giờ hãy thêm gói NuGet Microsoft.AspNetCore.Authentication.JwtBearer vào dự án của bạn. Để thực hiện việc này, hãy chọn dự án trong cửa sổ Solution Explorer, sau đó nhấp chuột phải và chọn Manage NuGet Packages. Trong cửa sổ NuGet Package Manager, tìm kiếm gói Microsoft.AspNetCore.Authentication.JwtBearer và cài đặt nó.

Ngoài ra, bạn có thể cài đặt gói thông qua console NuGet Package Manager bằng cách nhập lệnh hiển thị bên dưới.

PM> Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

Chỉ định khóa bí mật trong file appsettings.json

Tiếp theo, tạo một phần trong file appsinstall.json để biết thông tin về Issuer (Nhà phát hành), Audience (Đối tượng) và Key (Khóa). Thông tin này sẽ được sử dụng sau này để tạo JSON Web Token. Lưu ý rằng bạn có thể đặt bất kỳ tên nào cho phần này mà bạn muốn; Ta sẽ sử dụng tên "Jwt" cho thuận tiện.

Thêm thông tin sau vào file appsettings.json.

  "Jwt": {
    "Issuer": "https://v1study.com/",
    "Audience": "https://v1study.com/",
    "Key": "This is a sample secret key - please don't use in production environment.'"
  }

Chỉ định cài đặt chứng thực trong file Program.cs

Phương thức AddAuthenication trong file Program.cs được sử dụng để định cấu hình chứng thực JWT tại thời điểm ứng dụng khởi động. Nó chỉ định sơ đồ chứng thực là JwtBearer. Ngoài ra, lệnh gọi phương thức AddJwtBearer giúp định cấu hình các tham số mã thông báo.

Các giá trị Issuer, Audience và Key được đọc từ file cấu hình appsinstall.json. Thể hiện TokenValidationParameters được sử dụng để cho biết liệu thông tin về Issuer, Audience, Key và Lifetime có nên được chứng thực hay không.

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey
        (Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true
    };
});

Để thêm dịch vụ ủy quyền vào ứng dụng của bạn, Program.cs của bạn cũng phải bao gồm đoạn code sau:

builder.Services.AddAuthorization();

Program.cs của bạn cũng phải bao gồm các phương thức sau để kích hoạt khả năng xác thực và ủy quyền.

app.UseAuthentication();
app.UseAuthorization();

Tạo model User trong ASP.NET Core

Chúng ta sẽ cần một lớp để lưu trữ thông tin đăng nhập của một hoặc nhiều người dùng. Tạo một lớp có tên User trong một file có cùng tên với phần mở rộng .cs. Sau đó chèn đoạn code sau:

public class User
{
    public string UserName { get; set; }
    public string Password { get; set; }
}

Lớp này sẽ được sử dụng để chấp nhận thông tin chứng thực của người dùng làm đầu vào.

Tạo điểm cuối để tạo JSON Web Token

Cuối cùng, chúng ta cần viết code cần thiết để tạo và xác thực JWT mà chúng ta sẽ sử dụng để ủy quyền các lệnh gọi tới API. Khi mã thông báo được tạo để đáp ứng yêu cầu ban đầu tới API, bạn có thể sao chép mã đó và sử dụng mã đó để ủy quyền trong tất cả các yêu cầu tiếp theo.

Bây giờ, hãy viết đoạn code sau vào file Program.cs để tạo điểm cuối HTTP Post mới sẽ tạo JWT cho người dùng được xác thực.

app.MapPost("/security/createToken",
[AllowAnonymous] (User user) =>
{
    if (user.UserName == "joydip" && user.Password == "joydip123")
    {
        var issuer = builder.Configuration["Jwt:Issuer"];
        var audience = builder.Configuration["Jwt:Audience"];
        var key = Encoding.ASCII.GetBytes
        (builder.Configuration["Jwt:Key"]);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim("Id", Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Email, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti,
                Guid.NewGuid().ToString())
             }),
            Expires = DateTime.UtcNow.AddMinutes(5),
            Issuer = issuer,
            Audience = audience,
            SigningCredentials = new SigningCredentials
            (new SymmetricSecurityKey(key),
            SecurityAlgorithms.HmacSha512Signature)
        };
        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var jwtToken = tokenHandler.WriteToken(token);
        var stringToken = tokenHandler.WriteToken(token);
        return Results.Ok(stringToken);
    }
    return Results.Unauthorized();
});

Một thể hiện của lớp User được sử dụng để chấp nhận tên người dùng và mật khẩu được chuyển đến điểm cuối này. Lưu ý đến attribute AllowAnonymous, nó được sử dụng để xác định rằng ta không cần kiểm tra ủy quyền ở điểm cuối này. Các Issuer, Audience và Key được đọc từ file cấu hình. Mỗi thứ này được sử dụng để tạo mã thông báo mà ta đã chỉ định sẽ hết hạn sau 5 phút.

Mã nguồn hoàn chỉnh cho Program.cs

Đây là mã nguồn đầy đủ của file Program.cs để bạn tham khảo.

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey
            (Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true
    };
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseHttpsRedirection();
app.MapGet("/security/getMessage", () => "Hello World!").RequireAuthorization();
app.MapPost("/security/createToken",
[AllowAnonymous] (User user) =>
{
    if (user.UserName == "joydip" && user.Password == "joydip123")
    {
        var issuer = builder.Configuration["Jwt:Issuer"];
        var audience = builder.Configuration["Jwt:Audience"];
        var key = Encoding.ASCII.GetBytes
        (builder.Configuration["Jwt:Key"]);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim("Id", Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Email, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti,
                Guid.NewGuid().ToString())
             }),
            Expires = DateTime.UtcNow.AddMinutes(5),
            Issuer = issuer,
            Audience = audience,
            SigningCredentials = new SigningCredentials
            (new SymmetricSecurityKey(key),
            SecurityAlgorithms.HmacSha512Signature)
        };
        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var jwtToken = tokenHandler.WriteToken(token);
        var stringToken = tokenHandler.WriteToken(token);
        return Results.Ok(stringToken);
    }
    return Results.Unauthorized();
});
app.UseAuthentication();
app.UseAuthorization();
app.Run();

Chứng thực JWT trong action

Khi bạn đăng thông tin xác thực của người dùng lên điểm cuối createToken bằng Postman, bạn sẽ có thể thấy mã thông báo được tạo.

api tối thiểu an toàn 02

Hình 2. JWT được tạo thành công.

Lưu ý rằng ta đã chuyển thông tin xác thực của người dùng, tức là tên người dùng và mật khẩu, trong phần nội dung của yêu cầu.

Bây giờ, hãy gọi điểm cuối HTTP Get mà ta đã tạo trước đó và chuyển mã thông báo đã tạo dưới dạng mã thông báo mang trong tiêu đề yêu cầu. Nếu mã thông báo được tạo của bạn hợp lệ, bạn sẽ thấy thông báo hiển thị trong Hình 3.

api tối thiểu an toàn 03

Hình 3. Điểm cuối HTTP Get trả về tin nhắn văn bản trong phản hồi.

Như bạn có thể thấy trong Hình 3, tin nhắn văn bản “Hello World!” được hiển thị vì mã thông báo ta đã truyền là hợp lệ. Cũng lưu ý phản hồi HTTP 200 OK (được đánh dấu bằng hình chữ nhật màu xanh lá cây).

Trong ví dụ này, ta đã mã hóa cứng tên người dùng và mật khẩu để giữ cho mọi thứ đơn giản. Tất nhiên, bạn không bao giờ nên mã hóa thông tin đăng nhập của người dùng trong môi trường production. Một lựa chọn tốt là sử dụng ASP.NET Core Identity để quản lý tài khoản người dùng.

Để kiểm tra việc triển khai API tối thiểu của ta tại đây, ta đã sử dụng Postman, một trong những công cụ phổ biến nhất hiện nay để kiểm tra API. Để kiểm tra các điểm cuối API tối thiểu của mình, bạn cũng có thể sử dụng Swagger, một bộ công cụ giúp việc cung cấp biểu diễn đồ họa cho API của bạn trở nên đơn giản.

Nguồn: learn.microsoft.com
» Tiếp: Triển khai ứng dụng web ASP.NET
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!