This commit is contained in:
JiXieShi
2024-11-16 23:59:15 +08:00
parent f722153536
commit 87859c7bb8
42 changed files with 2018 additions and 485 deletions

146
sdk/cpp/README.md Normal file
View File

@@ -0,0 +1,146 @@
# Device SDK for C++
## 依赖
- libcurl
- nlohmann/json
- OpenSSL
### Windows
1. vcpkg 安装依赖:
```bash
vcpkg install curl:x64-windows
vcpkg install openssl:x64-windows
vcpkg install nlohmann-json:x64-windows
```
### Linux
1. Ubuntu/Debian:
```bash
sudo apt-get install libcurl4-openssl-dev libssl-dev nlohmann-json-dev
```
2. CentOS/RHEL:
```bash
sudo yum install libcurl-devel openssl-devel nlohmann-json-devel
```
## 编译
### Windows (Visual Studio)
1. 添加包含目录:
- $(vcpkg_root)/installed/x64-windows/include
2. 添加库目录:
- $(vcpkg_root)/installed/x64-windows/lib
3. 添加依赖库:
- libcurl
- libssl
- libcrypto
### Linux
```bash
g++ -o example example.cpp device_sdk.cpp -lcurl -lssl -lcrypto -I/usr/include/nlohmann
```
## 基本用法
### 初始化
```cpp
#include "device_sdk.h"
DeviceClient client("http://localhost:8080", "your-32-byte-encrypt-key-here123456");
```
### 设备注册
```cpp
// 不带授权码注册
DeviceInfo device;
device.uid = "device-001";
device.device_model = "test-model";
std::string error;
if (!client.registerDevice(device, error)) {
std::cerr << "注册失败: " << error << std::endl;
return;
}
// 带授权码注册
DeviceInfo deviceWithLicense;
deviceWithLicense.uid = "device-002";
deviceWithLicense.device_model = "test-model";
deviceWithLicense.license_code = "your-license-code";
if (!client.registerDevice(deviceWithLicense, error)) {
std::cerr << "注册失败: " << error << std::endl;
return;
}
```
### 设备验证
```cpp
ValidateResponse response;
std::string error;
if (!client.validateDevice("device-001", response, error)) {
std::cerr << "验证失败: " << error << std::endl;
return;
}
std::cout << "设备状态: " << response.status << std::endl;
std::cout << "授权类型: " << response.license_type << std::endl;
std::cout << "过期时间: " << response.expire_time << std::endl;
```
### 更新启动次数
```cpp
int count;
std::string error;
if (!client.updateStartCount("device-001", count, error)) {
std::cerr << "更新失败: " << error << std::endl;
return;
}
std::cout << "当前启动次数: " << count << std::endl;
```
### 绑定授权码
```cpp
std::string error;
if (!client.bindLicense("device-001", "license-code-123", error)) {
std::cerr << "绑定失败: " << error << std::endl;
return;
}
```
## 完整示例
```cpp
#include <iostream>
#include "device_sdk.h"
int main() {
try {
DeviceClient client("http://localhost:8080", "your-32-byte-encrypt-key-here123456");
std::string error;
// 注册设备
DeviceInfo device;
device.uid = "device-001";
device.device_model = "test-model";
if (!client.registerDevice(device, error)) {
std::cerr << "注册失败: " << error << std::endl;
return 1;
}
std::cout << "设备注册成功" << std::endl;
// 绑定授权码
if (!client.bindLicense("device-001", "license-code-123", error)) {
std::cerr << "绑定失败: " << error << std::endl;
return 1;
}
std::cout << "授权码绑定成功" << std::endl;
// 验证设备
ValidateResponse response;
if (!client.validateDevice("device-001", response, error)) {
std::cerr << "验证失败: " << error << std::endl;
return 1;
}
std::cout << "设备状态: " << response.status << std::endl;
std::cout << "授权类型: " << response.license_type << std::endl;
// 更新启动次数
int count;
if (!client.updateStartCount("device-001", count, error)) {
std::cerr << "更新失败: " << error << std::endl;
return 1;
}
std::cout << "当前启动次数: " << count << std::endl;
} catch (const std::exception& e) {
std::cerr << "发生错误: " << e.what() << std::endl;
return 1;
}
return 0;
}
```

100
sdk/cpp/device_sdk.cpp Normal file
View File

