GST Licensing 機台綁定指南
本文件說明 GST.Core.Licensing 的架構、Machine Fingerprint 機制與部署流程。
1. 架構總覽
┌──────────────────────────────────────────────────┐
│ Metalama Aspect │
│ [LicenseCheck] / [LicenseRequired("feature")] │
├──────────────────────────────────────────────────┤
│ ILicenseService │
│ 驗證、Feature 檢查、USB Dongle、背景監控 │
├──────────────────────────────────────────────────┤
│ IReactorLicenseProvider │
│ .NET Reactor License.Status 封裝 │
├──────────────────────────────────────────────────┤
│ Hardware 層 │
│ MachineInfoService(WMI Fingerprint) │
│ UsbDeviceService(USB Dongle 偵測) │
└──────────────────────────────────────────────────┘
2. 核心介面
2.1 ILicenseService
public interface ILicenseService
{
ILicenseInfo? CurrentLicense { get; }
bool IsLicenseValid();
bool IsFeatureEnabled(string featureCode);
bool IsAnyFeatureEnabled(params string[] featureCodes);
IReadOnlyList<string> GetEnabledFeatures();
int? GetLimit(string limitCode);
bool IsUsbDonglePresent();
Task<LicenseActivationResult> ActivateAsync(string licenseKey);
Task<bool> DeactivateAsync();
}
2.2 ILicenseInfo
public interface ILicenseInfo
{
string LicenseId { get; }
string CustomerName { get; }
LicenseTier Tier { get; } // Trial/Basic/Standard/Professional/Enterprise
DateTime ExpiresAt { get; }
int DaysRemaining { get; }
bool IsExpired { get; }
bool IsTrial { get; }
LicenseBindingMode BindingMode { get; }
}
2.3 LicenseBindingMode
[Flags]
public enum LicenseBindingMode
{
None = 0,
Machine = 1, // 綁定硬體 Fingerprint
UsbDongle = 2, // 綁定 USB Dongle
MachineOrUsb = 3, // 擇一(彈性)
MachineAndUsb = 4, // 兩者都需要(嚴格)
Online = 8 // 需線上驗證
}
3. Machine Fingerprint
3.1 組成
| 元件 | WMI Query | 範例 |
|---|---|---|
| CPU ID | Win32_Processor.ProcessorId | BFEBFBFF000A0653 |
| Motherboard Serial | Win32_BaseBoard.SerialNumber | DBBGV1100120700A379100 |
| BIOS Serial | Win32_BIOS.SerialNumber | UDBGXTA00K221008170001 |
| HDD Serial | Win32_DiskDrive.SerialNumber(Index=0) | 0000_0000_... |
| MAC Address | NetworkInterface.GetPhysicalAddress()(最高速非 Loopback) | 2C-0D-A7-B4-3B-FB |
3.2 Combined Hash
CombinedHash = SHA256(UTF8("{CpuId}|{MotherboardSerial}|{BiosSerial}|{HddSerial}"))
僅非空元件參與 Hash 計算。
3.3 容錯機制
public bool Matches(MachineFingerprint other, int tolerance = 1)
預設容許 1 個元件不符,處理硬體更換(如換網卡、換硬碟)的情境。
4. USB Dongle 支援
4.1 偵測方式
Win32_DiskDrive (InterfaceType='USB')
→ Win32_DiskDriveToDiskPartition
→ Win32_LogicalDiskToPartition
→ 取得 DriveLetter, SerialNumber, VID/PID
4.2 UsbDeviceInfo
public record UsbDeviceInfo
{
public required string DeviceId { get; init; }
public required string SerialNumber { get; init; }
public string VolumeLabel { get; init; }
public string DriveLetter { get; init; }
public string VendorId { get; init; } // USB VID
public string ProductId { get; init; } // USB PID
}
5. Metalama Aspect 整合
5.1 [LicenseCheck] — 驗證 License 存在
[LicenseCheck]
public void PerformCriticalOperation() { /* ... */ }
// 無有效 License → 拋出 LicenseException
[LicenseCheck(ThrowOnFailure = false)]
public void PerformOptionalOperation() { /* ... */ }
// 無有效 License → 回傳 default,不拋例外
5.2 [LicenseRequired] — 驗證特定 Feature
[LicenseRequired("AdvancedReporting")]
public Report GenerateAdvancedReport() { /* ... */ }
// Feature 未啟用 → 拋出 LicenseException
6. DI 註冊
services.AddLicensing(options =>
{
options.LicenseFilePath = Path.Combine(configDir, "license.lic");
options.ThrowOnLicenseFailure = false;
options.BackgroundCheckIntervalMinutes = 30;
options.FingerprintTolerance = 1; // 容許 1 個硬體元件不符
options.AllowOfflineMode = true; // 支援離線環境
});
7. 部署流程
7.1 全新部署
1. 在目標機台執行應用程式
└→ 應用程式偵測無 License
└→ 匯出 hardware-id.txt(含 CombinedHash)
2. 使用者將 hardware-id.txt 傳給開發團隊
3. 開發團隊使用 .NET Reactor License Manager 產生 license.lic
└→ 綁定該機台的 Hardware ID
4. 將 license.lic 放到 %APPDATA%\{AppName}\license.lic
5. 重新啟動應用程式 → 驗證通過
7.2 License 檔案位置
%APPDATA%\ProcessVision\license.lic
7.3 App.xaml.cs 載入順序
// 1. 複製 License 檔(在 DI 之前,Reactor 保護 DLL 載入前)
CopyLicenseFiles();
// 2. DI 註冊 AddLicensing()
// 3. 初始化 AspectServiceLocator(啟用 Aspect 注入)
AspectServiceLocator.Initialize(Services);
// 4. 驗證 License 狀態
CheckLicenseStatus();
// 5. 若無 License,匯出 Hardware ID
ExportHardwareId();
8. Debug vs Release 行為
| 情境 | Debug | Release |
|---|---|---|
| 無 License | 靜默允許所有功能 | 顯示倒數視窗 / 限制功能 |
| Validator | DevelopmentLicenseValidator(全通過) | LicenseValidator(Reactor 驗證) |
| Feature Check | 全部 enabled | 依 License KeyValue 判斷 |
9. .NET Reactor Hardware Lock 差異
| GST.Core.Licensing | .NET Reactor Hardware Lock | |
|---|---|---|
| 層級 | 應用層 | DLL 保護層 |
| 綁定方式 | SHA256 Fingerprint | Reactor 內建 HID |
| 容錯 | 支援(tolerance 參數) | 不支援 |
| Feature 控制 | 支援(KeyValue) | 不支援 |
| 用途 | 應用授權管理 | 防止 DLL 複製執行 |
兩者是獨立機制,可同時使用。
10. 常見問題
10.1 License 驗證失敗
- 確認
license.lic在正確路徑(%APPDATA%\{AppName}\) - 確認 License 未過期
- 確認硬體未大幅更換(超過 tolerance 容許)
- 檢查
hardware-id.txt是否與 License 對應
10.2 硬體更換後 License 失效
更換超過 1 個硬體元件(如同時換主機板 + 硬碟)會導致 Fingerprint 不符。需重新匯出 Hardware ID 並產生新 License。
10.3 離線環境(Air-Gapped)
ValidateOnlineAsync() 目前回傳 true(不需網路),專為工廠離線環境設計。
文件版本:v1.0 | 建立日期:2026-03-16 | 對應 Issue:GST-172