KyrosDev Claude commited on
Commit
e69ed7b
·
1 Parent(s): c000155

改進授權流程為自動綁定模式

Browse files

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

LICENSE_WORKFLOW.md CHANGED
@@ -6,60 +6,9 @@ KSTools License Manager 是一個為 Revit Plugin 設計的授權管理系統,
6
 
7
  ## 授權流程詳細說明
8
 
9
- ### 1. 用戶端操作 (Revit Plugin)
10
 
11
- #### 1.1 硬體指紋生成
12
- ```csharp
13
- // HardwareManager.cs 中的實作
14
- public class HardwareManager
15
- {
16
- public static string GetHardwareId()
17
- {
18
- var hwInfo = "";
19
-
20
- // 取得 CPU ProcessorId
21
- using (var searcher = new ManagementObjectSearcher("SELECT ProcessorId FROM Win32_Processor"))
22
- {
23
- foreach (var obj in searcher.Get())
24
- {
25
- hwInfo += obj["ProcessorId"]?.ToString();
26
- break;
27
- }
28
- }
29
-
30
- // 取得主機板序號
31
- using (var searcher = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_BaseBoard"))
32
- {
33
- foreach (var obj in searcher.Get())
34
- {
35
- hwInfo += obj["SerialNumber"]?.ToString();
36
- break;
37
- }
38
- }
39
-
40
- // 生成 SHA256 Hash 並取前32位
41
- using (var sha256 = SHA256.Create())
42
- {
43
- var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(hwInfo));
44
- return Convert.ToBase64String(hash).Substring(0, 32);
45
- }
46
- }
47
- }
48
- ```
49
-
50
- #### 1.2 用戶操作步驟
51
- 1. 開啟 Revit Plugin
52
- 2. 點擊「取得授權」或「授權管理」按鈕
53
- 3. 系統自動生成 32 位硬體指紋
54
- 4. 用戶複製硬體指紋
55
- 5. 聯繫管理員提供:
56
- - 用戶名稱
57
- - 硬體指紋 (32位字符)
58
- - 所需授權期間
59
-
60
- ### 2. 管理員操作 (後台管理)
61
-
62
- #### 2.1 建立新授權
63
  1. 登入管理後台
64
  2. 進入「用戶管理」頁面
65
  3. 點擊「建立新授權」按鈕
@@ -67,7 +16,6 @@ public class HardwareManager
67
 
68
  **必要欄位:**
69
  - **用戶名稱**: 識別用戶的名稱
70
- - **硬體指紋**: 用戶提供的32位硬體指紋
71
  - **有效天數**: 授權有效期限
72
  - 7天 (試用)
73
  - 30天
@@ -79,47 +27,104 @@ public class HardwareManager
79
  - **電子郵件**: 聯繫用途
80
  - **備註**: 內部記錄使用
81
 
82
- #### 2.2 系統驗證
83
  - 用戶名稱不可為空
84
- - 硬體指紋必須為32位字符
85
  - 有效天數必須選擇
86
- - 硬體指紋自動轉為大寫統一格式
87
 
88
- #### 2.3 授權生成
89
  系統自動生成:
90
- - 唯一授權碼 (32位字符,顯示為 XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX 格式)
91
  - 到期時間計算
92
  - 建立時間記錄
 
93
 
94
- ### 3. 授權驗證流程
95
 
96
- #### 3.1 首次啟用
97
- 1. 用戶在 Revit Plugin 輸入收到的授權碼
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  2. 系統驗證授權碼有效性
99
- 3. 檢查硬體指紋是否匹配
100
- 4. 記錄啟用時間和硬體資訊
101
- 5. 授權狀態變更為「啟用
102
 
103
- #### 3.2 日常驗證
104
  1. Plugin 啟動時自動驗證
105
  2. 檢查授權是否過期
106
  3. 驗證硬體指紋匹配
107
- 4. 記錄使用時間
108
- 5. 更新��後使用時間
109
 
110
  ### 4. 授權狀態管理
111
 
112
  #### 4.1 授權狀態分類
113
- - **未啟用**: 已建立但未使用的授權
114
- - **啟用中**: 正常使用中的授權
115
  - **已過期**: 超過有效期限的授權
116
  - **已停用**: 管理員手動停用的授權
117
 
118
- #### 4.2 硬體綁定規則
119
- - 每個授權只能綁定到一台電腦
120
- - 硬體指紋基於 CPU + 主
121
- - 更換主要硬體將導致授權失效
122
- - 需重新申請授權
 
 
 
 
 
123
 
124
  ### 5. 系統記錄與監控
