首頁

目前文章總數:157 篇

  

最後更新:2024年 12月 07日

0060. .Net Core WebSite網站 - 建立Https憑證結合 Nginx,部署 gRPC聊天室 & 訂閱客戶端範例

日期:2023年 12月 24日

標籤: C# Asp.net Core Web MVC Web gRPC CentOS Nginx Https Chat Room

摘要:C# 學習筆記


應用所需:1. Visual Studio 2022 以上,支援.net Core 6
範例檔案:範例檔案
解決問題:1. 如何使用 Https 與 gRPC 連線
     2. 如何建立客戶端,並且保持訂閱狀態
     3. 將 gRPC 通訊的.Net Core WebSite 聊天室部署至 CentOs Server 基於此:範例檔案,此篇是 Https 的版本
基本介紹:本篇分為五大部分。
第一部分:Nginx 配置
第二部分:建立 Https 測試憑證
第三部分:聊天室代碼啟用 Https 調整
第四部分:部署聊天室 & Demo
第五部分:獨立客戶端 & Demo






第一部分:Nginx 配置

Step 1:Nginx 增加Https反代 - 建檔

先連線到遠端的Server 上 -> 新建 https.conf -> 設定內容保存
建立的內容如下圖,與指令:


# 建立一個https 的連線,並且加入憑證
server {
    listen        443 ssl http2;
    server_name   localhost;
    ssl_certificate     /etc/nginx/cert.crt;
    ssl_certificate_key /etc/nginx/cert.key;
    location / {
        proxy_pass http://localhost:5099;
        proxy_http_version 1.1;
        proxy_set_header   Host $host:$server_port;
        proxy_set_header Connection "";
        proxy_redirect off;
        proxy_set_header X-real-ip $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        grpc_pass         grpc://localhost:50051;
    }
    access_log /tmp/grpcHttpsChatroom.log;
    error_log /tmp/grpcHttpsChatroom.error.log;
}



Step 2:Nginx 增加Https反代 - 說明1

以下是設定說明:

listen 443 - 表示走 Https 通道
__ SSL - 表示 Https 進行 SSL/TLS 加密協議,並且表示啟用了 Https 憑證
__ Http2 - 通信協議,比 Http/1.1 更高高效能,是在 gRPC 中預設使用的協議
ssl_certificate 使用的公開金鑰,這個要提供給 gRPC 用戶端連線用
ssl_certificate_key 使用的加密金鑰,為 Server 自行保管
proxy_pass 設定網站的反代,走向內部時,會訪問我們代碼的聊天室網站, Kestreal 設定為 Http & 開啟 5099 Port
grpc_pass gRPC 傾聽的接口,走 Https 通信時, Kestreal 設定為 Https & 開啟 50051 Port



Step 3:Nginx 增加Https反代 - 說明2

以下兩個檔案,會在 [第二部分:建立 Https 測試憑證] 建立。
需要先建立 nginx Server的設定檔案(https.conf) 是因為憑證通常是基於域名產生

ssl_certificate /etc/nginx/cert.crt;                               
ssl_certificate_key /etc/nginx/cert.key;                               





第二部分:建立 Https 測試憑證

Step 1:建立測試憑證 - 建立

登入 CentOs 指令列,輸入以下:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/cert.key -out /etc/nginx/cert.crt -subj "/CN=mydomain.com" -extensions SAN -config <(echo "[req]"; echo distinguished_name=req; echo "[SAN]"; echo subjectAltName=IP:192.168.51.62)


對照以上指令, {} 是要依照自己的環境設定的內容:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout {私鑰輸出位置} -out {公開金鑰輸出位置} -subj "/CN=mydomain.com" -extensions SAN -config <(echo "[req]"; echo distinguished_name=req; echo "[SAN]"; echo subjectAltName=IP:{本機的IP位址})


私鑰輸出位置 伺服器持有的私鑰 .key 輸出檔案位置,與 https.conf 相呼應
公開金鑰輸出位置 用戶端、伺服器共同持有的公鑰 .crt 輸出檔案位置,與 https.conf 相呼應
本機的IP位址 此 IP 為本機,在驗證憑證時會比對是否當前伺服器為合法持有者




Step 2:建立 Kestrel 使用的 SSL憑證