@@ -0,0 +1,100 @@
#include "device_sdk.h"
#include <sstream>
#include <iomanip>
#include <vector>
DeviceClient::DeviceClient(const std::string& base_url, const std::string& encrypt_key)
: base_url_(base_url), encrypt_key_(encrypt_key) {
curl_global_init(CURL_GLOBAL_ALL);
curl_ = curl_easy_init();
}
DeviceClient::~DeviceClient() {
if (curl_) {
curl_easy_cleanup(curl_);
}
curl_global_cleanup();
}
bool DeviceClient::registerDevice(const DeviceInfo& device_info, std::string& error) {
json request;
request["uid"] = device_info.uid;
request["device_model"] = device_info.device_model;
if (!device_info.license_code.empty()) {
request["license_code"] = device_info.license_code;
}
std::string response;
if (!post(base_url_ + "/api/devices/register", request.dump(), response, error)) {
return false;
}
auto resp_json = json::parse(response);
if (resp_json["code"] != 0) {
error = resp_json["error"];
return false;
}
return true;
}
bool DeviceClient::validateDevice(const std::string& uid, ValidateResponse& response, std::string& error) {
std::string encrypted_response;
if (!get(base_url_ + "/api/devices/" + uid + "/validate", encrypted_response, error)) {
return false;
}
auto resp_json = json::parse(encrypted_response);
if (resp_json["code"] != 0) {
error = resp_json["error"];
return false;
}
std::string decrypted = decryptResponse(resp_json["data"]);
auto data = json::parse(decrypted);
response.status = data["status"];
response.license_type = data["license_type"];
response.expire_time = data["expire_time"];
response.start_count = data["start_count"];
response.max_uses = data["max_uses"];
response.timestamp = data["timestamp"];
return true;
}
bool DeviceClient::updateStartCount(const std::string& uid, int& count, std::string& error) {
std::string response;
if (!post(base_url_ + "/api/devices/" + uid + "/start", "", response, error)) {
return false;
}
auto resp_json = json::parse(response);
if (resp_json["code"] != 0) {
error = resp_json["error"];
return false;
}
count = resp_json["data"]["start_count"];
return true;
}
bool DeviceClient::bindLicense(const std::string& uid, const std::string& license_code, std::string& error) {
json request;
request["license_code"] = license_code;
std::string response;
if (!post(base_url_ + "/api/devices/" + uid + "/license", request.dump(), response, error)) {
return false;
}
auto resp_json = json::parse(response);
if (resp_json["code"] != 0) {
error = resp_json["error"];
return false;
}
return true;
}
// ... HTTP 请求和加密相关的辅助函数实现 ...

61
sdk/cpp/device_sdk.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef DEVICE_SDK_H
#define DEVICE_SDK_H
#ifdef _WIN32
#include <windows.h>
#include <winhttp.h>
#else
#include <curl/curl.h>
#endif
#include <string>
#include <memory>
#include <ctime>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
struct DeviceInfo {
std::string uid;
std::string device_model;
std::string license_code; // 可选
};
struct ValidateResponse {
std::string status;
std::string license_type;
std::string expire_time;
int start_count;
int max_uses;
int64_t timestamp;
};
class DeviceClient {
public:
DeviceClient(const std::string& base_url, const std::string& encrypt_key);
~DeviceClient();
// 设备注册
bool registerDevice(const DeviceInfo& device_info, std::string& error);
// 设备验证
bool validateDevice(const std::string& uid, ValidateResponse& response, std::string& error);
// 更新启动次数
bool updateStartCount(const std::string& uid, int& count, std::string& error);
// 绑定授权码
bool bindLicense(const std::string& uid, const std::string& license_code, std::string& error);
private:
std::string base_url_;
std::string encrypt_key_;
CURL* curl_;
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp);
bool post(const std::string& url, const std::string& data, std::string& response, std::string& error);
bool get(const std::string& url, std::string& response, std::string& error);
std::string decryptResponse(const std::string& encrypted);
};
#endif // DEVICE_SDK_H

220
sdk/csharp/DeviceSDK.cs Normal file
View File