125
 
@@ -139,29 +144,36 @@ public class HardwareManager
139
 
140
  ### 6. 安全特性
141
 
142
- #### 6.1 硬體綁定安全
143
- - 基於硬體唯一識別
144
- - SHA256 雜湊加密
145
- - 32位字符長度
146
- - 大小寫統一處理
147
 
148
- #### 6.2 授權保護
149
- - 授權碼加密儲存
150
- - 時間戳記驗證
151
- - IP 地址
152
- - 異常使用監控
 
153
 
154
  ## 故障排除
155
 
156
- ### 常見問題
157
- 1. **硬體指紋不匹配**: 更換了 CPU 或主機板
 
 
158
  2. **授權過期**: 超過設定的有效期限
159
- 3. **授權未啟用**: 用戶未正確輸入授權碼
160
-
161
- ### 解決方案
162
- 1. 重新申請授權 (硬體變更)
163
- 2. 延長授權期限 (過期問題)
164
- 3. 重新發送正確授權碼 (啟用問題)
 
 
 
 
165
 
166
  ## 技術規格
167
 
 
6
 
7
  ## 授權流程詳細說明
8
 
9
+ ### 1. 管理員操作 (後台管理)
10
 
11
+ #### 1.1 建立新授權 (簡化流程)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  1. 登入管理後台
13
  2. 進入「用戶管理」頁面
14
  3. 點擊「建立新授權」按鈕
 
16
 
17
  **必要欄位:**
18
  - **用戶名稱**: 識別用戶的名稱
 
19
  - **有效天數**: 授權有效期限
20
  - 7天 (試用)
21
  - 30天
 
27
  - **電子郵件**: 聯繫用途
28
  - **備註**: 內部記錄使用
29
 
30
+ #### 1.2 系統驗證
31
  - 用戶名稱不可為空
 
32
  - 有效天數必須選擇
33
+ - **無需硬體指紋**: 系統將在首次啟用時自動綁定
34
 
35
+ #### 1.3 授權生成
36
  系統自動生成:
37
+ - 唯一授權碼 (32位字符)
38
  - 到期時間計算
39
  - 建立時間記錄
40
+ - **硬體指紋設為 null** (等待首次綁定)
41
 
42
+ ### 2. 用戶端操作 (Revit Plugin)
43
 
