Skip to content

Java SDK

提供 A7Client 参考实现,基于 Java 11+ 标准库(java.net.http.HttpClientjava.security.MessageDigest、Gson 或 javax.json 解析 JSON)。本示例使用 org.json

依赖

xml
<dependency>
  <groupId>org.json</groupId>
  <artifactId>json</artifactId>
  <version>20240303</version>
</dependency>

完整参考实现

A7Client.java

java
import org.json.JSONObject;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Map;
import java.util.TreeMap;

public class A7Client {
    private static final String BASE = "https://api.a7p.cn/api/verify";
    private final String appId;
    private final String appKey;
    private String apiPassword = "";
    private final HttpClient http = HttpClient.newHttpClient();

    public A7Client(String appId, String appKey) {
        this.appId = appId;
        this.appKey = appKey;
    }

    private static String md5(String text) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] b = md.digest(text.getBytes(StandardCharsets.UTF_8));
        StringBuilder sb = new StringBuilder();
        for (byte x : b) sb.append(String.format("%02x", x));
        return sb.toString();
    }

    private static String rc4Hex(String key, String data) {
        byte[] k = key.getBytes(StandardCharsets.UTF_8);
        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] + (k[i % k.length] & 0xff)) % 256;
            int t = s[i]; s[i] = s[j]; s[j] = t;
        }
        byte[] p = data.getBytes(StandardCharsets.UTF_8);
        byte[] out = new byte[p.length];
        int x = 0, y = 0;
        for (int i = 0; i < p.length; i++) {
            x = (x + 1) % 256;
            y = (y + s[x]) % 256;
            int t = s[x]; s[x] = s[y]; s[y] = t;
            out[i] = (byte) (p[i] ^ s[(s[x] + s[y]) % 256]);
        }
        StringBuilder sb = new StringBuilder();
        for (byte b : out) sb.append(String.format("%02X", b));
        return sb.toString();
    }

    private JSONObject post(String url, Map<String, String> form, Map<String, String> headers) throws Exception {
        StringBuilder body = new StringBuilder();
        for (var e : form.entrySet()) {
            if (body.length() > 0) body.append("&");
            body.append(e.getKey()).append("=").append(e.getValue());
        }
        HttpRequest.Builder b = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString(body.toString()));
        for (var h : headers.entrySet()) b.header(h.getKey(), h.getValue());
        HttpResponse<String> r = http.send(b.build(), HttpResponse.BodyHandlers.ofString());
        return new JSONObject(r.body());
    }

    public void init() throws Exception {
        long ts = System.currentTimeMillis() / 1000;
        String sign = md5(appId + appKey + ts + appKey);
        Map<String, String> form = new TreeMap<>();
        form.put("AppId", appId);
        form.put("AppKey", appKey);
        form.put("TimeStamp", String.valueOf(ts));
        form.put("Sign", sign);
        JSONObject resp = post(BASE, form, Map.of());
        if (resp.getInt("code") != 200)
            throw new RuntimeException("Init 失败: " + resp.getInt("code") + " " + resp.getString("message"));
        apiPassword = resp.getJSONObject("data").getString("api_password");
    }

    public JSONObject call(String action, Map<String, String> params) throws Exception {
        if (apiPassword.isEmpty()) init();
        Map<String, String> sorted = new TreeMap<>();
        if (params != null) sorted.putAll(params);
        StringBuilder plain = new StringBuilder("action=").append(action);
        for (var e : sorted.entrySet())
            plain.append("&").append(e.getKey()).append("=").append(e.getValue());
        long ts = System.currentTimeMillis() / 1000;
        String data = rc4Hex(apiPassword, plain.toString());
        String sign = md5(apiPassword + ts + data);
        Map<String, String> form = new TreeMap<>();
        form.put("TimeStamp", String.valueOf(ts));
        form.put("Data", data);
        form.put("Sign", sign);
        return post(BASE, form, Map.of("X-Api-Password", apiPassword));
    }
}

使用示例

java
A7Client client = new A7Client("150018", "YOUR_APP_KEY");
Map<String, String> params = new java.util.HashMap<>();
params.put("Card", "UTDE-7TCX-N5AT-ZDSP");
params.put("Mac", "TEST-ABC-123");
JSONObject resp = client.call("SingleLogin", params);
if (resp.getInt("code") == 200) {
    System.out.println("token: " + resp.getJSONObject("data").getString("token"));
} else {
    System.out.println("失败: " + resp.getInt("code") + " " + resp.getString("message"));
}

注意事项

  • apiPassword 仅保存在实例字段。
  • 使用 TreeMap 排序拼接明文,结果可复现(顺序本身不影响服务端解析)。
  • 更多接口参数见 API 参考

相关链接

A7验证 · 软件授权与网络验证平台