Mục lục thư viện
Thư viện tài liệuBắt đầu theo ngôn ngữ
Bắt đầu với C# và .NET
Bài này dành cho phần mềm WinForms, WPF, console hay dịch vụ chạy trên .NET 8 trở lên: cài SDK, vài dòng mã, chạy thử, xem kết quả trên dashboard, xử lý lỗi và phát hành. Unity và .NET Framework dùng thư viện C, có bài riêng: [Bắt đầu với Unity](/docs/start-unity).
6 phút đọc
Cần có
- .NET 8 SDK trở lên. Gói chạy trên .NET 8 và các bản mới hơn, kéo theo hai gói từ nuget.org:
BouncyCastle.CryptographyvàSystem.Security.Cryptography.ProtectedData. - Một ứng dụng trên dashboard. Trang Tích hợp › Kết nối phần mềm của nó có địa chỉ API, App ID, khoá công khai và nút Tạo key thử nghiệm. Địa chỉ API kết thúc bằng
/api, không có/v1:https://<trang ClavisAUH của bạn>/api.
1. Cài SDK
Tải ClavisAuh.0.1.0.nupkg ở trang Tải về, kiểm SHA-256 như trang đó hướng dẫn, rồi đặt file vào thư mục packages cạnh file .csproj của bạn. Tạo nuget.config cũng cạnh file .csproj:
<configuration>
<packageSources>
<add key="clavisauh" value="packages" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>
Rồi thêm gói:
dotnet add package ClavisAuh --version 0.1.0
Đường dẫn trong nuget.config tính từ chỗ đặt file đó, và Visual Studio đọc cùng file này. nuget.org cấp hai gói đi kèm, nên lần restore đầu cần mạng. Nếu file bạn tải có số phiên bản mới hơn, đổi số trong lệnh cho khớp. SDK chưa có trên NuGet; khi có, bài này sẽ ghi thêm cách cài từ đó.
2. Mã tối thiểu
Các khối dưới đây là từng phần của một file; file đầy đủ, kèm hàm phụ, ở bước 3.
Tạo client một lần khi phần mềm khởi động. Địa chỉ API, App ID và khoá công khai là ba giá trị ở trang Kết nối phần mềm: mẫu đọc chúng từ biến môi trường, phần mềm của bạn thì ghi thẳng vào mã. Khoá thiết bị nằm trong một file, trên Windows được niêm phong bằng DPAPI cho người dùng hiện tại; phiên và token offline gắn với khoá này, còn suất trên license đi theo các định danh đã băm của máy. Mẫu đặt phần này trong Settings.cs để năm kịch bản dùng chung, cùng các hàm phụ đọc cài đặt và báo lỗi:
using ClavisAuh;
namespace ClavisSamples;
/// <summary>What every scenario reads from the environment, and the client they all start from.</summary>
internal static class Settings
{
public static string Env(string name) =>
Environment.GetEnvironmentVariable(name) ?? throw new InvalidOperationException($"{name} is not set");
public static string? Optional(string name) => Environment.GetEnvironmentVariable(name);
public static string Lang => Optional("CLAVIS_LANG") ?? "en";
public static string State => Optional("CLAVIS_STATE_DIR") ?? ".clavis";
public static ClavisClient Client(string? appVersion = null) => new(new ClavisOptions
{
BaseUrl = Env("CLAVIS_BASE_URL"),
AppId = Env("CLAVIS_APP_ID"),
AppPublicKeys = [Env("CLAVIS_APP_KEY")],
Device = ClavisDevice.Default(),
// The same key every run: sessions and offline tokens stay with this machine.
DeviceKey = ClavisDevice.LoadOrCreateKey(Path.Combine(State, "device.key")),
AppVersion = appVersion,
});
/// <summary>The sentence for the person using the software, then a line for the CI to read.</summary>
public static int Fail(ClavisException e)
{
Console.WriteLine(e.UserMessage(Lang));
Console.WriteLine($"FAIL {e.Code}");
return 1;
}
}
Kích hoạt key của khách. Khi không có mạng, token offline lưu lần trước giữ phần mềm chạy tiếp:
JsonObject? grant = null;
try
{
await client.InitAsync();
grant = await client.ActivateAsync(Settings.Env("CLAVIS_LICENSE"));
}
catch (ClavisException e)
{
// No answer (network_error, timeout...): a token saved last time keeps the software working.
var offline = e.Retryable && File.Exists(saved) ? client.VerifyOffline(File.ReadAllText(saved)) : null;
if (offline is null) return Settings.Fail(e);
Console.WriteLine("Offline: using the license saved on this machine");
}
Khoá công khai chỉ dùng để kiểm chữ ký, nhúng vào phần mềm là an toàn. Đừng bao giờ nhúng một khoá bí mật nào.
3. Chạy thử
Cách nhanh nhất là dự án mẫu, đã nối sẵn SDK và cài đặt của ứng dụng:
- Ở trang Kết nối phần mềm, chọn C#, bấm Tạo key thử nghiệm rồi Tải dự án mẫu (.zip), và giải nén.
- Trong thư mục vừa giải nén, chạy
.\run.cmd <key thử nghiệm>(Windows) hoặc./run.sh <key thử nghiệm>(Linux, macOS). Script chạydotnet run --project ClavisSamples -c Release -- k1.
Muốn chạy trong dự án của bạn: thêm Settings.cs ở trên và nguyên K1Key.cs dưới đây vào một dự án console đã có gói, rồi cho Program.cs đúng một dòng return await ClavisSamples.K1Key.RunAsync();. Đặt bốn cài đặt lấy ở trang Kết nối phần mềm (CLAVIS_APP_KEY là khoá công khai, không phải bí mật), rồi dotnet run:
$env:CLAVIS_BASE_URL = "<địa chỉ API>"
$env:CLAVIS_APP_ID = "<App ID>"
$env:CLAVIS_APP_KEY = "<khoá công khai>"
$env:CLAVIS_LICENSE = "<key thử nghiệm>"
export CLAVIS_BASE_URL="<địa chỉ API>" CLAVIS_APP_ID="<App ID>" CLAVIS_APP_KEY="<khoá công khai>" CLAVIS_LICENSE="<key thử nghiệm>"
Mẫu in License active until <ngày>, rồi OK k1 active ở dòng cuối. Chạy lại trên cùng máy vẫn thành công và không tốn thêm suất: server nhận ra máy qua các định danh đã băm (Thiết bị, HWID và chuyển máy). Khoá thiết bị, dùng để ký yêu cầu, nằm trong thư mục .clavis dưới thư mục bạn chạy lệnh (hoặc trong CLAVIS_STATE_DIR). Container tạo mới cho mỗi lần chạy thì lần nào cũng giống một máy mới, xem Câu hỏi thường gặp. Toàn bộ kịch bản K1 trong dự án mẫu:
using System.Text.Json.Nodes;
using ClavisAuh;
namespace ClavisSamples;
/// <summary>K1 · License key only: activate on this machine, keep the session alive, work offline for a while.</summary>
internal static class K1Key
{
public static async Task<int> RunAsync()
{
var client = Settings.Client("1.0.0");
var saved = Path.Combine(Settings.State, "offline.token");
JsonObject? grant = null;
try
{
await client.InitAsync();
grant = await client.ActivateAsync(Settings.Env("CLAVIS_LICENSE"));
}
catch (ClavisException e)
{
// No answer (network_error, timeout...): a token saved last time keeps the software working.
var offline = e.Retryable && File.Exists(saved) ? client.VerifyOffline(File.ReadAllText(saved)) : null;
if (offline is null) return Settings.Fail(e);
Console.WriteLine("Offline: using the license saved on this machine");
}
if (grant is not null)
{
var license = grant["license"]!;
Console.WriteLine($"License {license["status"]} until {license["expiresAt"]?.ToString() ?? "forever"}");
// Every few minutes while the software runs; sessions end after an hour without one.
await client.HeartbeatAsync();
if (client.OfflineToken is { } token) File.WriteAllText(saved, token);
await client.LogoutAsync();
}
Console.WriteLine($"OK k1 {(grant is null ? "offline" : grant["license"]!["status"])}");
return 0;
}
}
4. Thấy trên dashboard
- Tích hợp › Kết nối phần mềm: bước 4 chuyển sang Đã kết nối, kèm
csharp/0.1.0và hệ điều hành của máy. - Phát hành & bản quyền › License: key chuyển từ Chưa dùng sang Đang dùng.
- Người dùng cuối › Thiết bị & phiên: máy vừa chạy, cùng phiên nó vừa mở.
5. Xử lý lỗi
Mọi lần bị từ chối là một ClavisException có ba thứ bạn cần:
e.Code: mã trong bảng Mã lỗi, ví dụinvalid_credentialshaydevice_limit.e.UserMessage("vi"): câu cho người dùng phần mềm của bạn, tiếng Việt hoặc"en", hiện thẳng được.e.Retryable:truekhi không nhận được câu trả lời (mất mạng, quá chậm, máy chủ bận); để một lúc rồi thử lại có thể thành công.
Câu trả lời không mang đúng chữ ký của ứng dụng bị từ chối bằng ClavisVerificationException (cũng là một ClavisException), với mã unsigned_response, invalid_response_signature, unknown_key hoặc stale_response. Hãy coi đó như không có câu trả lời; thường là địa chỉ API hoặc khoá công khai trong phần mềm bị sai.
Khi phần mềm đang chạy, gửi heartbeat đều đặn để giữ phiên, khoảng mỗi 5 tới 30 phút: phiên hết sau 60 phút không có heartbeat.
// Every few minutes while the software runs; sessions end after an hour without one.
await client.HeartbeatAsync();
Lưu token offline mới nhất, để lần sau không có mạng vẫn mở được:
if (client.OfflineToken is { } token) File.WriteAllText(saved, token);
Mọi gói đều có token offline, mặc định dùng được 7 ngày; Premium tự chọn thời hạn, còn Chế độ chỉ online thì tắt hẳn. Xem License offline.
6. Đóng gói và phát hành
- Ghi địa chỉ API, App ID và khoá công khai vào mã hoặc vào file cấu hình đi cùng bản build. Máy khách không cần biến môi trường nào.
- Đặt thư mục trạng thái (khoá thiết bị, token offline) trong
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), thêm tên phần mềm của bạn. Đừng đặt trong thư mục cài đặt. - Truyền
AppVersionđúng phiên bản bạn phát hành: server dùng nó để chặn bản quá cũ và báo có bản mới, xem Bản phát hành có ký và cập nhật. - Trong WinForms và WPF, gọi các hàm
…Asyncbằngawaittrong sự kiện của giao diện. Đừng chặn bằng.Resulthay.Wait(): giao diện sẽ đứng trong lúc chờ mạng. dotnet publish -c Releaseđóng gói SDK cùng phần mềm như mọi gói NuGet. Chạy thử bản publish tớiOK k1 activetrên một máy sạch trước khi phát hành.- Trước khi bán, bật Đã phát hành công khai ở Tích hợp › Cài đặt ứng dụng. Mọi key TEST ngừng hoạt động, nên key thử nào lỡ lọt ra ngoài cũng không dùng được nữa.
Câu hỏi thường gặp
Phần mềm có cần mạng mỗi lần mở không? Kích hoạt và heartbeat cần mạng. Khi mất mạng, token đã lưu giữ phần mềm chạy tiếp, mặc định 7 ngày hoặc theo thời hạn chọn trên Premium, được kiểm ngay trên máy mà không cần server.
Khách cài lại hệ điều hành hay đổi máy thì sao? Cài lại trên cùng máy, server vẫn nhận ra máy nhờ dấu nhận diện phần cứng đã băm. Máy mới chiếm một suất mới; hết suất thì khách nhận device_limit, và bạn Đặt lại thiết bị trong trang License. Xem Thiết bị, HWID và chuyển máy.
Giới hạn tính năng theo gói license thế nào? client.Has("ten_tinh_nang") cho biết license có quyền tính năng đó không; tạo quyền trong Gói license và quyền tính năng.
Phần mềm của tôi còn chạy .NET Framework 4.x? SDK này cần .NET 8. Với .NET Framework, gọi thư viện C qua P/Invoke đúng như bài Bắt đầu với Unity: cùng mã, không phụ thuộc Unity.