以下是完整的 Java 验证客户端实现,使用 JDK 内置库,无需第三方依赖。
package com.auth.sdk;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 网络验证系统 Java SDK
*/
public class AuthClient {
private final String appKey;
private final String appSecret;
private final String transferKey;
private final String apiUrl;
// 登录状态
private String token = "";
private String execToken = "";
private String username = "";
private String expireTime = "";
private int points = 0;
private int heartbeatInterval = 30;
// 错误信息
private int lastCode = 0;
private String lastMsg = "";
// 心跳线程
private Thread heartbeatThread;
private final AtomicBoolean heartbeatRunning = new AtomicBoolean(false);
private String machineCode = "";
public AuthClient(String appKey, String appSecret, String transferKey, String apiUrl) {
this.appKey = appKey;
this.appSecret = appSecret;
this.transferKey = transferKey;
this.apiUrl = apiUrl;
}
// ==================== 加密算法 ====================
/**
* RC4 加密/解密
*/
private static byte[] rc4(byte[] data, byte[] key) {
int[] S = new int[256];
for (int i = 0; i < 256; i++) S[i] = i;
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + S[i] + (key[i % key.length] & 0xFF)) % 256;
int tmp = S[i]; S[i] = S[j]; S[j] = tmp;
}
byte[] result = new byte[data.length];
int x = 0, y = 0;
for (int k = 0; k < data.length; k++) {
x = (x + 1) % 256;
y = (y + S[x]) % 256;
int tmp = S[x]; S[x] = S[y]; S[y] = tmp;
result[k] = (byte)(data[k] ^ S[(S[x] + S[y]) % 256]);
}
return result;
}
/**
* HMAC-SHA256 签名
*/
private static String hmacSha256(String data, String key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder();
for (byte b : hash) {
hex.append(String.format("%02x", b));
}
return hex.toString();
}
/**
* 字节数组转HEX
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hex = new StringBuilder();
for (byte b : bytes) {
hex.append(String.format("%02x", b));
}
return hex.toString();
}
/**
* HEX转字节数组
*/
private static byte[] hexToBytes(String hex) {
byte[] bytes = new byte[hex.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
}
return bytes;
}
/**
* 生成随机字符串
*/
private static String generateNonce(int length) {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
SecureRandom random = new SecureRandom();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
sb.append(chars.charAt(random.nextInt(chars.length())));
}
return sb.toString();
}
/**
* 获取Unix时间戳
*/
private static long getTimestamp() {
return System.currentTimeMillis() / 1000;
}
// ==================== 请求处理 ====================
/**
* 构建加密请求
*/
private String buildRequest(Map<String, String> params) throws Exception {
// 按key排序
TreeMap<String, String> sorted = new TreeMap<>(params);
// 拼接参数
StringBuilder data = new StringBuilder();
for (Map.Entry<String, String> entry : sorted.entrySet()) {
if (data.length() > 0) data.append("&");
data.append(entry.getKey()).append("=").append(entry.getValue());
}
// 计算签名
String sign = hmacSha256(data.toString(), appSecret);
data.append("&sign=").append(sign);
// RC4加密
byte[] encrypted = rc4(data.toString().getBytes(StandardCharsets.UTF_8),
transferKey.getBytes(StandardCharsets.UTF_8));
return bytesToHex(encrypted);
}
/**
* 解密响应
*/
private String decryptResponse(String response) {
if (response == null || response.isEmpty() || response.startsWith("{")) {
return response; // 明文响应
}
// HEX解码并RC4解密
byte[] encrypted = hexToBytes(response);
byte[] decrypted = rc4(encrypted, transferKey.getBytes(StandardCharsets.UTF_8));
return new String(decrypted, StandardCharsets.UTF_8);
}
/**
* 发送HTTP GET请求
*/
private String httpGet(String urlStr) throws Exception {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
return response.toString();
} finally {
conn.disconnect();
}
}
/**
* 发送API请求
*/
private Map<String, Object> sendRequest(Map<String, String> params) {
try {
String encData = buildRequest(params);
String url = apiUrl + "?app_key=" + URLEncoder.encode(appKey, "UTF-8")
+ "&data=" + encData;
String response = httpGet(url);
String decrypted = decryptResponse(response);
Map<String, Object> result = parseJson(decrypted);
lastCode = ((Number) result.getOrDefault("code", -1)).intValue();
lastMsg = (String) result.getOrDefault("msg", "");
return result;
} catch (Exception e) {
lastCode = -1;
lastMsg = e.getMessage();
return new HashMap<>();
}
}
/**
* 简单JSON解析
*/
private static Map<String, Object> parseJson(String json) {
Map<String, Object> result = new HashMap<>();
if (json == null || !json.startsWith("{")) return result;
// 匹配字符串值
Pattern strPattern = Pattern.compile("\"(\\w+)\"\\s*:\\s*\"([^\"]*)\"");
Matcher strMatcher = strPattern.matcher(json);
while (strMatcher.find()) {
result.put(strMatcher.group(1), strMatcher.group(2));
}
// 匹配数字值
Pattern numPattern = Pattern.compile("\"(\\w+)\"\\s*:\\s*(-?\\d+)");
Matcher numMatcher = numPattern.matcher(json);
while (numMatcher.find()) {
String key = numMatcher.group(1);
if (!result.containsKey(key)) {
result.put(key, Long.parseLong(numMatcher.group(2)));
}
}
return result;
}
// ==================== API方法 ====================
/**
* 初始化
*/
public boolean init(String machineCode, String version) {
Map<String, String> params = new HashMap<>();
params.put("action", "init");
params.put("machine_code", machineCode);
params.put("nonce", generateNonce(16));
params.put("timestamp", String.valueOf(getTimestamp()));
if (version != null && !version.isEmpty()) {
params.put("version", version);
}
Map<String, Object> result = sendRequest(params);
if (lastCode != 1) return false;
heartbeatInterval = ((Number) result.getOrDefault("heartbeat_interval", 30)).intValue();
if (heartbeatInterval <= 0) heartbeatInterval = 30;
return true;
}
/**
* 登录(统一接口)
* @param machineCode 机器码
* @param username 用户名或卡密
* @param password 密码(卡密登录传"0")
*/
public boolean login(String machineCode, String username, String password) {
this.machineCode = machineCode;
Map<String, String> params = new HashMap<>();
params.put("action", "login");
params.put("machine_code", machineCode);
params.put("nonce", generateNonce(16));
params.put("password", password);
params.put("timestamp", String.valueOf(getTimestamp()));
params.put("username", username);
Map<String, Object> result = sendRequest(params);
if (lastCode != 2) return false;
token = (String) result.getOrDefault("token", "");
execToken = (String) result.getOrDefault("exec_token", "");
this.username = (String) result.getOrDefault("username", "");
expireTime = (String) result.getOrDefault("expire_time", "");
points = ((Number) result.getOrDefault("points", 0)).intValue();
return true;
}
/**
* 心跳
*/
public boolean heartbeat(String machineCode) {
Map<String, String> params = new HashMap<>();
params.put("action", "heartbeat");
params.put("timestamp", String.valueOf(getTimestamp()));
params.put("token", token);
if (machineCode != null && !machineCode.isEmpty()) {
params.put("machine_code", machineCode);
}
Map<String, Object> result = sendRequest(params);
if (lastCode != 3) return false;
expireTime = (String) result.getOrDefault("expire_time", expireTime);
points = ((Number) result.getOrDefault("points", points)).intValue();
execToken = (String) result.getOrDefault("exec_token", execToken);
return true;
}
/**
* 登出
*/
public boolean logout(String machineCode) {
stopHeartbeat();
Map<String, String> params = new HashMap<>();
params.put("action", "logout");
params.put("timestamp", String.valueOf(getTimestamp()));
params.put("token", token);
if (machineCode != null && !machineCode.isEmpty()) {
params.put("machine_code", machineCode);
}
sendRequest(params);
return lastCode == 4;
}
/**
* 扣点
*/
public boolean deduct(int points, String requestId, String machineCode) {
Map<String, String> params = new HashMap<>();
params.put("action", "deduct");
params.put("points", String.valueOf(points));
params.put("timestamp", String.valueOf(getTimestamp()));
params.put("token", token);
if (requestId != null && !requestId.isEmpty()) {
params.put("request_id", requestId);
} else {
params.put("request_id", UUID.randomUUID().toString());
}
if (machineCode != null && !machineCode.isEmpty()) {
params.put("machine_code", machineCode);
}
Map<String, Object> result = sendRequest(params);
if (lastCode != 8) return false;
this.points = ((Number) result.getOrDefault("points", this.points)).intValue();
return true;
}
/**
* 启动心跳线程
*/
public void startHeartbeat(Consumer<Boolean> callback) {
if (heartbeatRunning.get()) return;
heartbeatRunning.set(true);
heartbeatThread = new Thread(() -> {
while (heartbeatRunning.get()) {
try {
Thread.sleep(heartbeatInterval * 1000L);
} catch (InterruptedException e) {
break;
}
if (!heartbeatRunning.get()) break;
boolean success = heartbeat(machineCode);
if (callback != null) {
callback.accept(success);
}
if (!success) {
heartbeatRunning.set(false);
break;
}
}
});
heartbeatThread.setDaemon(true);
heartbeatThread.start();
}
/**
* 停止心跳线程
*/
public void stopHeartbeat() {
heartbeatRunning.set(false);
if (heartbeatThread != null) {
heartbeatThread.interrupt();
}
}
// ==================== Getters ====================
public String getToken() { return token; }
public String getUsername() { return username; }
public String getExpireTime() { return expireTime; }
public int getPoints() { return points; }
public int getHeartbeatInterval() { return heartbeatInterval; }
public int getLastCode() { return lastCode; }
public String getLastMsg() { return lastMsg; }
}
package com.auth.sdk;
import java.util.Scanner;
public class Main {
// 获取机器码(示例)
private static String getMachineCode() {
try {
String os = System.getProperty("os.name");
String user = System.getProperty("user.name");
String arch = System.getProperty("os.arch");
return Integer.toHexString((os + user + arch).hashCode()).toUpperCase();
} catch (Exception e) {
return "DEFAULT_MACHINE_CODE";
}
}
public static void main(String[] args) {
// 配置
final String APP_KEY = "your_app_key";
final String APP_SECRET = "your_app_secret";
final String TRANSFER_KEY = "your_transfer_key";
final String API_URL = "https://your-server.com/api/v1/client/enc";
AuthClient client = new AuthClient(APP_KEY, APP_SECRET, TRANSFER_KEY, API_URL);
String machineCode = getMachineCode();
Scanner scanner = new Scanner(System.in);
// 1. 初始化
System.out.println("正在初始化...");
if (!client.init(machineCode, "1.0.0")) {
System.out.println("初始化失败: " + client.getLastMsg());
return;
}
System.out.println("初始化成功,心跳间隔: " + client.getHeartbeatInterval() + "秒");
// 2. 登录
System.out.print("请输入卡密: ");
String cardNo = scanner.nextLine().trim();
System.out.println("正在登录...");
if (!client.login(machineCode, cardNo, "0")) {
System.out.println("登录失败: " + client.getLastMsg() + " (code: " + client.getLastCode() + ")");
return;
}
System.out.println("登录成功!");
System.out.println("用户名: " + client.getUsername());
System.out.println("到期时间: " + client.getExpireTime());
System.out.println("剩余点数: " + client.getPoints());
// 3. 启动心跳
client.startHeartbeat(success -> {
if (!success) {
System.out.println("\n心跳失败: " + client.getLastMsg());
}
});
// 4. 主循环
System.out.println("\n输入 q 退出,输入 d 扣点...");
while (true) {
String cmd = scanner.nextLine().trim().toLowerCase();
if ("q".equals(cmd)) {
break;
} else if ("d".equals(cmd)) {
if (client.deduct(1, null, machineCode)) {
System.out.println("扣点成功,剩余: " + client.getPoints());
} else {
System.out.println("扣点失败: " + client.getLastMsg());
}
}
}
// 5. 退出
client.logout(machineCode);
System.out.println("已退出登录");
scanner.close();
}
}
# 编译
javac -encoding UTF-8 -d out src/com/auth/sdk/*.java
# 运行
java -cp out com.auth.sdk.Main