feat: add native/Java auth SDK, docs, CI, and examples

Made-with: Cursor
This commit is contained in:
hpd840321
2026-04-06 17:42:09 +08:00
commit 3894315759
35 changed files with 4825 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
name: ci-java
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
maven:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
cache: maven
- name: Build native (.so)
run: |
sudo apt-get update
sudo apt-get install -y build-essential cmake
cmake -S native -B native/build -DCMAKE_BUILD_TYPE=Release
cmake --build native/build --parallel
- name: Maven verify
run: mvn -f java/pom.xml -B verify
env:
LD_LIBRARY_PATH: ${{ github.workspace }}/native/build
+40
View File
@@ -0,0 +1,40 @@
name: ci-native
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- name: Set up JDK (JNI)
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: Configure & build (Unix)
if: runner.os != 'Windows'
run: |
cmake -S native -B native/build -DCMAKE_BUILD_TYPE=Release
cmake --build native/build --parallel
- name: Configure & build (Windows)
if: runner.os == 'Windows'
run: |
cmake -S native -B native/build -DCMAKE_BUILD_TYPE=Release
cmake --build native/build --config Release --parallel
- name: Test (Unix)
if: runner.os != 'Windows'
working-directory: native/build
run: ctest --output-on-failure
- name: Test (Windows)
if: runner.os == 'Windows'
working-directory: native/build
run: ctest -C Release --output-on-failure
+16
View File
@@ -0,0 +1,16 @@
name: release
on:
push:
tags:
- "v*.*.*"
jobs:
placeholder:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Tag pushed
run: |
echo "Release workflow placeholder — attach native ZIP & Maven publish in later milestone."
echo "Ref ${GITHUB_REF}"
+41
View File
@@ -0,0 +1,41 @@
# Build
**/build/
**/target/
**/out/
cmake-build-*/
# Java
*.class
*.jar
*.war
*.iml
.idea/
.settings/
.project
.classpath
# Native
*.o
*.obj
*.so
*.dylib
*.dll
*.a
*.lib
*.exp
*.pdb
# OS / IDE
.DS_Store
*.swp
*.swo
.vscode/
# Maven
dependency-reduced-pom.xml
# Python
__pycache__/
*.py[cod]
.venv/
venv/
View File
+147
View File
@@ -0,0 +1,147 @@
# 比特授权云:机制与授权模式梳理
> 依据公开文档站点整理,主要页面入口:[欢迎页](https://doc.bitanswer.cn/docs/welcome/)、[授权模式分类](https://doc.bitanswer.cn/category/docs/license-models/)、[客户端 API](https://doc.bitanswer.cn/category/docs/client-api/)、[授权设计与创建](https://doc.bitanswer.cn/docs/function/licensing-design-creation/)、[基本授权过程](https://doc.bitanswer.cn/docs/application-development/development-process/)、[SDK 概述](https://doc.bitanswer.cn/docs/client-api/sdk-overview/)。
> 整理日期:2026-04-06
---
## 1. 总体机制:控制台与产品模型
比特授权云将「卖什么、怎么控、怎么发到客户端」拆成多层概念:
| 概念 | 作用 |
| ---------------- | -------------------------------------------------------------- |
| **产品(Product** | 对应一套或一组软件;内建 **特征项(功能/加密点)****配置项(小型安全存储,键值字符串)**。 |
| **模版(Template** | 产品特征项的**子集**,常对应一个发行版本(如「简单版 / 专业版」)。 |
| **业务(Business** | 定义**商业模式 + 授权类型**(云、集团、单机浮动、演示、帐号等)及各类**授权属性**(有效期、终端数、离线策略等)。 |
| **授权码 SN** | 16 位全球唯一标识;用户付费后发放;客户端用 SN 做激活、续期、升级。 |
集成侧:**库文件与头文件与产品绑定**,混用会导致 `**Login` 返回 0x105**(文档明确说明)。
典型流程:规划功能与模块 → 控制台建立产品与模版 → 下载对应开发模块 → 建立业务 → 生成并分发授权码 → 客户端激活与运行时校验。
### 1.1 保护方式与平台
- **保护方式**[欢迎页](https://doc.bitanswer.cn/docs/welcome/) 说明提供 **API****外壳** 两种;可组合使用以提高安全性。
- **平台**Windows32/64)、Linux(含 ARM/x86)、macOS、Android、iOS 等;其它语言可通过动态库集成。
---
## 2. SDK 侧:与模式无关的共性(运行时语义)
来源:[SDK 概述](https://doc.bitanswer.cn/docs/client-api/sdk-overview/)
| 名词 | 含义 |
| --------------------- | ----------------------------------------- |
| **资源** | 需要保护或控制的功能或数据单元。 |
| **授权** | 将资源访问权下放给设备或用户。 |
| **激活** | 将设备/用户与权限绑定(典型:在线更新许可,再登录)。 |
| **授权升级** | 增加或减少资源访问权限。 |
| **授权检查** | 检查是否拥有某资源权限。 |
| **授权文件 / License 文件** | **单机类**授权保存在设备本地的许可数据。 |
| **特征项** | 与程序中「加密点 / 功能开关」对应;可有版本;类型包括只读、读写、算法、密钥等。 |
| **内存授权** | 将已有 License 传入 API 校验,**无激活过程**。 |
文档强调:在**集团授权**场景下,**QueryFeature** 与 **ReleaseFeature** 应**成对**调用(占用与释放模块用户数)。
示例主流程(文档 C 示例):`Bit_Login(..., BIT_MODE_AUTO)` → 失败则输入 SN → `Bit_UpdateOnline` 激活 → 再 `Bit_Login` → 结束 `Bit_Logout`
---
## 3. 授权模式分述
文档中「本地授权」与「云授权」的区分要点:[云授权](https://doc.bitanswer.cn/docs/license-models/cloud-license/) 页指出,除云授权外,其它方式通常需向客户端发放与**硬件指纹绑定**的电子许可证,激活后往往**不必**再连授权服务器(云授权则运行时实时连网、授权数据不落客户端)。
### 3.1 云授权
- **文档**[云授权](https://doc.bitanswer.cn/docs/license-models/cloud-license/)
- **许可数据**:始终在云端;客户端不保存完整授权数据。
- **标识**:**授权码 SN**;文档描述为同一 SN 同一时间仅一台计算机使用。验证时上传 **SN + 本机硬件指纹**
- **安全会话**:验证通过后握手,用产品预置密钥派生**会话密钥**,后续通讯加密(文档类比网银并强调更复杂)。
- **心跳**:约 **每 20 分钟**与服务器心跳以保持会话。
- **多进程**:同一客户端同一 SN 最多 **10 个进程**;控制台可管理在线状态。
- **管控**:授权属性变更可对在线客户端快速生效。
- **局限**:运行期通常需联网;文档提及 **云授权 2.0** 改善依赖并支持高优先级登录、短时离线等。
- **业务属性**:使用时间、次数、有效期;可设终端限制等。
### 3.2 单机浮动授权
- **文档**[单机浮动授权](https://doc.bitanswer.cn/docs/license-models/local-license/)
- **运行期**:激活后一般**不连**外部授权服务器即可使用(与云授权对比)。
- **激活**:客户端提交 **SN + 硬件指纹**,服务器验证后下发与本机绑定的**本地许可**;复制到其它机器不可用。
- **许可存放**:本机;对比——集团授权在**集团服务器**,云授权在**云端**。
- **常见业务属性**
- **离线 vs 智能连接**:完全离线,或联网时后台自动校验/升级且不打断用户。
- **强制认证**:最大离线时长,到期必须再连服务器。
- **安装限制**:激活次数上限(多客户端**共享**该次数池)。
- **终端限制**:允许多少台机器激活。
- **失效与恢复**:许可损坏、硬盘问题或硬件变更可致失效;可按规则重新下载许可(可能消耗安装次数或终端名额)。
### 3.3 集团授权
- **文档**[集团授权](https://doc.bitanswer.cn/docs/license-models/group-license/)
- **形态**:开发商**自建授权服务器**,对局域网或互联网用户做**并发**控制。
- **控制台**:创建业务时选择「集团授权」;可配置**用户数(并发上限)**,默认描述为最多 **999** 并发。
- **服务端组件**(需单独下载安装):
- 集团服务安装程序 `bit_service.exe`Windows 服务),默认端口 **8273**(通讯)、**8274**Web 管理)。
- **集团服务扩展模块(.EXT)**:与**产品绑定**,不可混用。
- **集团授权管理中心**:浏览器访问,默认示例 `http://localhost:8274`
- **客户端**:需配置集团服务器地址(API 或配置文件),参见 [部署和配置客户端](https://doc.bitanswer.cn/docs/application-development/application-distribution/) 等文档。
### 3.4 比特 ID 授权
- **文档**[比特 ID 授权](https://doc.bitanswer.cn/docs/license-models/bitid-license/)
- **机制**:在**单机授权**基础上增加 **USB 比特 ID** 绑定;插硬件的机器才能正常使用。
- **场景**:离线环境下便于带硬件换机;控制台可追踪、注销设备。
### 3.5 演示授权
- **文档**[演示授权](https://doc.bitanswer.cn/docs/license-models/demo-license/)
- **特点**:演示码可被**多台客户端重复使用**;有有效期(如 15–180 天);**重装无法**简单延长试用期;可改为正式 SN。
- **数据**:激活需连授权中心,可统计演示用户量、分布等。
- **归类**:文档说明为**本地授权**——激活后使用**不必**再连服务器。
### 3.6 帐号授权
- **文档**[帐号授权](https://doc.bitanswer.cn/docs/license-models/account-license/)
- **对象**:从设备授权扩展到**人/帐号**,常为**订阅**模式。
- **集成路径**
1. **库直连**`Bit_LoginByPassword`(业务 GUID、密钥、帐号、密码等)。
2. **OIDC**(文档更推荐):托管登录页取得 `code``POST /oidc/token`**access_token**`Bit_LoginByToken`
- **实践建议**:认证模块(Launchpad)与主程序分离,token 传入主程序。
---
## 4. 授权设计与特征项、配置项(摘要)
来源:[授权设计与创建](https://doc.bitanswer.cn/docs/function/licensing-design-creation/)
- **业务可配维度**(随授权类型不同而变化)包括:连接类型、强制认证周期、安装限制、有效期、集团用户数、起止日期、使用次数、终端限制等。
- **授权内容更新生效**
- **云授权**:可较快反映到客户端(文档称可无需重启应用)。
- **集团 / 单机**:一般在客户端**下次连服务器**时同步;智能连接、强制认证会影响同步时机。
- **特征项类型**:只读、读写、算法、密钥等;均支持 QueryFeature / ReleaseFeature;集团场景下成对调用尤其重要。
- **配置项(Data)**:存配置与小数据,UTF-8;**不宜**替代特征项做安全控制;客户端可能有缓存,异常断电有丢失风险。
---
## 5. 模式对照表(便于与自研抽象对齐)
| 维度 | 云授权 | 单机 / 演示 / 比特 ID(本地类) | 集团授权 | 帐号授权 |
| ---- | ------------ | -------------------- | ---------- | --------------- |
| 判定位置 | 云端实时 | 本地许可(可选定期/智能连网同步) | 集团服务器(并发) | 云端 + token/密码 |
| 长期联网 | 一般需要 | 通常不强制(依业务属性) | 依部署与策略 | 登录/刷新时需要 |
| 并发模型 | SN + 在线会话/心跳 | 终端数、安装次数等 | 并发用户数 | 订阅/帐号维度 |
| 典型标识 | SN | SN(演示码策略特殊) | SN + 服务器地址 | 帐号 / OIDC token |
---
## 6. 免责声明
本文档为对 **doc.bitanswer.cn** 公开页面的**学习性整理**,不替代原厂文档;产品行为、默认值与 API 以官方网站与最新版本为准。
+349
View File
@@ -0,0 +1,349 @@
# 比特授权云:客户端 API 梳理(基于官方文档)
> 来源:[C 语言接口定义 v2](https://doc.bitanswer.cn/docs/client-api/c-interface-definitions-v2/)、[Java 语言接口 v2](https://doc.bitanswer.cn/docs/client-api/java-interface-v2/)、[SDK 概述](https://doc.bitanswer.cn/docs/client-api/sdk-overview/)、[典型场景](https://doc.bitanswer.cn/docs/client-api/examples-of-using-the-sdk/)。
> 其它语言入口(文档中心列举):[Delphi](https://doc.bitanswer.cn/docs/client-api/delphi-interface/)、[C#](https://doc.bitanswer.cn/docs/client-api/csharp-interface/)、[VB.Net](https://doc.bitanswer.cn/docs/client-api/vb-net-interface/)、[VB6](https://doc.bitanswer.cn/docs/client-api/vb6-interface/)。
> 整理日期:2026-04-06
> **说明**:本文为对公开文档的**纲要式整理**,参数细节、错误码与边界条件以官方页面及随产品下发的 `bitanswer.h`/接口文件为准。
> **本地对照**:已与仓库内手工备份的 C 接口 `[docs/c.md](c.md)`、Java 接口 `[docs/java.md](java.md)` 交叉核对(见文末「审核说明」及 **附录 A**)。
---
## 1. SDK 交付形态与前置条件
- **获取方式**:在比特授权云平台「模块下载」获取与**产品绑定**的静态库/动态库及接口定义;**不同产品库不可混用**(文档说明 `Login` 不匹配会返回 `0x105` 类错误)。
- **C**:头文件 `bitanswer.h` + 平台库(文档示例如 `libxxx_xxx_x64.a`)。
- **Java**JNI + 主封装类 `**BitAnswer`**(方法名为 camelCase,语义与 C API 大体一一对应;签名与枚举见 **附录 A** / `[docs/java.md](java.md)`)。
- **支持平台(SDK 概述列举)**Windows、Linux、Android、ARM Linux、MIPS Linux、macOS、iOS、HarmonyOS、QNX 等。
**运行期共性概念(SDK 概述)**
| 概念 | 含义 |
| -------------------------- | ---------------------------------------------------------------------------------------- |
| `application_data` / 产品识别码 | 编译进接口定义、与产品一一对应,多数 API 必填。 |
| `BIT_HANDLE` / 会话句柄 | `Bit_Login` / `Bit_LoginEx` 等成功后返回,后续特征项/心跳等依赖此句柄。 |
| `BIT_STATUS` | API 返回状态码;具体取值与错误码见官方「错误码」文档。 |
| 授权文件默认目录(典型场景) | Linux`{当前用户}/BitAnswer`Windows`{系统盘}/ProgramData/BitAnswer`;可用 `Bit_SetRootPath` 自定义。 |
---
## 2. URL / 地址约定(`Bit_Login` 的 `szURL` 等)
文档说明 `szURL` 可表达多种「授权来源」(节选):
| 形式 | 含义 |
| ----------------------------- | --------------------------------------------------------------------------------------- |
| `bit://ip:port` | 集团授权服务地址。 |
| `http://ip:port` | 授权服务中心(如云/E3 等)。 |
| `root://路径` | 仅在该路径下查找授权,效果类似先 `Bit_SetRootPath`。 |
| `lic://文件路径` / 目录 / License 串 | 内存或本地文件加载:`lic://` 可加载文件内容、目录下 `*.lic`、或直接传入 License 串;**多个路径用逗号分隔**(`c.md` 示例为半角 `,`)。 |
| 简写 | 文档示例:`/tmp,/data,127.0.0.1:8273` 等与 `lic://...`/`bit://...` 组合等价写法。 |
**配置优先级(文档)**:同一设置同时存在时 — **环境变量 > 调用 API 传入 > 配置文件**`bit_config.xml`,典型场景详述多 `Server`/`Proxy` 节点)。
---
## 3. 登录模式与标识(`LOGIN_MODE` / `LoginMode`
`Bit_Login` / `Bit_LoginEx``mode` 为**按位组合**。文档列出的典型取值(C 名 / Java 名对应):
| C(示意) | Java(示意) | 语义摘要 |
| -------------------------------------- | --------------------------- | ------------------------------------ |
| `BIT_MODE_LOCAL` | `LOCAL` | 仅本地(单机、BIT-ID、内存授权等)。 |
| `BIT_MODE_REMOTE` | `REMOTE` | 仅远端(云、帐号、集团等)。 |
| `BIT_MODE_AUTO` | `AUTO` | 本地与远端都尝试;**优先级:本地 > 远端**(在已配置远端前提下)。 |
| `BIT_MODE_CACHE` | `CACHE` | 云授权 SN 缓存,便于下次免输 SN。 |
| `BIT_MODE_USB` | `USB` | USB 相关授权。 |
| `LOGIN_MODE_PROCESS` | `PROCESS` | **集团**:按进程占并发点;其它模式无效。 |
| `LOGIN_MODE_SESSION` | `SESSION` | **集团**:更细粒度会话占点。 |
| `LOGIN_MODE_HIGH_PRIORITY` | `HIGH_PR` | **云**:高优先级登录可挤占普通登录。 |
| `LOGIN_MODE_NO_NETWORK` | `NO_NETWORK` | 禁止库联网。 |
| `LOGIN_MODE_CHECK_USERNAME` | `CHECK_USERNAME` | **集团**:设备+用户名才视为同一点。 |
| `LOGIN_MODE_USE_SESSIONID` | `USE_SESSIONID` | `Logout` 不释放点数,由第三方维护心跳。 |
| `LOGIN_MODE_HIGH_PERFORMANCE` | `HIGH_PERFORMANCE` | **集团**:合并请求降负载。 |
| `LOGIN_MODE_PERSISTENT_CONN` | `PERSISTENT_CONN` | **集团**长连接。 |
| `LOGIN_MODE_CREATE_HANDLE_ONLY` | `CREATE_HANDLE_ONLY` | 仅创建句柄,**真正校验推迟到 Query**。 |
| `LOGIN_MODE_CREATE_NEW_HANDLE` | `CREATE_NEW_HANDLE` | 每次唯一句柄,属性互不干扰。 |
| `LOGIN_MODE_MID` / `ACCOUNT` / `GROUP` | `MID` / `ACCOUNT` / `GROUP` | 按设备、帐号、分组名占点等。 |
C 示例中常写作 `LOGIN_MODE(BIT_MODE_REMOTE | LOGIN_MODE_HIGH_PERFORMANCE)` 等形式,即用 `LOGIN_MODE` 宏包装按位或(见 `c.md`)。
**szSN / sn 常见形态(文档)**:云平台 SN`#0` / `#bitid:0` 等形式选 BIT-ID`<口令>` 激活口令;`SN<口令>` 拼接;帐号授权可与密码类参数配合(具体见帐号授权专章)。
---
## 4. API 分组:认证与会话(C 名为主,Java 对照)
| 功能 | C API | Java API(文档) |
| ---------- | ------------------------------------------- | ----------------------------------- |
| 登录 | `Bit_Login`, `Bit_LoginEx` | `login`, `loginEx` |
| 帐号 Token | `Bit_LoginByToken`, `Bit_LoginByTokenEx` | `loginByToken`, `loginByTokenEx` |
| 帐号密码 | `Bit_LoginByPassword` | `loginByPassword` |
| 登出 | `Bit_Logout` | `logout` |
| 浮动迁出 | `Bit_Revoke`(在线/离线迁出、请求码) | `revoke`, `revokeOnline` |
| 删除本机 SN 数据 | `Bit_RemoveSn` | `removeSn` |
| 手动心跳 | `Bit_Heartbeat`(10s 内最多触发一次等说明) | `heartbeat` |
| Session 控制 | `Bit_SessionControl`, `Bit_SetSessionState` | `sessionControl`, `setSessionState` |
---
## 5. API 分组:激活与离线/在线升级
| 功能 | C API | Java`BitAnswer``docs/java.md`) |
| -------- | ----------------------------------------------- | ----------------------------------------------------------------- |
| 在线激活/升级 | `Bit_UpdateOnline` | `void updateOnline(String url, String sn)` |
| 取升级请求串 | `Bit_GetRequestInfo`(绑定类型、设备码 `REQ_TYPE_MID` 等) | `String getRequestInfo(String sn, BindingType type)` |
| 应用升级包 | `Bit_ApplyUpdateInfo` | `String applyUpdateInfo(String updateInfo)` |
| 远程集团应用升级 | `Bit_ApplyUpdateInfoEx` | **`java.md` 未收录**;若需远程集团升级请以随包接口为准 |
| 用请求串换升级码 | `Bit_GetUpdateInfo` | `String getUpdateInfo(String url, String sn, String requestInfo)` |
**典型场景补充**
- **在线**`Bit_UpdateOnline(NULL, sn, application_data)`BIT-ID 可用 `#0` 等形式。
- **离线**:本机 `Bit_GetRequestInfo` → 联网换取 License 串 → 本机 `Bit_ApplyUpdateInfo`;涉密场景可用**设备码**缩短人工传递长度。
- **内存授权**:从离线包取 `<code>`,拼 `lic://...` 作为 `Bit_Login` 的 url,模式 `BIT_MODE_LOCAL`
---
## 6. API 分组:特征项(功能模块与加密点)
| 功能 | C API | Java API(文档) |
| ------------ | ---------------------------------------------- | -------------------------------------- |
| 批量 Query | `Bit_BatchBegin`, `Bit_BatchEnd` | `batchBegin`, `batchEnd` |
| 基础占用/释放 | `Bit_QueryFeature`, `Bit_ReleaseFeature` | `queryFeature`, `releaseFeature` |
| 集团扩展占用 | `Bit_QueryFeatureEx`, `Bit_ReleaseFeatureEx` | `queryFeatureEx`, `releaseFeatureEx` |
| 按名称+版本/队列 | `Bit_QueryFeatureEx2`, `Bit_ReleaseFeatureEx2` | `queryFeatureEx2`, `releaseFeatureEx2` |
| 查存在/有效期(不占点) | `Bit_GetFeatureInfo2`, `Bit_GetFeatureInfoEx2` | `getFeatureInfo2`, `getFeatureInfoEx2` |
| Ticket 信息 | `Bit_GetTicketInfo` | `getTicketInfo` |
| 算法特征 | `Bit_ConvertFeature` | `convertFeature` |
| 密钥特征加解密 | `Bit_EncryptFeature`, `Bit_DecryptFeature` | `encryptFeature`, `decryptFeature` |
| 读写特征值 | `Bit_ReadFeature`, `Bit_WriteFeature` | `readFeature`, `writeFeature` |
文档强调:**集团授权**下 `QueryFeature` / `ReleaseFeature`(及 Ex 系列)需**成对**使用以正确占用/释放并发用户数;`QueryFeatureEx` 支持多种 `mode`(默认非阻塞、尽可能占用、仅检查计数等)。
---
## 7. API 分组:配置项(Data
| 功能 | C API | Java API(文档) |
| ------- | ------------------------------------------- | ----------------------------------- |
| 读 | `Bit_GetDataItem` | `getDataItem` |
| 写/创建 | `Bit_SetDataItem` | `setDataItem` |
| 枚举数量/名称 | `Bit_GetDataItemNum`, `Bit_GetDataItemName` | `getDataItemNum`, `getDataItemName` |
| 删(授权码侧) | `Bit_RemoveDataItem` | `removeDataItem` |
Java 文档对名称/值长度有简要限制说明(如名称最长 128 字节、值 1024 字节量级,以官方为准)。
---
## 8. API 分组:信息查询
`c.md`**「信息获取」** 大节对应。该节除下表外,还包括版本/句柄/诊断等 API(为减少重复,详见 **§10**)。
| 功能 | C API | 说明(摘要) |
| ------- | -------------------- | ------------------------------------------------ |
| 会话信息 | `Bit_GetSessionInfo` | XML 或字段型信息:SN/特征/配置项摘要、服务器时间、授权类型组合、起止日期、离线分钟数等。 |
| 本机/环境信息 | `Bit_GetInfo` | 已激活 SN 列表、特征列表、集团服务器发现、BIT-ID 列表、升级错误详情等。 |
| 集团服务信息 | `Bit_GetServerInfo` | **无需先 Login**;查集团侧授权/特征等 XML。 |
**与 `c.md` 源码块核对**`Bit_GetServerInfo` 小节内的函数名被写成 `Bit_GeServerInfo`(缺字母 `t`),与章节标题不一致,应视为复制/排版笔误;集成时以 **`bitanswer.h`** 为准。
---
## 9. API 分组:借出 / 归还(浮动与集团场景扩展)
文档在 C / Java 接口中均用「借出操作」类章节描述。**C** 名称见下左列;**Java`BitAnswer`** 见 [`docs/java.md`](java.md) 及下表。
| C API | Java 方法(`docs/java.md`) |
| ----------------------------- | ----------------------------------------------------------------------------------------------- |
| `Bit_GetBorrowRequest` | `String getBorrowRequest(String sn, int durationDay)` |
| `Bit_GetBorrowFeatureRequest` | `String getBorrowFeatureRequest(int durationDay, String borrowScope)` |
| `Bit_CheckOutSn` | `void checkOutSn(String url, int featureId)` |
| `Bit_CheckOutSnEx` | `void checkOutSnEx(String url, int featureId, String xmlScope, int durationDays)` |
| `Bit_CheckOut` | `void checkOut(String url, String scope, String featureList, int durationDays)` |
| `Bit_CheckOutFeatures` | `void checkOutFeatures(String url, int[] featureList, int durationDays)` |
| `Bit_CheckIn` | `void checkIn(String url, int featureId)` |
| `Bit_CheckInEx` | `void checkInEx(String url, int featureId, String xmlScope)` |
| `Bit_ApplyBorrowInfo` | `void ApplyBorrowInfo(String borrowInfo)`(文档锚点为大写 `ApplyBorrowInfo`;参数类型应以实际 SDK 的 `String` 为准) |
参数语义、`scope` / `featureList` XML 格式等以原文为准。Java 文档注明 **applyUpdateInfo** 可兼容离线借出串(与 `ApplyBorrowInfo` 重叠场景见原文)。
---
## 10. API 分组:路径、代理、属性与其它工具
对应 `c.md`**「信息获取」** 后半(`Bit_GetVersion` 起)与 **「属性设置」** 全节。常见于部署与诊断:
| C API | 用途摘要 |
| ----------------------------------- | ------------------- |
| `Bit_SetRootPath` | 授权文件根路径。 |
| `Bit_SetProxy` | HTTP/SOCKS 等代理参数。 |
| `Bit_SetLocalServer` | 本地/集团服务发现相关设置(见原文)。 |
| `Bit_GetProductPath` | 产品路径查询。 |
| `Bit_GetVersion` | 库版本。 |
| `Bit_GetLastError` | 上一 API 错误码。 |
| `Bit_GetNextHandle` | 枚举/遍历句柄(见原文)。 |
| `Bit_SetAttr` / `Bit_SetCustomInfo` | 句柄/会话属性与自定义信息。 |
| `Bit_TestBitService` | 探测集团服务等可达性。 |
**Java`docs/java.md`)对应**`int getVersion()` · `String getProductPath()` · `int getLastError()` · `void testBitService(String url, String sn, int featureId)` · `void setRootPath(String rootPath)` · `void setLocalServer(String hostName, int port, int timeout)` · `void setProxy(String hostName, int port, String userId, String password)` · `void setAttr(int type, byte[] pValue)` · `void setCustomInfo(...)``String` / `int` 重载)。**未在 `java.md` 列出**`Bit_GetNextHandle` 的 Java 对等方法(若需遍历句柄请查随包 JNI 或新版文档)。
---
## 附录 AJava `BitAnswer` API(依据 [`docs/java.md`](java.md)
### A.1 类型与构造
| 项 | 说明 |
|----|------|
| **主类** | `BitAnswer`(实例方法;通过 JNI 调用 native,与 C SDK 同库) |
| **构造方法** | `BitAnswer()``BitAnswer(String url, String sn, LoginMode mode)`(便捷构造,等价于再调用 `login` 的用法以官方示例为准) |
| **错误处理** | 多数 API 为 `void` 或返回数据;`int getLastError()` 返回**当前线程**最近一次调用的错误码(成功为 `0`,见 `java.md` |
### A.2 文档中出现的枚举 / 参数类型(用于方法签名)
| 名称 | 用途(节选) |
|------|----------------|
| `LoginMode` | `login` / `loginEx`、构造器第三参数;与 C 侧 `LOGIN_MODE` 按位语义对应,Java 侧为枚举常量(`LOCAL``REMOTE``AUTO``CACHE``USB``PROCESS``SESSION``HIGH_PR``NO_NETWORK``CHECK_USERNAME``USE_SESSIONID``HIGH_PERFORMANCE``PERSISTENT_CONN``CREATE_HANDLE_ONLY``CREATE_NEW_HANDLE``MID``ACCOUNT``GROUP` |
| `BindingType` | `getRequestInfo``EXISTING``LOCAL``USB_STORAGE` |
| `BatchMode` | `batchBegin``BIT_BATCH_MODE_CONTINUE``BIT_BATCH_MODE_BREAK` |
| `SessionType` | `getSessionInfo``XML_TYPE_SN_INFO``XML_TYPE_FEATURE_INFO``XML_TYPE_DATA_INFO``BIT_SERVER_ADDRESS``BIT_SERVER_TIME``BIT_CONTROL_TYPE` 及各日期/次数/离线字段常量(见 `java.md` |
| `InfoType` | `getInfo``BIT_INFO_SERVER_ADDRESS``BIT_INFO_SN``BIT_INFO_SN_FEATURE` 等 |
| `ServerInfoType` | `getServerInfo``BIT_SERVER_INFO_SN``BIT_SERVER_INFO_SN_FEATURE``BIT_SERVER_INFO_SN_LICENSE` 等 |
| `SessionCtlType` | `sessionControl``SESSION_CTL_LOGOUT``SESSION_CTL_CHECK` |
| **`setAttr` 的 type** | `ATTR_HB_RETRY_FAILED_CALLBACK``ATTR_WAIT_TIMEOUT``ATTR_RETRY_COUNT` 等(见 `java.md` |
| **`setCustomInfo` 的 infoId** | `CustomInfo``CUSTOM_CLIENT_ID``CUSTOM_OPTION` 等(`java.md` 列至 `CUSTOM_GROUP_NAME(0xA)` |
### A.3 方法签名全表(与 C 对照)
**认证**
| Java |
|------|
| `void login(String url, String sn, LoginMode mode)` |
| `void loginEx(String url, String sn, int featureId, String xmlScope, LoginMode mode)` |
| `void loginByToken(String url, String token)` |
| `void loginByTokenEx(String url, String businessGuid, String token, String idpGuid, String grantType)` |
| `void loginByPassword(String url, String guid, String secret, String account, String password)` |
| `void logout()` |
| `String revoke(String sn)` |
| `void revokeOnline(String url, String sn)` |
| `void removeSn(String sn)` |
| `int heartbeat()` |
| `String sessionControl(String url, String sessionId, SessionCtlType type)` |
| `void setSessionState(int state, byte[] pReserved)` |
**激活升级**(另见 **§5** 表格)
**特征项**
| Java |
|------|
| `void batchBegin(BatchMode mode)` · `int[] batchEnd()` |
| `int queryFeature(int featureId)` · `int releaseFeature(int featureId)` |
| `int queryFeatureEx(int featureId, int mode, int required, String scope)` · `int releaseFeatureEx(int featureId, int consumed, String scope)` |
| `long queryFeatureEx2(String featureName, int mode, int required, String scope)` · `void releaseFeatureEx2(byte[] ticket, int consumed)` |
| `int getFeatureInfo2(String featureName, String scope)` · `String getFeatureInfoEx2(String featureName, String scope)` |
| `String getTicketInfo(byte[] ticket, int type)``type``BIT_TICKET_TYPR_USERS`,原文档拼写如此) |
| `int convertFeature(int featureId, int para1, int para2, int para3, int para4)` |
| `byte[] encryptFeature(int featureId, byte[] pPlainBuffer)` · `byte[] decryptFeature(int featureId, byte[] pCipherBuffer)` |
| `int readFeature(int featureId)` · `void writeFeature(int featureId, int featureValue)` |
**配置项**
| Java |
|------|
| `byte[] getDataItem(String dataItemName)` · `void setDataItem(String dataItemName, byte[] dataItemValue)` |
| `int getDataItemNum()` · `String getDataItemName(int index)` · `void removeDataItem(String dataItemName)` |
`java.md`:配置项名称最大 128 字节等限制与 C 侧一致。)
**信息获取**
| Java |
|------|
| `String getSessionInfo(SessionType type)` · `String getInfo(String sn, InfoType type)` |
| `String getServerInfo(String url, String sn, String scope, ServerInfoType type)` |
| `int getVersion()` · `void testBitService(String url, String sn, int featureId)` · `String getProductPath()` · `int getLastError()` |
**借出操作**(另见 **§9** 表)
### A.4 与 C 覆盖差异(据 `docs/java.md` 全文)
| C API | Java`docs/java.md` |
|-------|------------------------|
| `Bit_ApplyUpdateInfoEx` | 未收录 |
| `Bit_GetNextHandle` | 未收录 |
**补充**C 侧 `Bit_GetRequestInfo``BINDING_TYPE`**`REQ_TYPE_MID`(设备码)**`java.md``getRequestInfo``BindingType` 中仅列出 `EXISTING``LOCAL``USB_STORAGE`。设备码流程在 Java 是否另有接口,以 **随包 SDK / 更新版 `java.md`** 为准。
### A.5 文档小瑕疵(复制源)
- `loginEx` 说明中「Login 等价于 LoginEx」与 C 页类似,易误解;需包含**指定 featureId** 时用 `loginEx`
- `batchBegin` 正文写「由 **Bit_batchEnd** 提交」应为 **`batchEnd`Java** / **`Bit_BatchEnd`C**。
- `ApplyBorrowInfo` 参数写 `string` 应为 Java **`String`**。
---
## 11. C API 名称索引(供检索)
下列名称与 `c.md` 中全部 `## [Bit_…]` 小节一致,共 **59** 个独立函数(其中 `Bit_CheckOutSn` / `Bit_CheckOutSnEx` 合并在同一文档小节)。若与头文件有出入以 **`bitanswer.h`** 为准:
`Bit_ApplyBorrowInfo` · `Bit_ApplyUpdateInfo` · `Bit_ApplyUpdateInfoEx` · `Bit_BatchBegin` · `Bit_BatchEnd` · `Bit_CheckIn` · `Bit_CheckInEx` · `Bit_CheckOut` · `Bit_CheckOutFeatures` · `Bit_CheckOutSn` · `Bit_CheckOutSnEx` · `Bit_ConvertFeature` · `Bit_DecryptFeature` · `Bit_EncryptFeature` · `Bit_GetBorrowFeatureRequest` · `Bit_GetBorrowRequest` · `Bit_GetDataItem` · `Bit_GetDataItemName` · `Bit_GetDataItemNum` · `Bit_GetFeatureInfo2` · `Bit_GetFeatureInfoEx2` · `Bit_GetInfo` · `Bit_GetLastError` · `Bit_GetNextHandle` · `Bit_GetProductPath` · `Bit_GetRequestInfo` · `Bit_GetServerInfo` · `Bit_GetSessionInfo` · `Bit_GetTicketInfo` · `Bit_GetUpdateInfo` · `Bit_GetVersion` · `Bit_Heartbeat` · `Bit_Login` · `Bit_LoginEx` · `Bit_LoginByPassword` · `Bit_LoginByToken` · `Bit_LoginByTokenEx` · `Bit_Logout` · `Bit_QueryFeature` · `Bit_QueryFeatureEx` · `Bit_QueryFeatureEx2` · `Bit_ReadFeature` · `Bit_ReleaseFeature` · `Bit_ReleaseFeatureEx` · `Bit_ReleaseFeatureEx2` · `Bit_RemoveDataItem` · `Bit_RemoveSn` · `Bit_Revoke` · `Bit_SessionControl` · `Bit_SetAttr` · `Bit_SetCustomInfo` · `Bit_SetDataItem` · `Bit_SetLocalServer` · `Bit_SetProxy` · `Bit_SetRootPath` · `Bit_SetSessionState` · `Bit_TestBitService` · `Bit_UpdateOnline` · `Bit_WriteFeature`
---
## 12. 与mgmt/运营侧 API 的边界
开发者控制台还提供 **[Backoffice API](https://doc.bitanswer.cn/category/docs/backoffice-api/)**(REST/数据结构),用于授权码、用户、业务等**服务端自动化**,与上表**客户端安全库 API** 不同层。若需「控制台可调用的 HTTP API」,请单独以 Backoffice 文档为准。
---
## 13. 免责声明
本文档不替代比特授权云官方文档与随 SDK 发布的法律/技术支持条款;集成与合规问题请咨询官方渠道。
---
## 14. 对照本地备份的审核结论(2026-04-06)
### 14.1 `docs/c.md`
| 项 | 结论 |
|----|------|
| **函数覆盖** | `c.md` 中列出的全部 Bit 客户端 API 均已在本梳理的分组或 §11 索引中覆盖;**无遗漏**。 |
| **章节结构** | 「信息获取」后半与「属性设置」对应 §8 / §10,表头已说明。 |
| **Bit_GetServerInfo** | `c.md` 代码块误写为 `Bit_GeServerInfo`;§8 已注释。 |
| **Bit_LoginEx 说明** | `c.md``featured=0` 等笔误;本梳理未复述。 |
| **多路径分隔符** | 已标明示例为半角 `,`。 |
| **登录模式** | 与 `c.md` 表一致;已补充 `LOGIN_MODE(...)` 宏。 |
| **§11 索引** | 以 `c.md` 小节为对齐基准。 |
### 14.2 `docs/java.md`
| 项 | 结论 |
|----|------|
| **对等性** | `java.md`**`BitAnswer` 实例方法**与 C API 基本全覆盖;**附录 A** 为完整签名与枚举清单。 |
| **缺失相对 C** | `Bit_ApplyUpdateInfoEx``Bit_GetNextHandle``java.md` 中未出现(§A.4)。 |
| **`getServerInfo`** | Java 文档写为 `getServerInfo`;无 C 页 `GeServerInfo` 笔误问题。 |
| **借出** | §9 已按 `java.md` 补全 `checkOut*` / `checkIn*` / `ApplyBorrowInfo` 等 Java 方法表。 |
+1880
View File
File diff suppressed because it is too large Load Diff
+1202
View File
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
/*
* C++ 调用示例:初始化、校验许可、销毁句柄。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#include "craftlabs_auth.h"
#include <cstdio>
#include <cstdlib>
int main() {
AuthHandle h = auth_initialize("{}");
if (!h) {
return EXIT_FAILURE;
}
AuthResult r = auth_check_license(h);
if (r.success == 0) {
auth_destroy(h);
return EXIT_FAILURE;
}
auth_destroy(h);
std::printf("example cpp ok\n");
return EXIT_SUCCESS;
}
+21
View File
@@ -0,0 +1,21 @@
import cn.craftlabs.auth.AuthProvider;
import cn.craftlabs.auth.AuthResult;
import cn.craftlabs.auth.bitanswer.BitAnswerProvider;
/**
* 演示:通过 {@link BitAnswerProvider} 完成初始化与许可校验。
*
* <p>版权所有 © 广州创飞人工智能技术有限公司
*
* @author huangping@craftlabs.cn
*/
public class ExampleApp {
public static void main(String[] args) {
try (AuthProvider p = new BitAnswerProvider()) {
AuthResult r = p.initialize("{}");
System.out.println("init: " + r.isSuccess() + " " + r.getMessage());
r = p.checkLicense();
System.out.println("check: " + r.isSuccess() + " " + r.getMessage());
}
}
}
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
"""示例:设置 CRAFTLABS_AUTH_LIBRARY 或 PYTHONPATH 后运行。
版权所有 © 广州创飞人工智能技术有限公司
开发者:huangping@craftlabs.cn
"""
import os
import sys
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
sys.path.insert(0, os.path.join(ROOT, "python"))
from craftlabs_auth import AuthApi # noqa: E402
def main() -> None:
lib = os.path.join(ROOT, "native", "build", "libcraftlabs_auth_bitanswer.so")
if os.path.isfile(lib):
os.environ.setdefault("CRAFTLABS_AUTH_LIBRARY", lib)
with AuthApi() as api:
print(api.initialize("{}"))
print(api.check_license())
print(api.get_license_info())
if __name__ == "__main__":
main()
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.craftlabs</groupId>
<artifactId>craftlabs-auth-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>craftlabs-auth-bitanswer</artifactId>
<name>CraftLabs Auth — Bitanswer adapter</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>cn.craftlabs</groupId>
<artifactId>craftlabs-auth-core</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,73 @@
package cn.craftlabs.auth.bitanswer;
import cn.craftlabs.auth.AuthProvider;
import cn.craftlabs.auth.AuthResult;
import cn.craftlabs.auth.LicenseInfo;
import cn.craftlabs.auth.internal.NativeBridge;
/**
* 比特安索(Bitanswer)授权提供者的 Java 封装。
*
* <p>在静态初始化块中加载本地库 {@code craftlabs_auth_bitanswer},通过 {@link
* cn.craftlabs.auth.internal.NativeBridge} 将 {@link cn.craftlabs.auth.AuthProvider} 各方法委托给 C
* 端实现。重复调用 {@link #initialize(String)} 会先销毁已有 native 句柄再重新初始化。
*
* <p>版权所有 © 广州创飞人工智能技术有限公司
*
* @author huangping@craftlabs.cn
*/
public final class BitAnswerProvider implements AuthProvider {
static {
System.loadLibrary("craftlabs_auth_bitanswer");
}
private long nativeHandle;
@Override
public AuthResult initialize(String configJson) {
if (nativeHandle != 0L) {
NativeBridge.nativeDestroy(nativeHandle);
nativeHandle = 0L;
}
nativeHandle = NativeBridge.nativeInitialize(configJson != null ? configJson : "{}");
return new AuthResult(true, "Initialized");
}
@Override
public AuthResult activate(String licenseKey) {
return NativeBridge.nativeActivate(nativeHandle, licenseKey);
}
@Override
public AuthResult checkLicense() {
return NativeBridge.nativeCheckLicense(nativeHandle);
}
@Override
public LicenseInfo getLicenseInfo() {
return NativeBridge.nativeGetLicenseInfo(nativeHandle);
}
@Override
public boolean hasFeature(String featureName) {
return NativeBridge.nativeHasFeature(nativeHandle, featureName);
}
@Override
public AuthResult release() {
return NativeBridge.nativeRelease(nativeHandle);
}
@Override
public AuthResult heartbeat() {
return NativeBridge.nativeHeartbeat(nativeHandle);
}
@Override
public void close() {
if (nativeHandle != 0L) {
NativeBridge.nativeDestroy(nativeHandle);
nativeHandle = 0L;
}
}
}
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.craftlabs</groupId>
<artifactId>craftlabs-auth-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>craftlabs-auth-core</artifactId>
<name>CraftLabs Auth — core API</name>
<packaging>jar</packaging>
</project>
@@ -0,0 +1,37 @@
package cn.craftlabs.auth;
/**
* 授权能力的统一契约:初始化、激活、校验许可、查询特性与释放等生命周期方法。
*
* <p>实现类负责加载对应 native 或远端适配器;调用方应在不再使用时调用 {@link #close()} 释放底层资源。
*
* <p>版权所有 © 广州创飞人工智能技术有限公司
*
* @author huangping@craftlabs.cn
*/
public interface AuthProvider extends AutoCloseable {
/** 使用 JSON 配置初始化授权上下文;可重复调用,实现类应妥善处理句柄重置。 */
AuthResult initialize(String configJson);
/** 使用许可密钥激活(具体语义由底层供应商决定)。 */
AuthResult activate(String licenseKey);
/** 校验当前许可是否有效。 */
AuthResult checkLicense();
/** 返回当前许可详情;具体字段含义与失败时的表现以各 {@link AuthProvider} 实现为准。 */
LicenseInfo getLicenseInfo();
/** 查询指定特性是否开启。 */
boolean hasFeature(String featureName);
/** 释放/注销当前许可占用(与 {@link #close()} 侧重点不同,依供应商语义)。 */
AuthResult release();
/** 会话心跳,用于在线校验或租约续期等场景。 */
AuthResult heartbeat();
/** 释放 native 或远端资源;接口关闭后不得再调用其他方法。 */
@Override
void close();
}
@@ -0,0 +1,50 @@
package cn.craftlabs.auth;
import java.util.Objects;
/**
* 单次授权操作的结果:成功与否及 UTF-8 说明信息(可能来自 native,勿假定固定文案)。
*
* <p>版权所有 © 广州创飞人工智能技术有限公司
*
* @author huangping@craftlabs.cn
*/
public final class AuthResult {
private final boolean success;
private final String message;
public AuthResult(boolean success, String message) {
this.success = success;
this.message = message != null ? message : "";
}
public boolean isSuccess() {
return success;
}
public String getMessage() {
return message;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AuthResult that = (AuthResult) o;
return success == that.success && Objects.equals(message, that.message);
}
@Override
public int hashCode() {
return Objects.hash(success, message);
}
@Override
public String toString() {
return "AuthResult{success=" + success + ", message='" + message + '\'' + '}';
}
}
@@ -0,0 +1,68 @@
package cn.craftlabs.auth;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
/**
* 当前许可快照:是否已授权、过期时间及特性开关映射(不可变视图)。
*
* <p>版权所有 © 广州创飞人工智能技术有限公司
*
* @author huangping@craftlabs.cn
*/
public final class LicenseInfo {
private final boolean licensed;
private final Date expirationDate;
private final Map<String, Boolean> features;
public LicenseInfo(boolean licensed, Date expirationDate, Map<String, Boolean> features) {
this.licensed = licensed;
this.expirationDate = expirationDate;
this.features =
features == null ? Collections.emptyMap() : Collections.unmodifiableMap(features);
}
public boolean isLicensed() {
return licensed;
}
public Date getExpirationDate() {
return expirationDate;
}
public Map<String, Boolean> getFeatures() {
return features;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LicenseInfo that = (LicenseInfo) o;
return licensed == that.licensed
&& Objects.equals(expirationDate, that.expirationDate)
&& Objects.equals(features, that.features);
}
@Override
public int hashCode() {
return Objects.hash(licensed, expirationDate, features);
}
@Override
public String toString() {
return "LicenseInfo{licensed="
+ licensed
+ ", expirationDate="
+ expirationDate
+ ", features="
+ features
+ '}';
}
}
@@ -0,0 +1,32 @@
package cn.craftlabs.auth.internal;
import cn.craftlabs.auth.AuthResult;
import cn.craftlabs.auth.LicenseInfo;
/**
* JNI 入口:与 {@code jni_bridge.cpp} 中的 {@code Java_cn_craftlabs_auth_internal_NativeBridge_*}
* 函数签名一一对应,由各 {@code AuthProvider} 实现所在的模块加载同名 native 库后使用。
*
* <p>版权所有 © 广州创飞人工智能技术有限公司
*
* @author huangping@craftlabs.cn
*/
public final class NativeBridge {
private NativeBridge() {}
public static native long nativeInitialize(String configJson);
public static native void nativeDestroy(long handle);
public static native AuthResult nativeActivate(long handle, String licenseKey);
public static native AuthResult nativeCheckLicense(long handle);
public static native LicenseInfo nativeGetLicenseInfo(long handle);
public static native boolean nativeHasFeature(long handle, String featureName);
public static native AuthResult nativeRelease(long handle);
public static native AuthResult nativeHeartbeat(long handle);
}
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.craftlabs</groupId>
<artifactId>craftlabs-auth-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>craftlabs-auth-selfhosted</artifactId>
<name>CraftLabs Auth — self-hosted HTTP adapter</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>cn.craftlabs</groupId>
<artifactId>craftlabs-auth-core</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,72 @@
package cn.craftlabs.auth.selfhosted;
import cn.craftlabs.auth.AuthProvider;
import cn.craftlabs.auth.AuthResult;
import cn.craftlabs.auth.LicenseInfo;
import cn.craftlabs.auth.internal.NativeBridge;
/**
* 自研授权(HTTP)适配器的 Java 封装。
*
* <p>当前阶段复用与 Bitanswer 相同的 native 桩实现;后续可切换为独立 {@code
* libcraftlabs_auth_selfhosted}。
*
* <p>版权所有 © 广州创飞人工智能技术有限公司
*
* @author huangping@craftlabs.cn
*/
public final class SelfHostedAuthProvider implements AuthProvider {
static {
System.loadLibrary("craftlabs_auth_bitanswer");
}
private long nativeHandle;
@Override
public AuthResult initialize(String configJson) {
if (nativeHandle != 0L) {
NativeBridge.nativeDestroy(nativeHandle);
nativeHandle = 0L;
}
nativeHandle = NativeBridge.nativeInitialize(configJson != null ? configJson : "{}");
return new AuthResult(true, "Initialized (self-hosted stub)");
}
@Override
public AuthResult activate(String licenseKey) {
return NativeBridge.nativeActivate(nativeHandle, licenseKey);
}
@Override
public AuthResult checkLicense() {
return NativeBridge.nativeCheckLicense(nativeHandle);
}
@Override
public LicenseInfo getLicenseInfo() {
return NativeBridge.nativeGetLicenseInfo(nativeHandle);
}
@Override
public boolean hasFeature(String featureName) {
return NativeBridge.nativeHasFeature(nativeHandle, featureName);
}
@Override
public AuthResult release() {
return NativeBridge.nativeRelease(nativeHandle);
}
@Override
public AuthResult heartbeat() {
return NativeBridge.nativeHeartbeat(nativeHandle);
}
@Override
public void close() {
if (nativeHandle != 0L) {
NativeBridge.nativeDestroy(nativeHandle);
nativeHandle = 0L;
}
}
}
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.craftlabs</groupId>
<artifactId>craftlabs-auth-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>craftlabs-auth-tests</artifactId>
<name>CraftLabs Auth — integration tests</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>cn.craftlabs</groupId>
<artifactId>craftlabs-auth-bitanswer</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<native.library.path>${project.basedir}/../../native/build</native.library.path>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Djava.library.path=${native.library.path}</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,30 @@
package cn.craftlabs.auth;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import cn.craftlabs.auth.bitanswer.BitAnswerProvider;
import org.junit.jupiter.api.Test;
/**
* {@link BitAnswerProvider} 集成测试(依赖 native 桩)。
*
* <p>版权所有 © 广州创飞人工智能技术有限公司
*
* @author huangping@craftlabs.cn
*/
class BitAnswerProviderTest {
@Test
void initializeCheckAndLicenseInfo_roundTrip() {
try (AuthProvider p = new BitAnswerProvider()) {
AuthResult init = p.initialize("{}");
assertTrue(init.isSuccess(), init.getMessage());
AuthResult ok = p.checkLicense();
assertTrue(ok.isSuccess(), ok.getMessage());
LicenseInfo info = p.getLicenseInfo();
assertNotNull(info);
assertTrue(info.isLicensed());
}
}
}
+58
View File
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.craftlabs</groupId>
<artifactId>craftlabs-auth-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>CraftLabs Auth SDK (parent)</name>
<modules>
<module>craftlabs-auth-core</module>
<module>craftlabs-auth-bitanswer</module>
<module>craftlabs-auth-selfhosted</module>
<module>craftlabs-auth-tests</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>17</maven.compiler.release>
<junit.version>5.10.2</junit.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.craftlabs</groupId>
<artifactId>craftlabs-auth-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
+58
View File
@@ -0,0 +1,58 @@
cmake_minimum_required(VERSION 3.16)
project(craftlabs_auth_bitanswer LANGUAGES C CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CRAFTLABS_AUTH_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
option(CRAFTLABS_BUILD_JNI "Build JNI bridge (requires JDK)" ON)
option(CRAFTLABS_BUILD_TESTS "Build native smoke tests" ON)
set(CRAFTLABS_AUTH_CORE_SOURCES
"${CRAFTLABS_AUTH_SRC_DIR}/src/craftlabs_auth.cpp"
"${CRAFTLABS_AUTH_SRC_DIR}/src/bitanswer/bitanswer_adapter.cpp"
"${CRAFTLABS_AUTH_SRC_DIR}/src/selfhosted/http_client.cpp"
"${CRAFTLABS_AUTH_SRC_DIR}/src/selfhosted/selfhosted_adapter.cpp"
)
if(CRAFTLABS_BUILD_JNI)
find_package(JNI REQUIRED)
list(APPEND CRAFTLABS_AUTH_CORE_SOURCES
"${CRAFTLABS_AUTH_SRC_DIR}/src/jni/jni_bridge.cpp")
endif()
add_library(craftlabs_auth_bitanswer SHARED ${CRAFTLABS_AUTH_CORE_SOURCES})
target_include_directories(craftlabs_auth_bitanswer
PUBLIC
"${CRAFTLABS_AUTH_SRC_DIR}/include"
PRIVATE
"${CRAFTLABS_AUTH_SRC_DIR}/src"
)
if(CRAFTLABS_BUILD_JNI)
target_include_directories(craftlabs_auth_bitanswer PRIVATE ${JNI_INCLUDE_DIRS})
endif()
if(WIN32)
target_compile_definitions(craftlabs_auth_bitanswer PRIVATE CRAFTLABS_AUTH_PLATFORM_WINDOWS=1)
elseif(APPLE)
target_compile_definitions(craftlabs_auth_bitanswer PRIVATE CRAFTLABS_AUTH_PLATFORM_MACOS=1)
else()
target_compile_definitions(craftlabs_auth_bitanswer PRIVATE CRAFTLABS_AUTH_PLATFORM_LINUX=1)
endif()
if(CRAFTLABS_BUILD_TESTS)
enable_testing()
add_executable(craftlabs_auth_smoke
"${CRAFTLABS_AUTH_SRC_DIR}/tests/smoke_test.cpp")
target_link_libraries(craftlabs_auth_smoke PRIVATE craftlabs_auth_bitanswer)
target_include_directories(craftlabs_auth_smoke PRIVATE
"${CRAFTLABS_AUTH_SRC_DIR}/include")
set_target_properties(craftlabs_auth_smoke PROPERTIES
BUILD_RPATH "$<TARGET_FILE_DIR:craftlabs_auth_bitanswer>")
add_test(NAME craftlabs_auth_smoke COMMAND craftlabs_auth_smoke)
endif()
+70
View File
@@ -0,0 +1,70 @@
/*
* CraftLabs 授权 SDK — C 语言 FFI 公共头文件。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#ifndef CRAFTLABS_AUTH_H
#define CRAFTLABS_AUTH_H
#include <stdint.h>
/*
* FFI / Pythonctypes、cffi)说明
* ----------------------------------
* - 所有 `const char*` 均为 **UTF-8** 编码、以 `\0` 结尾。
* - `AuthHandle`:不透明指针,仅由本库函数产生与销毁(`auth_initialize` / `auth_destroy`)。
* - `AuthResult`、`LicenseInfo` 使用 `stdint` 定宽字段,避免 `time_t`/`int` 宽度随平台变化导致 Python 侧 Structure 布局错误。
* - `AuthResult.message`:指向 **由本库管理的静态或内部只读缓冲区**,调用方不得 `free`;在任意后续对本库的再次调用之后视为可能失效(与其它 FFI 语言惯例一致)。
* - `auth_get_license_info` 返回的 `LicenseInfo*`(及其中 `feature_names` / `feature_values` 指向的数组)须通过 `auth_free_license_info` 释放;释放后不得再读指针成员。
* - 官方 Python 绑定见仓库 `python/craftlabs_auth/`ctypes,与本头文件字段一一对应)。
*/
#ifdef _WIN32
#define CRAFTLABS_API __declspec(dllexport)
#else
#define CRAFTLABS_API __attribute__((visibility("default")))
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef void* AuthHandle;
typedef struct {
int32_t success; /* 1: 成功, 0: 失败 */
const char* message; /* UTF-8,由库持有,勿 free */
} AuthResult;
typedef struct {
int32_t is_licensed; /* 1: 已授权, 0: 否 */
int64_t expiration_date; /* Unix 纪元秒数(UTC),0 表示无过期限制 */
const char** feature_names; /* 以 NULL 结尾的指针数组,或长度由 feature_count 给出,由随 LicenseInfo 一并释放 */
int32_t* feature_values; /* 与 feature_names 一一对应,元素为 0/1 */
int32_t feature_count;
} LicenseInfo;
CRAFTLABS_API AuthHandle auth_initialize(const char* config_json);
CRAFTLABS_API AuthResult auth_activate(AuthHandle handle, const char* license_key);
CRAFTLABS_API AuthResult auth_check_license(AuthHandle handle);
CRAFTLABS_API LicenseInfo* auth_get_license_info(AuthHandle handle);
CRAFTLABS_API void auth_free_license_info(LicenseInfo* info);
CRAFTLABS_API int32_t auth_has_feature(AuthHandle handle, const char* feature_name);
CRAFTLABS_API AuthResult auth_release(AuthHandle handle);
CRAFTLABS_API AuthResult auth_heartbeat(AuthHandle handle);
CRAFTLABS_API void auth_destroy(AuthHandle handle);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,11 @@
/*
* 比特安索(Bitanswer)适配器实现(占位)。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#include "bitanswer_adapter.h"
void bitanswer_adapter_register(void) {
/* 后续里程碑:在此挂接 Bit_LoginEx、Bit_QueryFeature 等与 craftlabs_auth 生命周期对齐。 */
}
+20
View File
@@ -0,0 +1,20 @@
/*
* 比特安索(Bitanswer)适配器注册入口。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* 向核心库注册 Bitanswer 相关钩子;当前里程碑可为空实现,后续接入 Bit_LoginEx / Bit_QueryFeature 等。
*/
void bitanswer_adapter_register(void);
#ifdef __cplusplus
}
#endif
+112
View File
@@ -0,0 +1,112 @@
/*
* CraftLabs 授权 SDK — 核心 C API 实现(含桩逻辑与各适配器注册)。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#include "craftlabs_auth.h"
#include <cstdlib>
#include <cstring>
#include "bitanswer/bitanswer_adapter.h"
#include "selfhosted/selfhosted_adapter.h"
namespace {
/** 不透明句柄对应的内部状态;后续可扩展为供应商特定字段。 */
struct AuthContext {
int dummy;
};
static const AuthResult k_ok = {1, "stub"};
static const AuthResult k_fail = {0, "stub failure"};
AuthContext* as_ctx(AuthHandle h) {
return reinterpret_cast<AuthContext*>(h);
}
void ensure_adapters_registered_once() {
static bool once = false;
if (!once) {
bitanswer_adapter_register();
selfhosted_adapter_register();
once = true;
}
}
} // namespace
extern "C" {
CRAFTLABS_API AuthHandle auth_initialize(const char* /* config_json */) {
ensure_adapters_registered_once();
auto* ctx = new AuthContext{};
ctx->dummy = 1;
return reinterpret_cast<AuthHandle>(ctx);
}
CRAFTLABS_API AuthResult auth_activate(AuthHandle handle, const char* /* license_key */) {
if (!handle) {
return k_fail;
}
(void)as_ctx(handle);
return k_ok;
}
CRAFTLABS_API AuthResult auth_check_license(AuthHandle handle) {
if (!handle) {
return k_fail;
}
return k_ok;
}
CRAFTLABS_API LicenseInfo* auth_get_license_info(AuthHandle handle) {
if (!handle) {
return nullptr;
}
auto* info = static_cast<LicenseInfo*>(std::malloc(sizeof(LicenseInfo)));
if (!info) {
return nullptr;
}
info->is_licensed = 1;
info->expiration_date = 0;
info->feature_names = nullptr;
info->feature_values = nullptr;
info->feature_count = 0;
return info;
}
CRAFTLABS_API void auth_free_license_info(LicenseInfo* info) {
std::free(info);
}
CRAFTLABS_API int32_t auth_has_feature(AuthHandle handle, const char* /* feature_name */) {
if (!handle) {
return 0;
}
return 1;
}
CRAFTLABS_API AuthResult auth_release(AuthHandle handle) {
if (!handle) {
return k_fail;
}
return k_ok;
}
CRAFTLABS_API AuthResult auth_heartbeat(AuthHandle handle) {
if (!handle) {
return k_fail;
}
return k_ok;
}
CRAFTLABS_API void auth_destroy(AuthHandle handle) {
if (!handle) {
return;
}
delete as_ctx(handle);
}
}
+151
View File
@@ -0,0 +1,151 @@
/*
* JNI 桥:java 包 cn.craftlabs.auth.internal.NativeBridge 与 craftlabs_auth.h C API 之间的映射。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#include <cstdint>
#include <jni.h>
#include "craftlabs_auth.h"
namespace {
AuthHandle from_jlong(jlong ptr) {
return reinterpret_cast<AuthHandle>(static_cast<uintptr_t>(ptr));
}
jobject make_auth_result(JNIEnv* env, const AuthResult& r) {
jclass cls = env->FindClass("cn/craftlabs/auth/AuthResult");
if (!cls) {
return nullptr;
}
jmethodID ctor = env->GetMethodID(cls, "<init>", "(ZLjava/lang/String;)V");
if (!ctor) {
return nullptr;
}
jboolean ok = (r.success != 0) ? JNI_TRUE : JNI_FALSE;
jstring msg = env->NewStringUTF(r.message ? r.message : "");
if (!msg) {
return nullptr;
}
jobject out = env->NewObject(cls, ctor, ok, msg);
env->DeleteLocalRef(msg);
return out;
}
jobject make_license_info(JNIEnv* env, LicenseInfo* info) {
if (!info) {
return nullptr;
}
jclass cls = env->FindClass("cn/craftlabs/auth/LicenseInfo");
if (!cls) {
return nullptr;
}
jmethodID ctor = env->GetMethodID(cls, "<init>", "(ZLjava/util/Date;Ljava/util/Map;)V");
if (!ctor) {
return nullptr;
}
jboolean licensed = info->is_licensed ? JNI_TRUE : JNI_FALSE;
jobject date = nullptr;
if (info->expiration_date != 0) {
jclass dateCls = env->FindClass("java/util/Date");
jmethodID dateCtor = env->GetMethodID(dateCls, "<init>", "(J)V");
jlong ms = static_cast<jlong>(info->expiration_date) * 1000LL;
date = env->NewObject(dateCls, dateCtor, ms);
}
jclass mapCls = env->FindClass("java/util/HashMap");
jmethodID mapCtor = env->GetMethodID(mapCls, "<init>", "()V");
jobject map = env->NewObject(mapCls, mapCtor);
jmethodID put = env->GetMethodID(mapCls, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
for (int i = 0; i < info->feature_count; ++i) {
const char* name = info->feature_names[i];
int v = info->feature_values[i];
jstring jn = env->NewStringUTF(name ? name : "");
jclass boolCls = env->FindClass("java/lang/Boolean");
jmethodID valueOf =
env->GetStaticMethodID(boolCls, "valueOf", "(Z)Ljava/lang/Boolean;");
jobject jb = env->CallStaticObjectMethod(boolCls, valueOf, v ? JNI_TRUE : JNI_FALSE);
env->CallObjectMethod(map, put, jn, jb);
env->DeleteLocalRef(jn);
env->DeleteLocalRef(jb);
}
jobject out = env->NewObject(cls, ctor, licensed, date, map);
if (date) {
env->DeleteLocalRef(date);
}
env->DeleteLocalRef(map);
return out;
}
} // namespace
extern "C" {
JNIEXPORT jlong JNICALL Java_cn_craftlabs_auth_internal_NativeBridge_nativeInitialize(
JNIEnv* env, jclass, jstring configJson) {
const char* utf = configJson ? env->GetStringUTFChars(configJson, nullptr) : nullptr;
AuthHandle h = auth_initialize(utf ? utf : "{}");
if (configJson && utf) {
env->ReleaseStringUTFChars(configJson, utf);
}
return static_cast<jlong>(reinterpret_cast<uintptr_t>(h));
}
JNIEXPORT void JNICALL Java_cn_craftlabs_auth_internal_NativeBridge_nativeDestroy(JNIEnv*,
jclass,
jlong handle) {
auth_destroy(from_jlong(handle));
}
JNIEXPORT jobject JNICALL Java_cn_craftlabs_auth_internal_NativeBridge_nativeActivate(
JNIEnv* env, jclass, jlong handle, jstring licenseKey) {
const char* utf = licenseKey ? env->GetStringUTFChars(licenseKey, nullptr) : "";
AuthResult r = auth_activate(from_jlong(handle), utf ? utf : "");
if (licenseKey && utf) {
env->ReleaseStringUTFChars(licenseKey, utf);
}
return make_auth_result(env, r);
}
JNIEXPORT jobject JNICALL Java_cn_craftlabs_auth_internal_NativeBridge_nativeCheckLicense(
JNIEnv* env, jclass, jlong handle) {
AuthResult r = auth_check_license(from_jlong(handle));
return make_auth_result(env, r);
}
JNIEXPORT jobject JNICALL Java_cn_craftlabs_auth_internal_NativeBridge_nativeGetLicenseInfo(
JNIEnv* env, jclass, jlong handle) {
LicenseInfo* info = auth_get_license_info(from_jlong(handle));
jobject jinfo = make_license_info(env, info);
auth_free_license_info(info);
return jinfo;
}
JNIEXPORT jboolean JNICALL Java_cn_craftlabs_auth_internal_NativeBridge_nativeHasFeature(
JNIEnv* env, jclass, jlong handle, jstring featureName) {
const char* utf = featureName ? env->GetStringUTFChars(featureName, nullptr) : "";
int v = auth_has_feature(from_jlong(handle), utf ? utf : "");
if (featureName && utf) {
env->ReleaseStringUTFChars(featureName, utf);
}
return v ? JNI_TRUE : JNI_FALSE;
}
JNIEXPORT jobject JNICALL Java_cn_craftlabs_auth_internal_NativeBridge_nativeRelease(JNIEnv* env,
jclass,
jlong handle) {
AuthResult r = auth_release(from_jlong(handle));
return make_auth_result(env, r);
}
JNIEXPORT jobject JNICALL Java_cn_craftlabs_auth_internal_NativeBridge_nativeHeartbeat(
JNIEnv* env, jclass, jlong handle) {
AuthResult r = auth_heartbeat(from_jlong(handle));
return make_auth_result(env, r);
}
}
+12
View File
@@ -0,0 +1,12 @@
/*
* 自研适配器 HTTP 客户端桩实现。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#include "http_client.h"
int selfhosted_http_ping(const char* /* base_url */) {
/* 接入 libcurl 等之后再实现真实请求;当前保证注册阶段可链接通过。 */
return 0;
}
+22
View File
@@ -0,0 +1,22 @@
/*
* 自研适配器用的最小 HTTP 门面(后续可接 libcurl / WinHTTP)。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* 对给定基地址做连通性探测;当前为占位实现。
* @param base_url 服务根 URL,可为空(实现定义行为)。
* @return 0 表示占位成功,非 0 预留为错误码。
*/
int selfhosted_http_ping(const char* base_url);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,12 @@
/*
* 自研 HTTP 授权适配器实现。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#include "selfhosted_adapter.h"
#include "http_client.h"
void selfhosted_adapter_register(void) {
(void)selfhosted_http_ping("");
}
@@ -0,0 +1,18 @@
/*
* 自研 HTTP 授权适配器 — 注册入口。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/** 注册自研适配器依赖(如 HTTP 客户端探测);可与 Bitanswer 注册并存。 */
void selfhosted_adapter_register(void);
#ifdef __cplusplus
}
#endif
+34
View File
@@ -0,0 +1,34 @@
/*
* Native 库冒烟测试。
*
* 版权所有 © 广州创飞人工智能技术有限公司
* 开发者:huangping@craftlabs.cn
*/
#include "craftlabs_auth.h"
#include <cstdio>
#include <cstdlib>
int main() {
AuthHandle h = auth_initialize("{}");
if (!h) {
std::fprintf(stderr, "auth_initialize returned null\n");
return EXIT_FAILURE;
}
AuthResult r = auth_check_license(h);
if (r.success == 0) {
std::fprintf(stderr, "auth_check_license failed\n");
auth_destroy(h);
return EXIT_FAILURE;
}
LicenseInfo* info = auth_get_license_info(h);
if (!info) {
std::fprintf(stderr, "auth_get_license_info returned null\n");
auth_destroy(h);
return EXIT_FAILURE;
}
auth_free_license_info(info);
auth_destroy(h);
std::printf("native smoke ok\n");
return EXIT_SUCCESS;
}