@@ -0,0 +1,220 @@
using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace DeviceSDK
{
public class DeviceClient : IDisposable
{
private readonly string _baseUrl;
private readonly string _encryptKey;
private readonly HttpClient _httpClient;
private bool _disposed;
public DeviceClient(string baseUrl, string encryptKey)
{
_baseUrl = baseUrl.TrimEnd('/');
_encryptKey = encryptKey;
_httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(30)
};
}
// 设备注册
public async Task<DeviceRegisterResponse> RegisterDeviceAsync(DeviceRegisterRequest request)
{
try
{
var response = await PostAsync("/api/devices/register", request);
return JsonSerializer.Deserialize<DeviceRegisterResponse>(response);
}
catch (Exception ex)
{
throw new DeviceSDKException("设备注册失败", ex);
}
}
// 设备验证
public async Task<DeviceValidateResponse> ValidateDeviceAsync(string uid)
{
try
{
var response = await GetAsync($"/api/devices/{uid}/validate");
var result = JsonSerializer.Deserialize<ApiResponse>(response);
if (result.Code != 0)
{
throw new DeviceSDKException(result.Error);
}
var decrypted = DecryptResponse(result.Data);
return JsonSerializer.Deserialize<DeviceValidateResponse>(decrypted);
}
catch (Exception ex)
{
throw new DeviceSDKException("设备验证失败", ex);
}
}
// 更新启动次数
public async Task<StartCountResponse> UpdateStartCountAsync(string uid)
{
try
{
var response = await PostAsync($"/api/devices/{uid}/start", null);
return JsonSerializer.Deserialize<StartCountResponse>(response);
}
catch (Exception ex)
{
throw new DeviceSDKException("更新启动次数失败", ex);
}
}
// 绑定授权码
public async Task BindLicenseAsync(string uid, string licenseCode)
{
try
{
var request = new { license_code = licenseCode };
var response = await PostAsync($"/api/devices/{uid}/license", request);
var result = JsonSerializer.Deserialize<ApiResponse>(response);
if (result.Code != 0)
{
throw new DeviceSDKException(result.Error);
}
}
catch (Exception ex)
{
throw new DeviceSDKException("绑定授权码失败", ex);
}
}
private async Task<string> GetAsync(string path)
{
var response = await _httpClient.GetAsync(_baseUrl + path);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
private async Task<string> PostAsync(string path, object data)
{
var content = data == null ? null :
new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(_baseUrl + path, content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
private string DecryptResponse(string encrypted)
{
try
{
var cipherBytes = Convert.FromBase64String(encrypted);
var keyBytes = Encoding.UTF8.GetBytes(_encryptKey);
using var aes = Aes.Create();
aes.Key = keyBytes;
var iv = new byte[16];
Array.Copy(cipherBytes, 0, iv, 0, 16);
aes.IV = iv;
using var decryptor = aes.CreateDecryptor();
var cipher = new byte[cipherBytes.Length - 16];
Array.Copy(cipherBytes, 16, cipher, 0, cipher.Length);
var plainBytes = decryptor.TransformFinalBlock(cipher, 0, cipher.Length);
return Encoding.UTF8.GetString(plainBytes);
}
catch (Exception ex)
{
throw new DeviceSDKException("解密响应失败", ex);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_httpClient?.Dispose();
}
_disposed = true;
}
}
}
// 请求和响应模型
public class DeviceRegisterRequest
{
public string Uid { get; set; }
public string DeviceModel { get; set; }
public string LicenseCode { get; set; }
}
public class DeviceRegisterResponse
{
public int Code { get; set; }
public string Message { get; set; }
public DeviceRegisterData Data { get; set; }
}
public class DeviceRegisterData
{
public string Uid { get; set; }
public string DeviceModel { get; set; }
public string Status { get; set; }
}
public class DeviceValidateResponse
{
public string Status { get; set; }
public string LicenseType { get; set; }
public DateTime ExpireTime { get; set; }
public int StartCount { get; set; }
public int MaxUses { get; set; }
public long Timestamp { get; set; }
}
public class StartCountResponse
{
public int Code { get; set; }
public string Message { get; set; }
public StartCountData Data { get; set; }
}
public class StartCountData
{
public int StartCount { get; set; }
public string Status { get; set; }
public DateTime LastActiveAt { get; set; }
}
internal class ApiResponse
{
public int Code { get; set; }
public string Error { get; set; }
public string Data { get; set; }
}
// 自定义异常
public class DeviceSDKException : Exception
{
public DeviceSDKException(string message) : base(message) { }
public DeviceSDKException(string message, Exception innerException)
: base(message, innerException) { }
}
}

90
sdk/csharp/README.md Normal file
View File

@@ -0,0 +1,90 @@
# Device SDK for C#
## 安装
`DeviceSDK.cs` 添加到你的项目中。
## 初始化
```csharp
var client = new DeviceClient(
baseUrl: "http://your-server:8080", // 服务器地址
encryptKey: "your-32-byte-encrypt-key-here123456" // 32字节加密密钥
);
```
## API 使用说明
### 1. 设备注册
```csharp
// 基本注册(无授权码)
var response = await client.RegisterDeviceAsync(new DeviceRegisterRequest {
Uid = "device-001",
DeviceModel = "test-model"
});```
```csharp
// 带授权码的注册
var response = await client.RegisterDeviceAsync(new DeviceRegisterRequest {
Uid = "device-001",
DeviceModel = "test-model",
License = "your-license-code-here"
});
```
### 2. 设备验证
```csharp
var validateResponse = await client.ValidateDeviceAsync("device-001");
Console.WriteLine($"设备状态: {validateResponse.Status}");
Console.WriteLine($"授权类型: {validateResponse.LicenseType}");
Console.WriteLine($"过期时间: {validateResponse.ExpireTime}");
```
### 3. 更新启动次数
```csharp
var startCountResponse = await client.UpdateStartCountAsync("device-001");
Console.WriteLine($"当前启动次数: {startCountResponse.Data.StartCount}");
```
### 4. 绑定授权码
```csharp
var bindLicenseResponse = await client.BindLicenseAsync("device-001", "your-license-code-here");
Console.WriteLine($"绑定结果: {bindLicenseResponse.Data.Success}");
```
## 错误处理
SDK 使用 `DeviceSDKException` 统一处理错误:
```csharp
try {
// 调用 API 的代码
} catch (DeviceSDKException e) {
Console.WriteLine($"错误码: {e.ErrorCode}, 错误信息: {e.Message}");
}
```
## 响应数据结构
```csharp
// 设备验证响应
public class DeviceValidateResponse
{
public string Status { get; set; } // 设备状态
public string LicenseType { get; set; } // 授权类型
public DateTime ExpireTime { get; set; } // 过期时间
public int StartCount { get; set; } // 启动次数
public int MaxUses { get; set; } // 最大使用次数
}
// 启动次数响应
public class StartCountResponse
{
public int Code { get; set; }
public string Message { get; set; }
public StartCountData Data { get; set; }
}
```

View File

@@ -0,0 +1 @@

104
sdk/go/README.md Normal file
View File

@@ -0,0 +1,104 @@
# Device SDK for Go
## 安装
`go import "license-server/sdk/go/devicesdk"`
## 基本用法
### 初始化客户端
```go
client := devicesdk.NewDeviceClient(
"http://localhost:8080",
"your-32-byte-encrypt-key-here123456",
)
```
### 设备注册
```go
// 不带授权码注册
err := client.RegisterDevice(&devicesdk.DeviceInfo{
UID: "device-001",
DeviceModel: "test-model",
})
if err != nil {
log.Fatalf("设备注册失败: %v", err)
}
// 带授权码注册
err = client.RegisterDevice(&devicesdk.DeviceInfo{
UID: "device-002",
DeviceModel: "test-model",
LicenseCode: "your-license-code", // 可选
})
if err != nil {
log.Fatalf("设备注册失败: %v", err)
}
```
### 设备验证
```go
validateResp, err := client.ValidateDevice("device-001")
if err != nil {
log.Fatalf("设备验证失败: %v", err)
}
fmt.Printf("设备状态: %s\n", validateResp.Status)
fmt.Printf("授权类型: %s\n", validateResp.LicenseType)
fmt.Printf("过期时间: %s\n", validateResp.ExpireTime)
```
### 更新启动次数
```go
count, err := client.UpdateStartCount("device-001")
if err != nil {
log.Fatalf("更新启动次数失败: %v", err)
}
fmt.Printf("当前启动次数: %d\n", count)
```
### 绑定授权码
```go
err := client.BindLicense("device-001", "your-license-code")
if err != nil {
log.Fatalf("绑定授权码失败: %v", err)
}
```
## 完整示例
```go
package main
import (
"fmt"
"log"
"license-server/sdk/go/devicesdk"
)
func main() {
client := devicesdk.NewDeviceClient(
"http://localhost:8080",
"your-32-byte-encrypt-key-here123456",
)
// 注册设备
err := client.RegisterDevice(&devicesdk.DeviceInfo{
UID: "device-001",
DeviceModel: "test-model",
})
if err != nil {
log.Fatalf("设备注册失败: %v", err)
}
fmt.Println("设备注册成功")
// 绑定授权码
err = client.BindLicense("device-001", "license-code-123")
if err != nil {
log.Fatalf("绑定授权码失败: %v", err)
}
fmt.Println("授权码绑定成功")
// 验证设备
validateResp, err := client.ValidateDevice("device-001")
if err != nil {
log.Fatalf("设备验证失败: %v", err)
}
fmt.Printf("设备状态: %s\n", validateResp.Status)
fmt.Printf("授权类型: %s\n", validateResp.LicenseType)
fmt.Printf("过期时间: %s\n", validateResp.ExpireTime)
// 更新启动次数
count, err := client.UpdateStartCount("device-001")
if err != nil {
log.Fatalf("更新启动次数失败: %v", err)
}
fmt.Printf("当前启动次数: %d\n", count)
}
```

191
sdk/go/device_sdk.go Normal file
View File

@@ -0,0 +1,191 @@
package devicesdk
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
type DeviceClient struct {
baseURL string
encryptKey string
client *http.Client
}
type DeviceInfo struct {
UID string `json:"uid"`
DeviceModel string `json:"device_model"`
LicenseCode string `json:"license_code,omitempty"`
}
type ValidateResponse struct {
Status string `json:"status"`
LicenseType string `json:"license_type"`
ExpireTime time.Time `json:"expire_time"`
StartCount int `json:"start_count"`
MaxUses int `json:"max_uses"`
Timestamp int64 `json:"timestamp"`
}
type ApiResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Error string `json:"error"`
Data interface{} `json:"data"`
}
func NewDeviceClient(baseURL, encryptKey string) *DeviceClient {
return &DeviceClient{
baseURL: baseURL,
encryptKey: encryptKey,
client: &http.Client{Timeout: time.Second * 30},
}
}
func (c *DeviceClient) RegisterDevice(info *DeviceInfo) error {
data, err := json.Marshal(info)
if err != nil {
return fmt.Errorf("序列化请求数据失败: %v", err)
}
resp, err := c.post("/api/devices/register", data)
if err != nil {
return err
}
if resp.Code != 0 {
return fmt.Errorf(resp.Error)
}
return nil
}
func (c *DeviceClient) ValidateDevice(uid string) (*ValidateResponse, error) {
resp, err := c.get(fmt.Sprintf("/api/devices/%s/validate", uid))
if err != nil {
return nil, err
}
if resp.Code != 0 {
return nil, fmt.Errorf(resp.Error)
}
decrypted, err := c.decryptResponse(resp.Data.(string))
if err != nil {
return nil, err
}
var validateResp ValidateResponse
if err := json.Unmarshal([]byte(decrypted), &validateResp); err != nil {
return nil, fmt.Errorf("解析响应数据失败: %v", err)
}
return &validateResp, nil
}
func (c *DeviceClient) UpdateStartCount(uid string) (int, error) {
resp, err := c.post(fmt.Sprintf("/api/devices/%s/start", uid), nil)
if err != nil {
return 0, err
}
if resp.Code != 0 {
return 0, fmt.Errorf(resp.Error)
}
data := resp.Data.(map[string]interface{})
return int(data["start_count"].(float64)), nil
}
func (c *DeviceClient) BindLicense(uid, licenseCode string) error {
data, err := json.Marshal(map[string]string{
"license_code": licenseCode,
})
if err != nil {
return fmt.Errorf("序列化请求数据失败: %v", err)
}
resp, err := c.post(fmt.Sprintf("/api/devices/%s/license", uid), data)
if err != nil {
return err
}
if resp.Code != 0 {
return fmt.Errorf(resp.Error)
}
return nil
}
// HTTP 请求辅助方法
func (c *DeviceClient) get(path string) (*ApiResponse, error) {
resp, err := c.client.Get(c.baseURL + path)
if err != nil {
return nil, fmt.Errorf("HTTP请求失败: %v", err)
}
defer resp.Body.Close()
return c.parseResponse(resp)
}
func (c *DeviceClient) post(path string, data []byte) (*ApiResponse, error) {
var resp *http.Response
var err error
if data == nil {
resp, err = c.client.Post(c.baseURL+path, "application/json", nil)
} else {
resp, err = c.client.Post(c.baseURL+path, "application/json", bytes.NewBuffer(data))
}
if err != nil {
return nil, fmt.Errorf("HTTP请求失败: %v", err)
}
defer resp.Body.Close()
return c.parseResponse(resp)
}
func (c *DeviceClient) parseResponse(resp *http.Response) (*ApiResponse, error) {
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应数据失败: %v", err)
}
var apiResp ApiResponse
if err := json.Unmarshal(body, &apiResp); err != nil {
return nil, fmt.Errorf("解析响应数据失败: %v", err)
}
return &apiResp, nil
}
func (c *DeviceClient) decryptResponse(encrypted string) (string, error) {
ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
if err != nil {
return "", fmt.Errorf("base64解码失败: %v", err)
}
block, err := aes.NewCipher([]byte(c.encryptKey))
if err != nil {
return "", fmt.Errorf("创建AES cipher失败: %v", err)
}
if len(ciphertext) < aes.BlockSize {
return "", fmt.Errorf("密文长度不足")
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(ciphertext, ciphertext)
return string(ciphertext), nil
}