应用签名联网验证模块完成。
This commit is contained in:
100
src/cc/winboll/app/AppSignaturesUtils.java
Normal file
100
src/cc/winboll/app/AppSignaturesUtils.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package cc.winboll.app;
|
||||
|
||||
import cc.winboll.LogUtils;
|
||||
import cc.winboll.util.IniConfigUtils;
|
||||
import java.util.Base64;
|
||||
|
||||
public class AppSignaturesUtils {
|
||||
// 1. 懒汉单例(解决过早加载问题)
|
||||
private static AppSignaturesUtils INSTANCE = null;
|
||||
private static final String CONFIG_SECTION = "APP";
|
||||
private static final String KEY_SIGN_FINGERPRINT = "app_sign_fingerprint";
|
||||
private static final String KEY_EFFECTIVE_TIME = "app_sign_effective_time";
|
||||
private String targetSign;
|
||||
private long effectiveTime;
|
||||
|
||||
// 私有构造
|
||||
private AppSignaturesUtils() {}
|
||||
|
||||
// 2. 懒加载单例+确保配置已加载
|
||||
public static AppSignaturesUtils getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (AppSignaturesUtils.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new AppSignaturesUtils();
|
||||
INSTANCE.initConfig(); // 延迟初始化配置
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
// 公共init方法(供外部主动初始化,可选)
|
||||
public static void init() {
|
||||
getInstance();
|
||||
}
|
||||
|
||||
private void initConfig() {
|
||||
try {
|
||||
this.targetSign = IniConfigUtils.getConfigValue(CONFIG_SECTION, KEY_SIGN_FINGERPRINT, "").trim();
|
||||
String timeStr = IniConfigUtils.getConfigValue(CONFIG_SECTION, KEY_EFFECTIVE_TIME, "0").trim();
|
||||
this.effectiveTime = Long.parseLong(timeStr);
|
||||
LogUtils.i("AppSignaturesUtils", "配置读取完成|目标签名:" + targetSign + "|生效时间戳:" + effectiveTime);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e("AppSignaturesUtils", "配置读取失败", e);
|
||||
this.targetSign = "";
|
||||
this.effectiveTime = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checksignatures(String signature, long validTime) {
|
||||
return getInstance().doCheck(signature, validTime);
|
||||
}
|
||||
|
||||
private boolean doCheck(String signature, long validTime) {
|
||||
if (signature == null || signature.isEmpty() || targetSign.isEmpty() || effectiveTime == 0) {
|
||||
LogUtils.w("AppSignaturesUtils", "校验失败:签名为空或配置未正确加载");
|
||||
return false;
|
||||
}
|
||||
|
||||
String decryptedSign;
|
||||
try {
|
||||
byte[] signBytes = Base64.getDecoder().decode(signature);
|
||||
decryptedSign = new String(signBytes, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
LogUtils.w("AppSignaturesUtils", "签名解密失败", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean signMatch = targetSign.equals(decryptedSign);
|
||||
boolean timeValid = validTime >= effectiveTime;
|
||||
LogUtils.d("AppSignaturesUtils", "解密后签名:" + decryptedSign + "|签名匹配:" + signMatch + "|时间有效:" + timeValid);
|
||||
|
||||
return signMatch && timeValid;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
test();
|
||||
}
|
||||
|
||||
private static void test() {
|
||||
IniConfigUtils.init();
|
||||
LogUtils.init();
|
||||
AppSignaturesUtils.init();
|
||||
|
||||
// 关键修改:手动生成正确签名,彻底杜绝复制粘贴隐形字符
|
||||
String rawSign = "WinBoLL_AuthCenter_Valid_Sign";
|
||||
String testSignature = Base64.getEncoder().encodeToString(rawSign.getBytes());
|
||||
long testValidTime = 1769000000000L;
|
||||
|
||||
System.out.println("===== AppSignaturesUtils 单元测试 =====");
|
||||
System.out.println("原文字符串:" + rawSign);
|
||||
System.out.println("自动Base64编码后:" + testSignature);
|
||||
System.out.println("示例生效时间戳:" + testValidTime);
|
||||
|
||||
boolean result = AppSignaturesUtils.checksignatures(testSignature, testValidTime);
|
||||
System.out.println("校验结果:" + (result ? "✅ 成功" : "❌ 失败"));
|
||||
System.out.println("======================================");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
package cc.winboll.service;
|
||||
|
||||
import cc.winboll.LogUtils;
|
||||
import cc.winboll.util.ServerUtils;
|
||||
import cc.winboll.util.ConsoleVersionUtils;
|
||||
import fi.iki.elonen.NanoHTTPD;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 独立HTTP监听服务类,仅负责请求接收与分发,业务逻辑完全依赖ServerUtils
|
||||
* HTTP监听服务类,仅负责服务启停、请求接收与分发
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026-01-15 23:45:00
|
||||
* @LastEditTime 新增/api/version版本查询接口
|
||||
* @LastEditTime 2026-01-22 修复GET参数解析+签名接口路由
|
||||
*/
|
||||
public class AuthCenterHttpService extends NanoHTTPD {
|
||||
private static final String TAG = "AuthCenterHttpService";
|
||||
private boolean isRunning = false;
|
||||
// 依赖业务处理工具类(通过构造注入,便于测试)
|
||||
private final AuthHttpHandler mHttpHandler;
|
||||
|
||||
// 构造方法:传入业务处理工具类
|
||||
public AuthCenterHttpService(int port) {
|
||||
super(port);
|
||||
LogUtils.d(TAG, "构造方法调用,监听端口:" + port);
|
||||
this.mHttpHandler = new AuthHttpHandler(); // 默认初始化工具类
|
||||
LogUtils.d(TAG, "构造方法调用,监听端口:" + port + ",业务处理工具类初始化完成");
|
||||
}
|
||||
|
||||
// 重载构造:支持外部注入工具类(便于单元测试)
|
||||
public AuthCenterHttpService(int port, AuthHttpHandler httpHandler) {
|
||||
super(port);
|
||||
this.mHttpHandler = httpHandler;
|
||||
LogUtils.d(TAG, "构造方法调用(外部注入工具类),监听端口:" + port);
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
@@ -48,111 +57,78 @@ public class AuthCenterHttpService extends NanoHTTPD {
|
||||
LogUtils.d(TAG, "接收请求:method=" + method.name() + ",原始uri=" + rawUri + ",规范化uri=" + normUri);
|
||||
|
||||
try {
|
||||
// 新增 /api/version 版本查询接口(GET)
|
||||
if (Method.GET.equals(method) && "/api/version".equals(normUri)) {
|
||||
return handleVersionQuery();
|
||||
} else if (Method.GET.equals(method) && "/".equals(normUri)) {
|
||||
return handleHelloWorld();
|
||||
// 请求路由分发(仅做判断,业务逻辑交给工具类)
|
||||
if (Method.GET.equals(method)) {
|
||||
return handleGetRequest(normUri, session); // 传session
|
||||
} else if (Method.POST.equals(method)) {
|
||||
return handlePostRequest(normUri, session);
|
||||
} else {
|
||||
LogUtils.d(TAG, "非目标请求,返回404");
|
||||
return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "404 Not Found");
|
||||
LogUtils.w(TAG, "不支持的请求方法:" + method.name());
|
||||
return mHttpHandler.buildErrorResponse(Response.Status.METHOD_NOT_ALLOWED, "不支持的请求方法");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "请求处理异常", e);
|
||||
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "application/json",
|
||||
"{\"code\":500,\"msg\":\"服务内部异常\",\"data\":null}");
|
||||
return mHttpHandler.buildErrorResponse(Response.Status.INTERNAL_ERROR, "服务内部异常");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理根路径默认请求
|
||||
*/
|
||||
private Response handleHelloWorld() {
|
||||
LogUtils.d(TAG, "根路径请求响应成功,返回服务运行标识");
|
||||
return newFixedLengthResponse(Response.Status.OK, "text/plain", "WinBoLL is Running...");
|
||||
}
|
||||
// 修复:GET路由传session,用NanoHTTPD原生方法解析参数(更可靠)
|
||||
private Response handleGetRequest(String normUri, IHTTPSession session) {
|
||||
// 优先处理favicon.ico,消除冗余日志
|
||||
if ("/favicon.ico".equals(normUri)) {
|
||||
return NanoHTTPD.newFixedLengthResponse(Response.Status.NO_CONTENT, "text/plain", "");
|
||||
}
|
||||
|
||||
// 新增:版本查询接口实现,调用ConsoleVersionUtils返回结果
|
||||
private Response handleVersionQuery() {
|
||||
String version = ConsoleVersionUtils.getVersion();
|
||||
LogUtils.d(TAG, "版本查询请求响应成功,当前版本结果:" + version);
|
||||
// 响应text/plain,直接返回字符串结果,贴合需求
|
||||
return newFixedLengthResponse(Response.Status.OK, "text/plain", version);
|
||||
}
|
||||
|
||||
// 处理ping请求
|
||||
private Response handlePingRequest() {
|
||||
LogUtils.d(TAG, "ping请求响应成功,返回pong");
|
||||
return newFixedLengthResponse(Response.Status.OK, "application/json",
|
||||
"{\"code\":200,\"msg\":\"pong\",\"data\":null}");
|
||||
}
|
||||
|
||||
// 发送验证码:修改为调用本地方法,解决递归问题
|
||||
private Response handleSendVerifyCode(IHTTPSession session) throws IOException, NanoHTTPD.ResponseException{
|
||||
session.parseBody(null);
|
||||
// 原生解析GET参数,无需自己拆分URI
|
||||
Map<String, String> params = session.getParms();
|
||||
String email = params.get("email");
|
||||
LogUtils.d(TAG, "接收验证码发送请求,邮箱:" + email);
|
||||
|
||||
// 核心修改:替换为本地发送方法,返回布尔值
|
||||
boolean sendResult = ServerUtils.sendVerifyCodeLocal(email);
|
||||
// 统一返回JSON格式,与其他接口保持一致
|
||||
String responseJson;
|
||||
if (sendResult) {
|
||||
responseJson = "{\"code\":200,\"msg\":\"验证码发送成功\",\"data\":null}";
|
||||
} else {
|
||||
responseJson = "{\"code\":500,\"msg\":\"验证码发送失败\",\"data\":null}";
|
||||
switch (normUri) {
|
||||
case "/":
|
||||
return mHttpHandler.handleHelloWorld();
|
||||
case "/api/version":
|
||||
return mHttpHandler.handleVersionQuery();
|
||||
case "/ping":
|
||||
return mHttpHandler.handlePingRequest();
|
||||
// 签名校验接口:传原生解析的参数
|
||||
case "/api/app-signatures-check":
|
||||
return mHttpHandler.handleAppSignatureCheck(params);
|
||||
default:
|
||||
LogUtils.d(TAG, "GET请求未匹配:" + normUri);
|
||||
return mHttpHandler.buildErrorResponse(Response.Status.NOT_FOUND, "404 Not Found");
|
||||
}
|
||||
return newFixedLengthResponse(Response.Status.OK, "application/json", responseJson);
|
||||
}
|
||||
|
||||
// 校验验证码:直接调用ServerUtils
|
||||
private Response handleVerifyCode(IHTTPSession session) throws IOException, NanoHTTPD.ResponseException {
|
||||
session.parseBody(null);
|
||||
Map<String, String> params = session.getParms();
|
||||
String email = params.get("email");
|
||||
String code = params.get("code");
|
||||
LogUtils.d(TAG, "接收验证码校验请求,邮箱:" + email + ",验证码:" + code);
|
||||
// 移除自己写的getParameters方法(原生session.getParms()更稳定,避免拆分bug)
|
||||
|
||||
String result = ServerUtils.verifyCode(email, code);
|
||||
// 兜底处理null值,避免返回空响应
|
||||
if (result == null) {
|
||||
result = "{\"code\":500,\"msg\":\"校验失败\",\"data\":null}";
|
||||
// 处理POST请求路由
|
||||
private Response handlePostRequest(String normUri, IHTTPSession session) throws IOException, ResponseException {
|
||||
// 解析请求参数(后续要和AuthHttpHandler的parseRequestParams统一)
|
||||
Map<String, String> params = mHttpHandler.parseRequestParams(session);
|
||||
if (params == null) {
|
||||
return mHttpHandler.buildErrorResponse(Response.Status.BAD_REQUEST, "参数解析失败");
|
||||
}
|
||||
return newFixedLengthResponse(Response.Status.OK, "application/json", result);
|
||||
}
|
||||
|
||||
// 提交公钥:直接调用ServerUtils
|
||||
private Response handleSubmitPublicKey(IHTTPSession session) throws IOException, NanoHTTPD.ResponseException {
|
||||
session.parseBody(null);
|
||||
Map<String, String> params = session.getParms();
|
||||
String email = params.get("email");
|
||||
String publicKey = params.get("appPublicKey");
|
||||
LogUtils.d(TAG, "接收公钥提交请求,邮箱:" + email + ",公钥:" + publicKey);
|
||||
|
||||
String result = ServerUtils.submitAppPublicKey(email, publicKey);
|
||||
if (result == null) {
|
||||
result = "{\"code\":500,\"msg\":\"公钥提交失败\",\"data\":null}";
|
||||
switch (normUri) {
|
||||
case "/send-verify-code":
|
||||
return mHttpHandler.handleSendVerifyCode(params);
|
||||
case "/verify-code":
|
||||
return mHttpHandler.handleVerifyCode(params);
|
||||
case "/submit-public-key":
|
||||
return mHttpHandler.handleSubmitPublicKey(params);
|
||||
case "/heartbeat-ping":
|
||||
return mHttpHandler.handleHeartbeatPing(params);
|
||||
default:
|
||||
LogUtils.d(TAG, "POST请求未匹配:" + normUri);
|
||||
return mHttpHandler.buildErrorResponse(Response.Status.NOT_FOUND, "404 Not Found");
|
||||
}
|
||||
return newFixedLengthResponse(Response.Status.OK, "application/json", result);
|
||||
}
|
||||
|
||||
// 心跳请求:直接调用ServerUtils
|
||||
private Response handleHeartbeatPing(IHTTPSession session) throws IOException, NanoHTTPD.ResponseException {
|
||||
session.parseBody(null);
|
||||
Map<String, String> params = session.getParms();
|
||||
String encryptData = params.get("encryptData");
|
||||
LogUtils.d(TAG, "接收心跳请求,加密数据:" + encryptData);
|
||||
|
||||
String result = ServerUtils.sendPingRequest(encryptData);
|
||||
if (result == null) {
|
||||
result = "{\"code\":500,\"msg\":\"心跳请求失败\",\"data\":null}";
|
||||
}
|
||||
return newFixedLengthResponse(Response.Status.OK, "application/json", result);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
// 对外暴露工具类(可选,便于外部扩展)
|
||||
public AuthHttpHandler getHttpHandler() {
|
||||
return mHttpHandler;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
231
src/cc/winboll/service/AuthHttpHandler.java
Normal file
231
src/cc/winboll/service/AuthHttpHandler.java
Normal file
@@ -0,0 +1,231 @@
|
||||
package cc.winboll.service;
|
||||
|
||||
import cc.winboll.LogUtils;
|
||||
import cc.winboll.app.AppSignaturesUtils;
|
||||
import cc.winboll.util.ServerUtils;
|
||||
import cc.winboll.util.ConsoleVersionUtils;
|
||||
import fi.iki.elonen.NanoHTTPD;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* HTTP接口业务处理工具类,封装所有接口逻辑,无HTTP依赖
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026-01-21 16:30:00
|
||||
*/
|
||||
public class AuthHttpHandler {
|
||||
private static final String TAG = "AuthHttpHandler";
|
||||
private static final String CONTENT_TYPE_JSON = "application/json";
|
||||
private static final String CONTENT_TYPE_PLAIN = "text/plain";
|
||||
|
||||
/**
|
||||
* 构建JSON错误响应
|
||||
*/
|
||||
public NanoHTTPD.Response buildErrorResponse(NanoHTTPD.Response.Status status, String msg) {
|
||||
String json = String.format("{\"code\":%d,\"msg\":\"%s\",\"data\":null}", status.getRequestStatus(), msg);
|
||||
return NanoHTTPD.newFixedLengthResponse(status, CONTENT_TYPE_JSON, json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用参数解析(兼容GET/POST),替换原parsePostParams
|
||||
*/
|
||||
public Map<String, String> parseRequestParams(NanoHTTPD.IHTTPSession session) throws IOException, NanoHTTPD.ResponseException {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
// 先取URL参数(优先级高)
|
||||
params.putAll(session.getParms());
|
||||
// 再取POST Body参数,重复key覆盖URL参数
|
||||
session.parseBody(params);
|
||||
LogUtils.d(TAG, "解析请求参数:" + params);
|
||||
return params.isEmpty() ? null : params;
|
||||
}
|
||||
|
||||
// ==================== 接口业务逻辑实现 ====================
|
||||
/**
|
||||
* 根路径默认响应
|
||||
*/
|
||||
public NanoHTTPD.Response handleHelloWorld() {
|
||||
LogUtils.d(TAG, "处理根路径请求");
|
||||
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, CONTENT_TYPE_PLAIN, "WinBoLL is Running...");
|
||||
}
|
||||
|
||||
/**
|
||||
* 版本查询接口
|
||||
*/
|
||||
public NanoHTTPD.Response handleVersionQuery() {
|
||||
LogUtils.d(TAG, "处理版本查询请求");
|
||||
String version = ConsoleVersionUtils.getVersion();
|
||||
LogUtils.d(TAG, "版本查询结果:" + version);
|
||||
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, CONTENT_TYPE_PLAIN, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ping请求响应
|
||||
*/
|
||||
public NanoHTTPD.Response handlePingRequest() {
|
||||
LogUtils.d(TAG, "处理ping请求");
|
||||
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, CONTENT_TYPE_JSON,
|
||||
"{\"code\":200,\"msg\":\"pong\",\"data\":null}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送验证码接口
|
||||
*/
|
||||
public NanoHTTPD.Response handleSendVerifyCode(Map<String, String> params) {
|
||||
if (params == null) {
|
||||
LogUtils.w(TAG, "发送验证码失败:未获取到参数");
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "未获取到请求参数");
|
||||
}
|
||||
String email = params.get("email");
|
||||
LogUtils.d(TAG, "处理验证码发送请求,邮箱:" + email);
|
||||
|
||||
if (email == null || email.isEmpty()) {
|
||||
LogUtils.w(TAG, "发送验证码失败:邮箱为空");
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "邮箱不能为空");
|
||||
}
|
||||
|
||||
boolean sendResult = ServerUtils.sendVerifyCodeLocal(email);
|
||||
String json = sendResult ?
|
||||
"{\"code\":200,\"msg\":\"验证码发送成功\",\"data\":null}" :
|
||||
"{\"code\":500,\"msg\":\"验证码发送失败\",\"data\":null}";
|
||||
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, CONTENT_TYPE_JSON, json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验验证码接口
|
||||
*/
|
||||
public NanoHTTPD.Response handleVerifyCode(Map<String, String> params) {
|
||||
if (params == null) {
|
||||
LogUtils.w(TAG, "校验验证码失败:未获取到参数");
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "未获取到请求参数");
|
||||
}
|
||||
String email = params.get("email");
|
||||
String code = params.get("code");
|
||||
LogUtils.d(TAG, "处理验证码校验请求,邮箱:" + email + ",验证码:" + code);
|
||||
|
||||
if (email == null || code == null || email.isEmpty() || code.isEmpty()) {
|
||||
LogUtils.w(TAG, "校验验证码失败:参数不全");
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "邮箱和验证码不能为空");
|
||||
}
|
||||
|
||||
String result = ServerUtils.verifyCode(email, code);
|
||||
if (result == null) {
|
||||
result = "{\"code\":500,\"msg\":\"校验失败\",\"data\":null}";
|
||||
}
|
||||
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, CONTENT_TYPE_JSON, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交公钥接口
|
||||
*/
|
||||
public NanoHTTPD.Response handleSubmitPublicKey(Map<String, String> params) {
|
||||
if (params == null) {
|
||||
LogUtils.w(TAG, "提交公钥失败:未获取到参数");
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "未获取到请求参数");
|
||||
}
|
||||
String email = params.get("email");
|
||||
String publicKey = params.get("appPublicKey");
|
||||
LogUtils.d(TAG, "处理公钥提交请求,邮箱:" + email + ",公钥:" + publicKey);
|
||||
|
||||
if (email == null || publicKey == null || email.isEmpty() || publicKey.isEmpty()) {
|
||||
LogUtils.w(TAG, "提交公钥失败:参数不全");
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "邮箱和公钥不能为空");
|
||||
}
|
||||
|
||||
String result = ServerUtils.submitAppPublicKey(email, publicKey);
|
||||
if (result == null) {
|
||||
result = "{\"code\":500,\"msg\":\"公钥提交失败\",\"data\":null}";
|
||||
}
|
||||
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, CONTENT_TYPE_JSON, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 心跳请求接口
|
||||
*/
|
||||
public NanoHTTPD.Response handleHeartbeatPing(Map<String, String> params) {
|
||||
if (params == null) {
|
||||
LogUtils.w(TAG, "心跳请求失败:未获取到参数");
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "未获取到请求参数");
|
||||
}
|
||||
String encryptData = params.get("encryptData");
|
||||
LogUtils.d(TAG, "处理心跳请求,加密数据:" + encryptData);
|
||||
|
||||
if (encryptData == null || encryptData.isEmpty()) {
|
||||
LogUtils.w(TAG, "心跳请求失败:加密数据为空");
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "加密数据不能为空");
|
||||
}
|
||||
|
||||
String result = ServerUtils.sendPingRequest(encryptData);
|
||||
if (result == null) {
|
||||
result = "{\"code\":500,\"msg\":\"心跳请求失败\",\"data\":null}";
|
||||
}
|
||||
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, CONTENT_TYPE_JSON, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用签名校验接口(处理/api/app-signatures-check请求)
|
||||
* 功能:调用AppSignaturesUtils完成校验,返回JSON格式校验结果
|
||||
*/
|
||||
public NanoHTTPD.Response handleAppSignatureCheck(Map<String, String> params) {
|
||||
LogUtils.d(TAG, "处理应用签名校验请求,参数:" + params);
|
||||
|
||||
// 前置判空
|
||||
if (params == null) {
|
||||
LogUtils.w(TAG, "签名校验失败:未获取到任何参数");
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "未获取到请求参数");
|
||||
}
|
||||
|
||||
// 1. 参数校验(signature和validTime必填)
|
||||
String signature = params.get("signature");
|
||||
String validTimeStr = params.get("validTime");
|
||||
if (signature == null || signature.isEmpty() || validTimeStr == null || validTimeStr.isEmpty()) {
|
||||
LogUtils.w(TAG, "签名校验失败:参数不全(signature或validTime为空)");
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "参数错误:signature和validTime不能为空");
|
||||
}
|
||||
|
||||
// 2. validTime格式校验(必须是数字时间戳)
|
||||
long validTime;
|
||||
try {
|
||||
validTime = Long.parseLong(validTimeStr);
|
||||
} catch (NumberFormatException e) {
|
||||
LogUtils.w(TAG, "签名校验失败:validTime格式错误(非数字),值:" + validTimeStr);
|
||||
return buildErrorResponse(NanoHTTPD.Response.Status.BAD_REQUEST, "参数错误:validTime必须是数字时间戳");
|
||||
}
|
||||
|
||||
// 3. 核心校验逻辑:调用AppSignaturesUtils 替代原有硬编码逻辑
|
||||
boolean isValid = AppSignaturesUtils.checksignatures(signature, validTime);
|
||||
|
||||
// 4. Base64解密(仅用于日志输出,不影响校验逻辑)
|
||||
String decryptedSign = "";
|
||||
try {
|
||||
byte[] signBytes = Base64.getDecoder().decode(signature);
|
||||
decryptedSign = new String(signBytes, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
decryptedSign = "解密失败";
|
||||
}
|
||||
|
||||
// 5. 构建响应结果
|
||||
String msg;
|
||||
if (isValid) {
|
||||
msg = "应用签名校验通过,为合法应用";
|
||||
LogUtils.d(TAG, "签名校验通过:" + msg + ",解密后签名:" + decryptedSign + ",时间戳:" + validTime);
|
||||
} else {
|
||||
msg = "应用签名校验失败(签名不匹配或时间不满足生效要求)";
|
||||
LogUtils.w(TAG, "签名校验失败:" + msg + ",解密后签名:" + decryptedSign + ",时间戳:" + validTime);
|
||||
}
|
||||
|
||||
// 6. 返回JSON响应(与APP端预期格式一致,字段完整)
|
||||
String responseJson = String.format(
|
||||
"{\"code\":%d,\"msg\":\"%s\",\"data\":{\"valid\":%b,\"signature\":\"%s\",\"decryptedSign\":\"%s\",\"validTime\":%d}}",
|
||||
isValid ? 200 : 403,
|
||||
msg,
|
||||
isValid,
|
||||
signature,
|
||||
decryptedSign,
|
||||
validTime
|
||||
);
|
||||
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, CONTENT_TYPE_JSON, responseJson);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user