我們聊天室使用的是 Kertrel Server,所以還需要將 .crt 與 .key 做 .pfx 憑證。
.pfx 憑證的特色是包含了憑證、私鑰和可能的中間憑證。簡單說 kestrel 只看打包的整合憑證。
CentOs 指令列,輸入以下:

openssl pkcs12 -export -out /etc/nginx/certificate.pfx -inkey /etc/nginx/cert.key -in /etc/nginx/cert.crt


對照以上指令, {} 是要依照自己的環境設定的內容:

openssl pkcs12 -export -out {pfx檔案輸出位置} -inkey {私鑰來源位置} -in {公開金鑰來源位置}







第三部分:聊天室代碼啟用 Https 調整

Step 1:代碼調整部分

本篇是 Https 版本,要部署到 Server 上,需要對代碼做啟用 Https 調整
基於範例檔案改進。
需調整黃框位置的代碼:


Step 2:專案設定檔案


打開 appsettings.json
加入 CertPath、PfxPath、 Kestrel 配置,部署到 Server 時,可以依照 Server 金鑰路徑調整:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "CertPath": "Certification\\cert.crt",
  "PfxPath": "/etc/nginx/certificate.pfx",
  "Kestrel": {
    "Ports": {
      "HttpPort": 5099,
      "HttpsPort": 50051
    }
  }
}



Step 3:調整初始化檔案

調整 program.cs 依照1. ~ 6. 的項目依序開啟 Https 連線,並且使用 appsettings.json 的設定內容

using System.Net;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

// 1. 添加gRPC 
builder.Services.AddGrpc();

// 2. 取得當前gRPC Https連線配置
IConfigurationRoot baseBuilderData = new ConfigurationBuilder()
    .AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "Properties", "launchSettings.json"), optional: true, reloadOnChange: true)
    .Build();

