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
+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>