Tác giả: Pavankumar Kasani, Ashok Srirama, and Ty Augustine
Ngày phát hành: 09 FEB 2026
Chuyên mục: .NET, Security, Identity, & Compliance
Giới thiệu
Trong các hệ thống phân tán hiện đại, microservices cần giao tiếp an toàn với nhau trong khi vẫn duy trì các ranh giới bảo mật nghiêm ngặt. Việc triển khai giao tiếp an toàn giữa các ứng dụng ở quy mô lớn đòi hỏi một phương pháp tiếp cận tiêu chuẩn hóa cho cả xác thực và ủy quyền. OAuth 2.0 cung cấp tiêu chuẩn hóa này, cho phép các dịch vụ xác minh danh tính của nhau và kiểm soát quyền truy cập vào các tài nguyên được bảo vệ.
Hãy xem xét một kịch bản thương mại điện tử điển hình, nơi người dùng đặt hàng thông qua giao diện người dùng của bạn, yêu cầu từ giao diện người dùng chuyển đến Dịch vụ Đặt hàng (Service A), sau đó gọi Dịch vụ Kho hàng (Service B) để kiểm tra mức tồn kho. Dịch vụ Kho hàng có thể cần gọi Dịch vụ Định giá (Service C) để tính toán chi phí cuối cùng. Mỗi cuộc gọi dịch vụ-đến-dịch vụ này đều yêu cầu xác thực và ủy quyền an toàn, đảm bảo rằng chỉ các dịch vụ hợp lệ mới có thể truy cập các điểm cuối được bảo vệ.
Bài đăng trên blog này trình bày cách triển khai xác thực dịch vụ-đến-dịch vụ cấp doanh nghiệp bằng cách sử dụng Microsoft Entra ID (trước đây là Azure AD) với luồng thông tin xác thực ứng dụng khách OAuth 2.0 cho các microservices .NET chạy trên các dịch vụ container của AWS.
Tổng quan kiến trúc
Kiến trúc của chúng tôi trình bày một mô hình giao tiếp microservices được triển khai trên các dịch vụ container của AWS, được bảo mật bằng xác thực và ủy quyền Microsoft Entra ID. Chúng tôi đăng ký mỗi microservice trong hệ thống của mình dưới dạng một ứng dụng trong Entra ID, tạo ra một danh tính rõ ràng cho mọi thành phần dịch vụ. Service A (Dịch vụ Đặt hàng), Service B (Dịch vụ Kho hàng) và Service C (Dịch vụ Định giá) đều có các đăng ký riêng với các ID ứng dụng khách và thông tin xác thực duy nhất. Luồng giao tiếp đầu cuối như sau.

Hình 1: Kiến trúc cấp cao
- Yêu cầu của người dùng: Người dùng cuối tương tác với giao diện người dùng, giao diện này gửi yêu cầu đến Service A.
- Service A đến Service B: Service A sau đó yêu cầu mã truy cập từ Entra ID bằng cách sử dụng thông tin xác thực ứng dụng khách của nó. Entra ID xác thực các thông tin xác thực này và cấp một mã thông báo được định phạm vi cụ thể để truy cập API của Service B. Service A, sau đó gọi Service B với mã thông báo trong tiêu đề Authorization.
- Xác thực mã thông báo: Service B xác thực mã thông báo đến với Entra ID và xử lý yêu cầu.
- Service B đến Service C: Service B tuân theo cùng một mô hình, lấy mã thông báo riêng của mình để gọi Service C.
- Chuỗi phản hồi: Các phản hồi chảy ngược qua chuỗi đến người dùng.