// 3. 啟用 Kestrel Server ,內部Server指向本地的https
int httpsPort = builder.Configuration.GetValue<int>("Kestrel:Ports:HttpsPort", 50051);
NetCoreGRPCChattingRoomForHttpsExample.Controllers.GlobalConst.Self_GRPC_URL = @$"https://localhost:{httpsPort}";
var certPath = builder.Configuration.GetValue<string>("CertPath", string.Empty);
NetCoreGRPCChattingRoomForHttpsExample.Controllers.GlobalConst.Cert_Path = certPath;
builder.WebHost.UseKestrel(options =>
{
    // 4. 配置 Web Endpoint(HTTP/1.1) => 連網站用
    var httpPort = builder.Configuration.GetValue<int>("Kestrel:Ports:HttpPort", 5099);   
    options.Listen(IPAddress.Any, httpPort, listenOptions => { });


    // 5. 配置Https + 使用 Server 上的產生Https憑證 (此pfx 是該Server域名產生,所以Local Debug啟動會異常)
    var httpsPort = builder.Configuration.GetValue<int>("Kestrel:Ports:HttpsPort", 50051);
    var pfxFile = builder.Configuration.GetValue<string>("PfxPath", string.Empty);
    options.Listen(IPAddress.Any, httpsPort, listenOptions =>
    {
        listenOptions.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2;
        // 5-2. 開啟Https 並且指向Server 的 HTTPS 憑證位置 
        listenOptions.UseHttps(pfxFile);        
    });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseStaticFiles();

app.UseRouting();
app.UseAuthorization();

//6. 基本的Grpc注入使用服務
app.UseEndpoints(endpoints =>
{    
    endpoints.MapGrpcService<NetCoreGRPCChattingRoomForHttpsExample.Service.ChatService>();
});

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();



Step 4:調整控制器 - 1

調整 ChatController.cs 新增 static string Cert_Path 變數

public class GlobalConst
{
    /// <summary>
    /// 加入聊天室的用戶會記錄於此
    /// </summary>
    public static Dictionary<string, string> DicMessages = new Dictionary<string, string>();
    public static string Self_GRPC_URL = "";
    public static string Cert_Path = "";
}



Step 5:調整控制器 - 2

調整 ChatController.cs 新建 GetGRPChannel() 方法,與調整對應的呼叫

GetGRPChannel() 方法:

/// <summary>
/// 取得公開金鑰Cert
/// </summary>
/// <returns></returns>
private static GrpcChannel? GetGRPChannel()
{
    var rootCert = System.IO.File.ReadAllText(NetCoreGRPCChattingRoomForHttpsExample.Controllers.GlobalConst.Cert_Path);
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(new X509Certificate2(Encoding.UTF8.GetBytes(rootCert)));            
    //如果憑證為測試憑證必須加入這行,如果生產有產生正式的Https 憑證把此行註解即可
    handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
    var channelOptions = new GrpcChannelOptions
    {
        HttpClient = new HttpClient(handler)
    };
    var client = GrpcChannel.ForAddress(GlobalConst.Self_GRPC_URL, channelOptions);
    return client;
}


調整對應的呼叫:

 // 1. 調整加入Https cert公開金鑰
using (var channel = GetGRPChannel())
{
    ...
    業務邏輯略
    ...
}





第四部分:部署聊天室 & Demo

Step 1:發佈程式

專案 -> 右鍵 -> 發布程式


Step 2:上傳到Server

將檔案內容部署到 CentOs Server 上


Step 3:調整配置

依照自己環境,調整 .crt 與 .pfx 的檔案路徑


Step 4:啟動gRPC 與 WebSite

輸入以下指令啟動 dotnet core 的 kestrel Server

dotnet NetCoreGRPCChattingRoomForHttpsExample.dll




Step 5:Demo 聊天室

聊天室已經可以使用 Https 做訂閱、推送
注意到為不安全,是因為憑證是用測試憑證,如果是生產環境,對外網域的情況下,這邊就不會跳出 Https 不安全的提示訊息




第五部分:獨立客戶端 & Demo

Step 1:獨立客戶端專案

這邊提供獨立的客戶端建立 Https 憑證的方式,可以參考 HttpsGrpcConnetionTest 專案
今天若為 Android、IOS、JavaScript 情況下也可以參考做調整


Step 2:代碼完整內容

代碼完整內容如下:

1 - 4 基本配置步驟
5, 6-1, 6-2 設定憑證,與指定方法
7, 8 客戶端要知道伺服器端的連線位置,並建立連線
9 呼叫 gRPC 的 SendMessageAsync() , 發送訊息測試
10 訂閱資料,永遠保持連線,當伺服器端推播資料時,永遠都會收到資訊


using ChatApp;
using Grpc.Core;
using Grpc.Net.Client;
using System.Security.Cryptography.X509Certificates;
using System.Text;

//1. 將正確的 proto 複製到 Protos 資料夾下
//2. 加入服務參考 -> 用戶端 -> 最新的.proto 檔案
//3. 從Server 環境下取得憑證檔案 EX: /etc/nginx/cert.crt 
//4. 指向檔案路徑 (※這個是測試憑證,基本上沒什麼用處)
var rootCert = File.ReadAllText(@"Certification\cert.crt");

//5. 設定憑證
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(new X509Certificate2(Encoding.UTF8.GetBytes(rootCert)));
//6-1. 測試環境可以開啟這個,因為憑證是我們產的測試Https 憑證,但生產需要將此行註解
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

//6-2. 建立HttpClient
var channelOptions = new GrpcChannelOptions
{
    HttpClient = new HttpClient(handler)
};

//7. 指定連線gRPC目標
using var channel = GrpcChannel.ForAddress("https://192.168.51.62:50051", channelOptions);

//8. 建立聯繫
var client =  new ChatApp.ChatService.ChatServiceClient(channel);

//9. 發送一個訊息
var sendResponseStream = client.SendMessageAsync(new MessageRequest() { Message = "Hello", Username = "Louis" });

//10. 開啟訂閱模式,等待資料推播過來
using (var subScribeResponseStream = client.Subscribe())
{
    using (var subscribeCall = client.Subscribe())
    {
        var subscribeRequest = new SubscribeRequest { SubscriberName = "Https Subscriber From Client" };
        await subscribeCall.RequestStream.WriteAsync(subscribeRequest);

        // 接收消息
        var receiveTask = Task.Run(async () =>
        {
            await foreach (var message in subscribeCall.ResponseStream.ReadAllAsync())
            {
                Console.WriteLine($"Received message: {message.Content}");
            }
        });
        // 等待接收消息的任務完成
        await receiveTask;
    }
}



Step 3:Demo 客戶端連線

我們啟動客戶端程式後,會發送一個訊息,接著加入訂閱模式
並且從 Https Server 聊天室發送訊息時,該客戶端也會同步收到訊息,完成 Https 下的聊天室功能