44
+ #### 2.1 硬體指紋生成 (自動)
45
+ ```csharp
46
+ // HardwareFingerprint.cs 中的實作
47
+ public class HardwareFingerprint
48
+ {
49
+ public static string GetHardwareId()
50
+ {
51
+ var hwInfo = "";
52
+
53
+ // 取得 CPU ProcessorId
54
+ string cpuId = GetCpuId();
55
+ if (!string.IsNullOrEmpty(cpuId))
56
+ hwInfo += cpuId;
57
+
58
+ // 取得主機板序號
59
+ string motherboardSerial = GetMotherboardSerial();
60
+ if (!string.IsNullOrEmpty(motherboardSerial))
61
+ hwInfo += motherboardSerial;
62
+
63
+ // 備選方案:MAC 地址
64
+ if (string.IsNullOrEmpty(hwInfo))
65
+ hwInfo = GetMacAddress();
66
+
67
+ // 生成 SHA256 Hash 並取前32位
68
+ using (var sha256 = SHA256.Create())
69
+ {
70
+ var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(hwInfo + "KSTOOLS_SALT"));
71
+ string hashString = Convert.ToBase64String(hash);
72
+ hashString = hashString.Replace("+", "").Replace("/", "").Replace("=", "");
73
+ return hashString.Substring(0, 32);
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ #### 2.2 用戶操作步驟 (簡化)
80
+ 1. 開啟 Revit Plugin
81
+ 2. 點擊「授權管理」按鈕
82
+ 3. 輸入管理員提供的授權碼
83
+ 4. **系統自動生成並綁定硬體指紋**
84
+ 5. 立即啟用成功,無需額外操作
85
+
86
+ ### 3. 授權驗證流程 (自動綁定)
87
+
88
+ #### 3.1 首次啟用 (自動綁定)
89
+ 1. 用戶在 Revit Plugin 輸入授權碼
90
+ 2. 系統驗證授權碼有效性
91
+ 3. **檢查是否為首次啟用** (`activated_at` 為 null)
92
+ 4. **自動生成硬體指紋並綁定**
93
+ 5. 記錄啟用時間和硬體資訊
94
+ 6. 授權狀態變更為「已啟用」
95
+
96
+ #### 3.2 重複啟用 (驗證綁定)
97
+ 1. 用戶再次輸入授權碼
98
  2. 系統驗證授權碼有效性
99
+ 3. **檢查硬體指紋是否匹配**
100
+ 4. 匹配成功 → 啟用成功
101
+ 5. 不匹配 → 顯示「此授權已綁定其他設備
102
 
103
+ #### 3.3 日常驗證 (快取支援)
104
  1. Plugin 啟動時自動驗證
105
  2. 檢查授權是否過期
106
  3. 驗證硬體指紋匹配
107
+ 4. **支援 24 小時離線使用** (快取機制)
108
+ 5. 記錄使用時間並更新後使用時間
109
 
110
  ### 4. 授權狀態管理
111
 
112
  #### 4.1 授權狀態分類
113
+ - **未啟用**: 已建立但未首次使用的授權 (`activated_at` 為 null)
114
+ - **啟用中**: 已綁定硬體且正常使用中的授權
115
  - **已過期**: 超過有效期限的授權
116
  - **已停用**: 管理員手動停用的授權
117
 
118
+ #### 4.2 硬體綁定規則 (自動綁定模式)
119
+ - **首次啟用自動綁定**: 輸入授權碼後自動綁定當前硬體
120
+ - **一一碼**: 每個授權只能在一台設備上使用
121
+ - **硬體指紋基於**: CPU ProcessorId + 主機板序號 + MAC 地址 (備選)
122
+ - **硬體變更影響**: 更換 CPU 或主機板將導致指紋變更,需重新授權
123
+
124
+ #### 4.3 安全機制
125
+ - **防止授權碼濫用**: 首次綁定後無法在其他設備使用
126
+ - **離線支援**: 24 小時本地快取,網路中斷仍可使用
127
+ - **自動過期**: 系統自動檢查並處理過期授權
128
 
129
  ### 5. 系統記錄與監控
130
 
 
144
 
145
  ### 6. 安全特性
146
 
147
+ #### 6.1 硬體綁定安全 (增強版)
148
+ - **多重硬體識別**: CPU ProcessorId + 主機板序號 + MAC 地址備選
149
+ - **SHA256 + Base64 編碼**: 強化加密算法
150
+ - **32位字符長度**: 標準長度確保唯一性
151
+ - **KSTOOLS_SALT**: 專用鹽值增加安全性
152
 
153
+ #### 6.2 授權保護 (自動綁定模式)
154
+ - **首次綁定保護**: 防止授權碼被搶先綁定到錯誤設備
155
+ - **本地加密快取**: Windows Registry 加密儲存
156
+ - **時間戳驗證**: 防止時間回撥攻擊
157
+ - **IP 地址記錄**: 完整的使用追蹤
158
+ - **24小時離線支援**: 網路中斷時的可用性保障
159
 
160
  ## 故障排除
161
 
162
+ ### 常見問題 (更新版)
163
+ 1. **「此授權已綁定其他設備」**:
164
+ - 原因: 授權已在其他設備上首次啟用
165
+ - 或者: 更換了 CPU 或主機板導致硬體指紋變更
166
  2. **授權過期**: 超過設定的有效期限
167
+ 3. **網路連接錯誤**: API 服務無法連接
168
+ 4. **首次啟用失敗**: 授權碼錯誤或已過期
169
+
170
+ ### 解決方案 (更新版)
171
+ 1. **硬體綁定問題**:
172
+ - 確認是否在正確的設備上啟用
173
+ - 如需變更設備,請聯繫管理員重新建立授權
174
+ 2. **過期問題**: 管理員可延長授權期限
175
+ 3. **網路問題**: 檢查網路連接,或等待離線快取生效
176
+ 4. **啟用失敗**: 確認授權碼正確性,檢查是否已過期
177
 
178
  ## 技術規格
179
 
README.md CHANGED
@@ -24,25 +24,24 @@ pinned: false
24
 
25
  ### 🎯 核心授權流程
26
 
27
- #### 用戶端操作 (Revit Plugin)
28
- 1. **硬體指紋生成** - 系統自動生成 32 位硬體指紋 (CPU + 主機板)
29
- 2. **取得指紋** - 用戶點擊按鈕複製硬體指紋
30
- 3. **聯繫管理員** - 提供用戶名稱和硬體指紋
31
-
32
  #### 管理員操作 (Web 後台)
33
  1. **建立授權** - 填入必要資訊:
34
  - 用戶名稱 *
35
- - 硬體指紋 * (32位字符)
36
  - 有效天數 * (7天/30天/90天/1年/永久)
37
  - 電子郵件 (選填)
38
  - 備註 (選填)
39
- 2. **生成授權碼** - 系統自動產生 32位字符授權碼 (顯示為 XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX)
40
  3. **提供給用戶** - 將授權碼交付給用戶
41
 
42
- #### 與驗證
43
- 1. **授權啟用** - 用戶在 Plugin 輸入授權碼首次啟用
44
- 2. **硬體綁定** - 系統驗證硬體指紋匹配並綁定
45
- 3. **日常驗證** - Plugin 每次使時自動驗證授權有效性
 
 
 
 
 
46
 
47
  ### 🎨 現代化前端
48
  - **零框架依賴**: 純 HTML/CSS/JS,載入迅速
@@ -52,10 +51,10 @@ pinned: false
52
  - **即時更新**: 無需重新載入的 SPA 體驗
53
 
54
  ### 🛡️ 安全機制
55
- - 硬體綁定授權系統
56
- - JWT Token 認證
57
- - 加密通訊協議
58
- - 防止授權
59
 
60
  ### 📊 管理功能
61
  - 用戶授權管理
 
24
 
25
  ### 🎯 核心授權流程
26
 
 
 
 
 
 
27
  #### 管理員操作 (Web 後台)
28
  1. **建立授權** - 填入必要資訊:
29
  - 用戶名稱 *
 
30
  - 有效天數 * (7天/30天/90天/1年/永久)
31
  - 電子郵件 (選填)
32
  - 備註 (選填)
33
+ 2. **生成授權碼** - 系統自動產生 32位字符授權碼
34
  3. **提供給用戶** - 將授權碼交付給用戶
35
 
36
+ #### 用戶端操作 (Revit Plugin)
37
+ 1. **輸入授權** - 用戶在 Plugin 輸入管理員提供的授權碼
38
+ 2. **自動綁定** - 首次啟用時系統自動生成並綁定 32位硬體指紋 (CPU + 主機板)
39
+ 3. **即時啟用** - 無需額外操作,立即啟成功
40
+
41
+ #### 持續驗證
42
+ 1. **自動驗證** - Plugin 每次使用時自動驗證授權有效性
43
+ 2. **硬體綁定** - 確保授權只能在綁定的設備上使用
44
+ 3. **離線支援** - 支援 24 小時離線使用 (快取機制)
45
 
46
  ### 🎨 現代化前端
47
  - **零框架依賴**: 純 HTML/CSS/JS,載入迅速
 
51
  - **即時更新**: 無需重新載入的 SPA 體驗
52
 
53
  ### 🛡️ 安全機制
54
+ - **硬體綁定**: 首次啟用自動綁定,防止授權碼被濫用
55
+ - **JWT Token 認證**: 管理後台安全認證
56
+ - **加密通訊**: HTTPS 加密通訊協議
57
+ - **一機一碼**: 每個授權碼只能在一台設備上使
58
 
59
  ### 📊 管理功能
60
  - 用戶授權管理
app/services/license_service.py CHANGED
@@ -52,10 +52,10 @@ class LicenseService:
52
  "license_code": license_code,
53
  "user_name": license_data.user_name,
54
  "user_email": license_data.user_email,
55
- "hardware_id": license_data.hardware_id,
56
  "expires_at": expires_at.isoformat(),
57
  "is_active": True,
58
- "activated_at": datetime.now(timezone.utc).isoformat() if license_data.hardware_id else None
59
  }
60
 
61
  print(f"🔄 Creating license with data: {license_record}")
@@ -119,8 +119,12 @@ class LicenseService:
119
  message="授權已過期"
120
  )
121
 
122
- # 檢查硬體綁定
123
- if license_data["hardware_id"] and license_data["hardware_id"] != activation.hardware_id:
 
 
 
 
124
  await self._log_usage(license_id, "activate", client_ip, error_message="硬體ID不符")
125
  return ActivationResponse(
126
  success=False,
 
52
  "license_code": license_code,
53
  "user_name": license_data.user_name,
54
  "user_email": license_data.user_email,
55
+ "hardware_id": None, # 方案B:創建時不綁定硬體,首次啟用時自動綁定
56
  "expires_at": expires_at.isoformat(),
57
  "is_active": True,
58
+ "activated_at": None # 首次啟用時才設定
59
  }
60
 
61
  print(f"🔄 Creating license with data: {license_record}")
 
119
  message="授權已過期"
120
  )
121
 
122
+ # 檢查硬體綁定狀態 (方案B:首次啟用自動綁定)
123
+ if license_data["activated_at"] is None:
124
+ # 首次啟用:允許綁定到當前硬體
125
+ print(f"🔄 首次啟用授權,綁定硬體ID: {activation.hardware_id}")
126
+ elif license_data["hardware_id"] != activation.hardware_id:
127
+ # 已啟用過,但硬體ID不匹配
128
  await self._log_usage(license_id, "activate", client_ip, error_message="硬體ID不符")
129
  return ActivationResponse(
130
  success=False,
frontend/js/components.js CHANGED
@@ -73,7 +73,7 @@ class Components {
73
  </label>
74
  <input type="text" id="userName" name="userName" class="form-input" required
75
  placeholder="輸入使用者名稱">
76
- <small class="form-help">此名稱將與硬體ID綁定</small>
77
  </div>
78
 
79
  <div class="form-group">
@@ -86,15 +86,6 @@ class Components {
86
  <small class="form-help">選填:用於聯繫和通知</small>
87
  </div>
88
 
89
- <div class="form-group">
90
- <label for="hardwareId" class="form-label">
91
- <i class="fas fa-desktop"></i>
92
- 硬體ID *
93
- </label>
94
- <input type="text" id="hardwareId" name="hardwareId" class="form-input" required
95
- placeholder="從 Revit Plugin 取得的 32 位硬體ID" maxlength="32">
96
- <small class="form-help">用戶從 Revit Plugin 取得的硬體ID (32 位字符)</small>
97
- </div>
98
 
99
  <div class="form-group">
100
  <label for="validDays" class="form-label">
@@ -126,9 +117,9 @@ class Components {
126
  硬體綁定機制說明
127
  </div>
128
  <div class="info-content">
129
- <p>• 系統會自動生成32位硬體ID (CPU ProcessorId + 主機板序號)</p>
130
- <p>• 使用者首次啟用時綁定到特定硬體</p>
131
- <p>• 更換CPU或主機板將導致授權失效,需重新授權</p>
132
  </div>
133
  </div>
134
 
@@ -166,7 +157,6 @@ class Components {
166
 
167
  // 驗證必要欄位
168
  const userName = formData.get('userName')?.trim();
169
- const hardwareId = formData.get('hardwareId')?.trim();
170
  const validDays = formData.get('validDays');
171
 
172
  if (!userName) {
@@ -174,16 +164,6 @@ class Components {
174
  return;
175
  }
176
 
177
- if (!hardwareId) {
178
- Utils.showError('驗證失敗', '請輸入硬體ID');
179
- return;
180
- }
181
-
182
- if (hardwareId.length !== 32) {
183
- Utils.showError('驗證失敗', '硬體ID必須為 32 位字符');
184
- return;
185
- }
186
-
187
  if (!validDays) {
188
  Utils.showError('驗證失敗', '請選擇有效天數');
189
  return;
@@ -193,7 +173,6 @@ class Components {
193
  user_name: userName,
194
  user_email: formData.get('email')?.trim() || null,
195
  expires_days: parseInt(validDays),
196
- hardware_id: hardwareId.toUpperCase(), // 統一轉為大寫
197
  notes: formData.get('notes')?.trim() || null
198
  };
199
 
 
73
  </label>
74
  <input type="text" id="userName" name="userName" class="form-input" required
75
  placeholder="輸入使用者名稱">
76
+ <small class="form-help">此名稱將顯示在授權記錄中</small>
77
  </div>
78
 
79
  <div class="form-group">
 
86
  <small class="form-help">選填:用於聯繫和通知</small>
87
  </div>
88
 
 
 
 
 
 
 
 
 
 
89
 
90
  <div class="form-group">
91
  <label for="validDays" class="form-label">
 
117
  硬體綁定機制說明
118
  </div>
119
  <div class="info-content">
120
+ <p>• 使用者首次輸入授權碼時會自動綁定硬體</p>
121
+ <p>• 系統自動生成32位硬體指紋 (CPU ProcessorId + 主機板序號)</p>
122
+ <p>• 每個授權碼只能在一台設備上啟用使用</p>
123
  </div>
124
  </div>
125
 
 
157
 
158
  // 驗證必要欄位
159
  const userName = formData.get('userName')?.trim();
 
160
  const validDays = formData.get('validDays');
161
 
162
  if (!userName) {
 
164
  return;
165
  }
166
 
 
 
 
 
 
 
 
 
 
 
167
  if (!validDays) {
168
  Utils.showError('驗證失敗', '請選擇有效天數');
169
  return;
 
173
  user_name: userName,
174
  user_email: formData.get('email')?.trim() || null,
175
  expires_days: parseInt(validDays),
 
176
  notes: formData.get('notes')?.trim() || null
177
  };
178