Hình 2: Luồng ủy quyền cấp cao
Xác thực trong kiến trúc này là quá trình xác minh danh tính dịch vụ. Khi Service A trình bày Client ID và Secret của nó cho Entra ID, nó chứng minh “nó là ai”. Khi Service B nhận được yêu cầu với mã truy cập, nó xác thực mã thông báo đó với Entra ID để xác nhận danh tính của người gọi là hợp lệ. Việc xác minh lẫn nhau này đảm bảo chỉ các dịch vụ đã đăng ký, đáng tin cậy mới tham gia vào chuỗi giao tiếp.
Mã truy cập được thiết kế để ủy quyền. Mục đích chính của chúng là cấp quyền truy cập vào các tài nguyên và API được bảo vệ. Khi Service A lấy mã truy cập, mã thông báo đó chỉ dành cho máy chủ tài nguyên (Service B) sẽ xác thực và sử dụng nó. Mã thông báo chứa thông tin về những gì người gọi được phép làm, thường được thể hiện dưới dạng phạm vi hoặc quyền. Mã truy cập là một phần của đặc tả OAuth 2.0 cốt lõi và về mặt kỹ thuật có thể ở bất kỳ định dạng nào, mặc dù chúng chủ yếu được triển khai dưới dạng JWTs (JSON Web Tokens).
Điều kiện tiên quyết
Trước khi triển khai giải pháp này, hãy đảm bảo bạn có:
Yêu cầu phát triển:
- SDK .NET 8.0 hoặc mới hơn đã được cài đặt.
- Visual Studio, VS Code, hoặc IDE ưa thích.
- Hiểu biết cơ bản về ASP.NET Core Web APIs.
- Docker để container hóa (tùy chọn).
Yêu cầu về Azure/Microsoft:
- Một tenant Microsoft Entra ID (Azure AD) đang hoạt động.
- Quyền quản trị để đăng ký ứng dụng trong Entra ID.
- Hiểu biết về giao thức OAuth 2.0.
Yêu cầu về AWS:
- Tài khoản AWS có quyền triển khai các khối lượng công việc container như Amazon Elastic Container Service (Amazon ECS) hoặc Amazon Elastic Kubernetes Service (Amazon EKS).
- Quen thuộc với các dịch vụ container của AWS.
- Cấu hình VPC và mạng cho giao tiếp giữa các dịch vụ.
Hướng dẫn cấu hình Entra ID
Mỗi microservice cần có danh tính riêng trong Entra ID, được biểu thị dưới dạng đăng ký ứng dụng. Các đăng ký này thiết lập mối quan hệ tin cậy và xác định dịch vụ nào có thể truy cập API nào. Quá trình cấu hình bao gồm ba thành phần chính:
- Đăng ký Service A trong Microsoft Entra ID. Một ID ứng dụng khách được tạo cho đăng ký và bạn phải tạo một khóa bí mật ứng dụng khách. Nếu bạn muốn triển khai ủy quyền dựa trên vai trò, hãy tham khảo Thêm vai trò ứng dụng vào ứng dụng của bạn và nhận chúng trong mã thông báo.
- Lưu trữ ID ứng dụng khách và khóa bí mật trong một khóa bí mật của AWS Secrets Manager.
- Lặp lại bước 1 và 2 cho Service B và C.
- Đăng ký Service A làm ứng dụng khách cho Service B và Service B làm ứng dụng khách cho Service C.
- Bước cuối cùng là cấp quyền cho Service A để truy cập API của Service B. Trong đăng ký ứng dụng của Service A, bạn sẽ thêm quyền API cho Service B, đặc biệt chọn quyền ứng dụng. Tham khảo Cấu hình quyền ứng dụng cho một API web.
- Lặp lại bước 5 cho Service B để truy cập Service C.
Sau khi hoàn tất cấu hình, bạn sẽ có một số giá trị quan trọng mà các dịch vụ của bạn cần trong thời gian chạy. Mỗi dịch vụ biết ID ứng dụng khách của riêng mình. Service A và Service B có khóa bí mật ứng dụng khách; tất cả các dịch vụ chia sẻ cùng một ID tenant và mỗi ứng dụng khách biết các phạm vi cho các API mà nó cần gọi. Các giá trị này sẽ được lưu trữ trong các khóa bí mật của AWS Secrets Manager và được cung cấp cho ứng dụng trong thời gian chạy.
Khi Service A cần gọi Service B, nó gọi lớp TokenAcquisitionService mẫu (trong đoạn mã sau) để lấy mã truy cập. Dịch vụ đầu tiên kiểm tra xem nó có mã thông báo được lưu trong bộ nhớ cache vẫn còn hiệu lực hay không. Việc lưu mã thông báo vào bộ nhớ cache là rất quan trọng đối với hiệu suất vì việc yêu cầu mã thông báo mới cho mỗi cuộc gọi API sẽ tạo ra tải không cần thiết cho Entra ID và thêm độ trễ vào các cuộc gọi dịch vụ của bạn. Việc triển khai lưu mã thông báo vào bộ nhớ cache và sử dụng lại chúng cho đến khi chúng còn năm phút nữa là hết hạn (một cài đặt có thể cấu hình), tại thời điểm đó nó yêu cầu một mã thông báo mới.
Nếu cần mã thông báo mới, dịch vụ sẽ xây dựng một yêu cầu POST đến điểm cuối mã thông báo của Entra ID. Yêu cầu bao gồm ID ứng dụng khách, khóa bí mật ứng dụng khách, loại cấp quyền (luôn là “client_credentials” cho luồng này) và phạm vi đại diện cho API của Service B. Phạm vi tuân theo định dạng api://<SERVICE_B_CLIENT_ID>/.default, cho Entra ID biết rằng Service A muốn một mã thông báo để truy cập tất cả các quyền được cấp cho API của Service B. Điểm cuối mã thông báo trả về một phản hồi JSON chứa mã truy cập, loại mã thông báo, thời gian hết hạn và các phạm vi được cấp.
public class TokenAcquisitionService{private readonly IConfiguration _configuration;private readonly HttpClient _httpClient;private string? _cachedToken;private DateTime _tokenExpiry = DateTime.MinValue;public TokenAcquisitionService(IConfiguration configuration){_configuration = configuration;_httpClient = new HttpClient();}public async Task<string> GetAccessTokenAsync(){// Check if we have a valid cached tokenif (!string.IsNullOrEmpty(_cachedToken) && DateTime.UtcNow < _tokenExpiry.AddMinutes(-5)){return _cachedToken;}var clientId = _configuration["AzureAd:ClientId"];var clientSecret = _configuration["AzureAd:ClientSecret"];var tenantId = _configuration["AzureAd:TenantId"];var scope = _configuration["ServiceB:Scope"];var requestBody = new FormUrlEncodedContent(new[]{new KeyValuePair<string, string>("client_id", clientId!),new KeyValuePair<string, string>("client_secret", clientSecret!),new KeyValuePair<string, string>("scope", scope!),new KeyValuePair<string, string>("grant_type", "client_credentials")});var tokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token";try{var response = await _httpClient.PostAsync(tokenEndpoint, requestBody);response.EnsureSuccessStatusCode();var content = await response.Content.ReadAsStringAsync();var tokenResponse = System.Text.Json.JsonSerializer.Deserialize<TokenResponse>(content);_cachedToken = tokenResponse!.access_token;_tokenExpiry = DateTime.UtcNow.AddSeconds(tokenResponse.expires_in);return _cachedToken;}catch (Exception ex){throw new InvalidOperationException($"Failed to acquire access token: {ex.Message}", ex);}}}
Với việc xử lý việc lấy mã thông báo, Service A sẽ sử dụng một ứng dụng khách HTTP tiêu chuẩn, thêm mã truy cập vào tiêu đề Authorization để truy cập Service B.
public async Task<string> GetDataFromServiceBAsync(){var token = await _tokenService.GetAccessTokenAsync();_httpClient.DefaultRequestHeaders.Clear();_httpClient.DefaultRequestHeaders.Authorization =new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);var serviceBUrl = _configuration["ServiceB:BaseUrl"];try{var response = await _httpClient.GetAsync(serviceBUrl);response.EnsureSuccessStatusCode();return await response.Content.ReadAsStringAsync();}catch (HttpRequestException ex){throw new InvalidOperationException($"Failed to call Service B: {ex.Message}", ex);}}
Việc triển khai của Service B tập trung vào việc nhận và xác thực mã truy cập từ các dịch vụ gọi. Xác thực mã thông báo diễn ra tự động với mỗi yêu cầu, nhưng bạn kiểm soát cách ứng dụng phản hồi các kết quả xác thực thông qua các chính sách ủy quyền.
Cách tiếp cận đơn giản nhất là sử dụng thuộc tính [Authorize] trên các bộ điều khiển hoặc các điểm cuối riêng lẻ. Thuộc tính này yêu cầu một mã thông báo hợp lệ để truy cập điểm cuối, chặn mọi yêu cầu chưa được xác thực.
[Authorize][ApiController][Route("[controller]")]public class ServiceAController(ILogger<ServiceAController> logger) : ControllerBase{...}
Điều này kết thúc việc thiết lập xác thực và ủy quyền trong mô hình microservices. Là một phương pháp hay nhất, hãy luôn lên kế hoạch xoay vòng các khóa bí mật ứng dụng khách của bạn và tự động cập nhật chúng trong các khóa bí mật của AWS Secrets Manager.
Kết luận
Việc triển khai xác thực ứng dụng-đến-ứng dụng mạnh mẽ với Microsoft Entra ID cung cấp bảo mật cấp doanh nghiệp cho các kiến trúc microservices chạy trên AWS. Luồng OAuth 2.0 Client Credentials cho phép xác thực an toàn, không mật khẩu giữa các dịch vụ trong khi vẫn duy trì các ranh giới bảo mật rõ ràng và nhật ký kiểm tra. Ủy quyền chi tiết cho phép kiểm soát chính xác dịch vụ nào có thể truy cập điểm cuối và hoạt động nào, với sự linh hoạt để điều chỉnh quyền mà không cần triển khai lại dịch vụ.
Kêu gọi hành động
Để triển khai lên AWS Fargate, bạn có thể sử dụng AWS Toolkit for Visual Studio, cung cấp cửa sổ explorer cho một số dịch vụ AWS, bao gồm các dịch vụ container, và các trình hướng dẫn giúp xây dựng và xuất bản ứng dụng lên đám mây AWS. Các trình hướng dẫn này hỗ trợ triển khai các ứng dụng .NET và .NET Framework lên AWS Elastic Beanstalk, Amazon Elastic Container Service (Amazon ECS), Amazon Elastic Container Registry (Amazon ECR), và AWS Fargate từ bên trong Visual Studio.
Về tác giả

Pavankumar Kasani
Pavankumar Kasani là Kiến trúc sư Giải pháp AWS có trụ sở tại thành phố New York. Anh ấy đam mê giúp khách hàng thiết kế các giải pháp có khả năng mở rộng, được kiến trúc tốt và hiện đại hóa trên AWS Cloud. Ngoài công việc, anh ấy thích dành thời gian cho gia đình, chơi cricket, bóng bàn và thử nghiệm các công thức nấu ăn mới trong bếp.

Ashok Srirama
Ashok là Kiến trúc sư Giải pháp Chuyên gia Container cấp cao tại Amazon Web Services, có trụ sở tại Washington Crossing, PA. Anh ấy chuyên về các ứng dụng serverless, container và kiến trúc hệ thống phân tán. Khi không dành thời gian cho gia đình, anh ấy thích xem cricket và lái xe.

Ty Augustine
Ty là Kiến trúc sư Giải pháp Chuyên gia Microsoft tập trung vào .NET, SQL Server và Container. Ty có trụ sở tại NYC và làm việc chặt chẽ với các ngành công nghiệp đa dạng để đẩy nhanh quá trình di chuyển và hiện đại hóa lên AWS Cloud. Trước khi đến AWS, Ty là kiến trúc sư phần mềm Microsoft stack trong hơn 20 năm.