وقتی یه درخواست از کلاینت ( مثلا وقتی کاربر در صفحه مرورگر محصولیو انتخواب میکنه ، خب این درخواست میاد سمت سرور و خب مشخصا ، قبلش این کاربر log in کرده و هم authaticate شده و هم authorize شده )
اما وقتی دو تا سرویس داریم ( هر دو سمت بک اند هستند ) ، مثلا یک سرویس مسول اینه که درخواست کاربرو بگیره بعدش مثلا بیاد بریزتش تو یه سرویس دیگه برای ذخیره سازی مثل kafka بعد از این دوباره اون سرویس درخواستو میگیره و این بار میده به سرویس دیگه مثلا vdb . حالا وقتی درخواست بین دو تا سرویس هست که جفتشون هم بک اند مون هستند پس برای این که این مطمین بشیم این دو سرویس همو میشناسن و کسی نمیتونه خودشو جای یکی از اینا بزاره و هکمون کنه میایم از این قابلیت استفاده میکنیم
یکی از این سرویس ها میشه سرورمون تو این سناریو ، یعنی سرویسی که api اونجا قرار داره و در دات نت این تنظیماتو براش باید انجام بدیم
builder.Services.Configure<KestrelServerOptions>(options => options.ConfigureHttpsDefaults(listenOptions =>
{
listenOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
listenOptions.AllowAnyClientCertificate();
listenOptions.ServerCertificate = new X509Certificate2(certificatePath, CertificateUtilities.PfxPassword);
}));
داخل program یه کار دیگه ام باید بکنیم ، اونم اینه که اول باید فایل cert بسازیم در محلی که بهش میگیم که در نهایت در تنظیمات بالا بفرستیمش سمت کلاینت ، پس می تونیم این خطو به program اضافه کنیم
CertificateUtilities.GenerateCertificate(certificatePath);
که میاد یه تابع رو فراخوانی میکنه که یه جای دیگه اونو ساختیم (هر جا خواستید میتونید این تابعو بزارید)
namespace myNameSpace;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
public static class CertificateUtilities
{
public const string PfxPassword = "MyPassword ";
public static void GenerateCertificate(string certPath)
{
if (string.IsNullOrEmpty(certPath))
{
میتونیم مثلا یه لاگ بزاریم اینجا
}
if (!File.Exists(certPath))
{
GenerateCertificate(certPath, PfxPassword);
if (!File.Exists(certPath))
{
throw new ArgumentException($"Could not create new certificate in: {certPath}");
}
}
}
private static void GenerateCertificate(string path, string password)
{
using var rsa = RSA.Create(2048);
var req = new CertificateRequest("C=IR/CO=Amnpardaz CO./Team=Vishkav/App=VDB", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
req.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, true));
req.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(req.PublicKey, false));
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1));
var bytes = cert.Export(X509ContentType.Pfx, password);
File.WriteAllBytes(path, bytes);
}
}
[HttpGet("{Storage}/{Id}")]
public IActionResult GetFile([FromServices] GetFileCommand command, [FromRoute] FileRequest request, CancellationToken cancellationToken)
{
var cert = this.HttpContext.Connection.ClientCertificate?.Thumbprint.ToString();
if (cert == null || this.options.Value.CertThumbprint != cert)
{
return new UnauthorizedResult();
}
return command.Run(request, cancellationToken);
}
2. تنظیمات سمت کلاینت :
بعد از اینکه سمت سرور کامل تنظیماتشو انجام دادیم ( ساخت یه فایل cert در ابتدای کار ، رمز نگاریش , و ارسالش به هر کی که به سرور دخواست بده )
حالا میرسیم به تنظیمات مربوط که کلاینت ، یعنی سرویسی که سمت بک اند هست و ارسال درخواست میده (post / delete/ get / ... ) به سمت سرور
میایم یه کلاس می سازیم و این تنظیماتو انجام میدیم
namespace ..... ;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Options;
public class HttpclientService
{
private readonly ILogger<HttpclientService> logger;
private readonly IOptions<.......> options;
public HttpclientService(ILogger<HttpclientService> logger, IOptions<......> options, )
{
this.logger = logger;
this.options = options;
}
public HttpClient HttpClient()
{
var handler = new HttpClientHandler();
var pss = CertificateUtilities.PfxPassword;
var clientCertificate = ConvertFromPfx(this.options.Value.CertificatePath, pss);
ّFile.TryWriteAllTextToFile("ThumbPrint.txt", clientCertificate.Thumbprint, CancellationToken.None);
handler.ClientCertificates.Add(clientCertificate);
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
{
if (cert?.Thumbprint == this.options.Value.VishkadehServerCertThumbprint)
{ return true; }
return false;
};
var client = new HttpClient(handler)
{
};
var uriString = this.options.Value.VishkadehURL ?? "";
if (string.IsNullOrEmpty(uriString))
{
throw new ArgumentException(".......");
}
if (!uriString.EndsWith("/", StringComparison.Ordinal))
{
uriString += "/";
}
client.BaseAddress = new Uri(uriString);
return client;
}
private static X509Certificate2 ConvertFromPfx(string pfx, string password)
{
var clientCertificate = new X509Certificate2(pfx, password,
X509KeyStorageFlags.Exportable);
return clientCertificate;
}
}
و در نهایت تو هر کلاسی که خواستیم ، خیلی راحت میتونیم درخواست رو ارسال کنیم
await this.client.PostAsync("path", content, token)