重构WinBoLl类库命名空间

This commit is contained in:
2026-01-15 11:11:21 +08:00
parent 2d0a4b4ebd
commit 25ec76b036
15 changed files with 682 additions and 93 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Thu Jan 15 02:32:25 GMT 2026
#Thu Jan 15 03:05:40 GMT 2026
stageCount=0
libraryProject=library
baseVersion=15.0
publishVersion=15.0.0
buildCount=47
buildCount=50
baseBetaVersion=15.0.1

View File

@@ -47,6 +47,8 @@
<activity android:name="cc.winboll.studio.authcenterapp.unittest.PingTestActivity"/>
<activity android:name="cc.winboll.studio.authcenterapp.activities.AuthTestActivity"/>
</application>
</manifest>

View File

@@ -9,6 +9,7 @@ import android.view.MenuItem;
import android.view.View;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.authcenterapp.R;
import cc.winboll.studio.authcenterapp.activities.AuthTestActivity;
import cc.winboll.studio.authcenterapp.activities.BaseWinBoLLActivity;
import cc.winboll.studio.authcenterapp.activities.ConsoleActivity;
import cc.winboll.studio.authcenterapp.unittest.PingTestActivity;
@@ -103,6 +104,9 @@ public class MainActivity extends BaseWinBoLLActivity {
break;
case R.id.item_ping_test:
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), PingTestActivity.class);
break;
case R.id.item_auth_test:
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), AuthTestActivity.class);
break;
}
return super.onOptionsItemSelected(item);

View File

@@ -0,0 +1,558 @@
package cc.winboll.studio.authcenterapp.activities;
import android.content.Context;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import cc.winboll.LogUtils;
import cc.winboll.library.model.AuthDataModel;
import cc.winboll.library.utils.AuthUtils;
import cc.winboll.library.utils.NFCUtils;
import cc.winboll.library.utils.ServerUtils;
import cc.winboll.studio.authcenterapp.R;
import cc.winboll.studio.authcenterapp.activities.AuthTestActivity;
import cc.winboll.studio.authcenterapp.manager.HeartbeatManager;
import cc.winboll.studio.authcenterapp.utils.StorageUtils;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
/**
* 鉴权全流程测试Activity分步执行+单步验证,适配全流程校验点
* 适配Android API30基于Java7语法开发支持单步测试与全流程自动化测试
* 集成NFC绑定、密钥加解密、服务器交互、心跳保活全链路测试能力
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/15 15:30:00
* @LastEditTime 2026/01/15 17:12:00
*/
public class AuthTestActivity extends BaseWinBoLLActivity implements View.OnClickListener {
// 常量定义区
private static final String TAG = "AuthTestActivity";
private static final String SERVER_PUBLIC_KEY = "此处替换为实际服务器公钥";
// 控件属性区
private EditText etEmail;
private EditText etServerUrl;
private EditText etVerifyCode;
private TextView tvTestStatus;
private Button btnStep1;
private Button btnStep2;
private Button btnStep3;
private Button btnStep4;
private Button btnStep5;
private Button btnStep6;
private Button btnStepAll;
// 核心工具与缓存属性区
private Handler mainHandler;
private String encryptPrivateKey; // 加密后的应用私钥
private String appPublicKeyStr; // 应用公钥字符串
// ===================== 生命周期方法 =====================
@Override
public String getTag() {
return TAG;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auth_test);
mainHandler = new Handler(Looper.getMainLooper());
LogUtils.d(TAG, "onCreate鉴权测试页面初始化");
initView();
initBaseConfig();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
LogUtils.d(TAG, "onNewIntent接收新Intent检测是否为NFC触发");
// 接收NFC标签广播处理真实NFC写入逻辑
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
LogUtils.d(TAG, "onNewIntent检测到NFC标签开始写入加密私钥");
if (tag != null && encryptPrivateKey != null) {
boolean writeSuccess = NFCUtils.writeDataToNfc(tag, encryptPrivateKey);
if (writeSuccess) {
updateStatus("NFC写入成功加密私钥已写入NFC卡");
LogUtils.i(TAG, "onNewIntentNFC卡写入加密私钥成功");
} else {
LogUtils.w(TAG, "onNewIntentNFC卡写入加密私钥失败");
}
} else {
LogUtils.w(TAG, "onNewIntentNFC标签或加密私钥为空无法写入");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
LogUtils.d(TAG, "onDestroy页面销毁停止心跳并清空临时私钥");
// 安全兜底:停止心跳+清空临时私钥,防止内存泄露
HeartbeatManager.stopHeartbeat();
AuthUtils.clearTempPrivateKey();
}
// ===================== 点击事件与初始化方法 =====================
@Override
public void onClick(View v) {
int id = v.getId();
LogUtils.d(TAG, "onClick触发点击事件控件ID=" + v.getId());
if (id == R.id.btn_step1_check_input) {
testStep1CheckInput();
} else if (id == R.id.btn_step2_send_code) {
testStep2SendVerifyCode();
} else if (id == R.id.btn_step3_verify_code) {
testStep3VerifyCode();
} else if (id == R.id.btn_step4_nfc_bind) {
testStep4NfcBind();
} else if (id == R.id.btn_step5_submit_pubkey) {
testStep5SubmitPublicKey();
} else if (id == R.id.btn_step6_heartbeat) {
testStep6Heartbeat();
} else if (id == R.id.btn_step_all_test) {
testAllStep();
}
}
/**
* 初始化控件与点击事件绑定
*/
private void initView() {
// 绑定控件
etEmail = (EditText) findViewById(R.id.et_test_email);
etServerUrl = (EditText) findViewById(R.id.et_test_server_url);
etVerifyCode = (EditText) findViewById(R.id.et_test_verify_code);
tvTestStatus = (TextView) findViewById(R.id.tv_test_status);
btnStep1 = (Button) findViewById(R.id.btn_step1_check_input);
btnStep2 = (Button) findViewById(R.id.btn_step2_send_code);
btnStep3 = (Button) findViewById(R.id.btn_step3_verify_code);
btnStep4 = (Button) findViewById(R.id.btn_step4_nfc_bind);
btnStep5 = (Button) findViewById(R.id.btn_step5_submit_pubkey);
btnStep6 = (Button) findViewById(R.id.btn_step6_heartbeat);
btnStepAll = (Button) findViewById(R.id.btn_step_all_test);
// 绑定点击事件
btnStep1.setOnClickListener(this);
btnStep2.setOnClickListener(this);
btnStep3.setOnClickListener(this);
btnStep4.setOnClickListener(this);
btnStep5.setOnClickListener(this);
btnStep6.setOnClickListener(this);
btnStepAll.setOnClickListener(this);
LogUtils.d(TAG, "initView控件初始化与点击事件绑定完成");
}
/**
* 初始化基础配置(存储、服务器工具)
*/
private void initBaseConfig() {
LogUtils.d(TAG, "initBaseConfig开始初始化基础工具类");
StorageUtils.init(this);
updateStatus("基础工具初始化完成,等待测试");
LogUtils.i(TAG, "initBaseConfig基础工具初始化完成");
}
// ===================== 分步测试核心方法 =====================
/**
* 步骤1测试邮箱+服务地址输入校验
*/
private void testStep1CheckInput() {
LogUtils.d(TAG, "testStep1CheckInput开始执行步骤1-邮箱与服务地址校验");
new Thread(new Runnable() {
@Override
public void run() {
String email = etEmail.getText().toString().trim();
String serverUrl = etServerUrl.getText().toString().trim();
LogUtils.d(TAG, "testStep1CheckInput入参-email=" + email + "serverUrl=" + serverUrl);
// 基础格式校验
if (email.isEmpty() || !email.contains("@")) {
updateStatus("步骤1失败邮箱格式无效");
LogUtils.w(TAG, "testStep1CheckInput邮箱为空或格式无效");
return;
}
if (serverUrl.isEmpty() || (!serverUrl.startsWith("http://") && !serverUrl.startsWith("https://"))) {
updateStatus("步骤1失败服务地址需带http/https");
LogUtils.w(TAG, "testStep1CheckInput服务地址为空或缺少http/https协议头");
return;
}
// 初始化服务器地址
ServerUtils.initServerUrl(serverUrl);
updateStatus("步骤1成功邮箱+服务地址校验通过");
LogUtils.i(TAG, "testStep1CheckInput步骤1执行成功服务器地址已初始化");
}
}).start();
}
/**
* 步骤2测试发送验证码
*/
private void testStep2SendVerifyCode() {
LogUtils.d(TAG, "testStep2SendVerifyCode开始执行步骤2-发送验证码");
new Thread(new Runnable() {
@Override
public void run() {
String email = etEmail.getText().toString().trim();
LogUtils.d(TAG, "testStep2SendVerifyCode入参-email=" + email);
// 前置校验
if (email.isEmpty() || !email.contains("@")) {
updateStatus("步骤2失败请先完成步骤1校验邮箱");
LogUtils.w(TAG, "testStep2SendVerifyCode前置校验失败邮箱未通过步骤1验证");
return;
}
// 调用服务器接口发送验证码
String result = ServerUtils.sendVerifyCode(email);
if (result != null && result.contains("success")) {
updateStatus("步骤2成功验证码已发送请注意查收");
LogUtils.i(TAG, "testStep2SendVerifyCode步骤2执行成功验证码发送成功");
} else {
updateStatus("步骤2失败验证码发送失败服务异常");
LogUtils.w(TAG, "testStep2SendVerifyCode步骤2执行失败服务返回结果=" + result);
}
}
}).start();
}
/**
* 步骤3测试验证码校验
*/
private void testStep3VerifyCode() {
LogUtils.d(TAG, "testStep3VerifyCode开始执行步骤3-校验验证码");
new Thread(new Runnable() {
@Override
public void run() {
String email = etEmail.getText().toString().trim();
String code = etVerifyCode.getText().toString().trim();
LogUtils.d(TAG, "testStep3VerifyCode入参-email=" + email + "code=" + code);
// 验证码格式校验
if (code.isEmpty() || code.length() != 6) {
updateStatus("步骤3失败请输入6位验证码");
LogUtils.w(TAG, "testStep3VerifyCode验证码为空或长度非6位");
return;
}
// 调用服务器接口校验验证码
String result = ServerUtils.verifyCode(email, code);
if (result != null && result.contains("success")) {
updateStatus("步骤3成功验证码校验通过");
LogUtils.i(TAG, "testStep3VerifyCode步骤3执行成功验证码校验通过");
} else {
updateStatus("步骤3失败验证码错误或已过期");
LogUtils.w(TAG, "testStep3VerifyCode步骤3执行失败服务返回结果=" + result);
}
}
}).start();
}
/**
* 步骤4测试NFC绑定生成密钥+写入NFC+本地存储)
*/
private void testStep4NfcBind() {
LogUtils.d(TAG, "testStep4NfcBind开始执行步骤4-NFC绑定与密钥处理");
new Thread(new Runnable() {
@Override
public void run() {
// 1. NFC功能可用性检测
if (!NFCUtils.isNfcEnabled(AuthTestActivity.this)) {
updateStatus("步骤4失败请先开启手机NFC功能");
LogUtils.w(TAG, "testStep4NfcBind手机NFC功能未开启");
return;
}
// 2. 生成RSA密钥对
KeyPair keyPair = AuthUtils.generateRsaKeyPair();
if (keyPair == null) {
updateStatus("步骤4失败RSA密钥对生成失败");
LogUtils.e(TAG, "testStep4NfcBindRSA密钥对生成失败");
return;
}
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
appPublicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
LogUtils.d(TAG, "testStep4NfcBindRSA密钥对生成成功应用公钥已Base64编码");
// 3. 服务器公钥加密应用私钥
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded());
encryptPrivateKey = AuthUtils.encryptByPublicKey(privateKeyBase64, SERVER_PUBLIC_KEY);
if (encryptPrivateKey == null) {
updateStatus("步骤4失败应用私钥加密失败");
LogUtils.e(TAG, "testStep4NfcBind应用私钥通过服务器公钥加密失败");
return;
}
LogUtils.d(TAG, "testStep4NfcBind应用私钥加密成功准备写入NFC与本地存储");
// 4. 提示用户贴近NFC卡简化逻辑预留广播接收入口
updateStatus("步骤4中请将空白NFC卡贴近手机感应区...");
boolean writeSuccess = false; // 真实场景由NFC广播回调更新
// 5. 本地存储加密私钥副本
boolean saveSuccess = StorageUtils.saveEncryptPrivateKey(encryptPrivateKey);
if (saveSuccess && writeSuccess) {
updateStatus("步骤4成功NFC写入+本地存储加密私钥完成");
LogUtils.i(TAG, "testStep4NfcBind步骤4执行成功NFC写入与本地存储均完成");
} else if (saveSuccess) {
updateStatus("步骤4成功(简化)本地存储完成NFC需配合广播优化");
LogUtils.i(TAG, "testStep4NfcBind步骤4执行成功(简化)本地存储完成NFC需依赖广播触发");
} else {
updateStatus("步骤4失败本地存储加密私钥失败");
LogUtils.e(TAG, "testStep4NfcBind步骤4执行失败加密私钥本地存储失败");
}
}
}).start();
}
/**
* 步骤5测试提交应用公钥+解析PONG数据完成鉴权
*/
private void testStep5SubmitPublicKey() {
LogUtils.d(TAG, "testStep5SubmitPublicKey开始执行步骤5-提交公钥与鉴权登录");
new Thread(new Runnable() {
@Override
public void run() {
// 前置校验:应用公钥是否已生成
if (appPublicKeyStr == null || appPublicKeyStr.isEmpty()) {
updateStatus("步骤5失败请先完成步骤4生成应用公钥");
LogUtils.w(TAG, "testStep5SubmitPublicKey前置校验失败应用公钥未生成未执行步骤4");
return;
}
String email = etEmail.getText().toString().trim();
LogUtils.d(TAG, "testStep5SubmitPublicKey入参-email=" + email + ",应用公钥已准备");
// 1. 提交应用公钥到服务器获取加密PONG数据
String encryptPong = ServerUtils.submitAppPublicKey(email, appPublicKeyStr);
if (encryptPong == null) {
updateStatus("步骤5失败提交公钥失败服务无响应");
LogUtils.e(TAG, "testStep5SubmitPublicKey提交应用公钥到服务器失败服务无响应");
return;
}
LogUtils.d(TAG, "testStep5SubmitPublicKey应用公钥提交成功获取到加密PONG数据");
// 2. 解密应用私钥(服务器公钥解密)
String encryptPriKey = StorageUtils.getEncryptPrivateKey();
String privateKeyStr = AuthUtils.decryptByPrivateKey(encryptPriKey, SERVER_PUBLIC_KEY);
if (privateKeyStr == null) {
updateStatus("步骤5失败解密应用私钥失败");
LogUtils.e(TAG, "testStep5SubmitPublicKey通过服务器公钥解密应用私钥失败");
return;
}
LogUtils.d(TAG, "testStep5SubmitPublicKey应用私钥解密成功准备解密PONG数据");
// 3. 应用私钥解密PONG数据并解析
String pongJson = AuthUtils.decryptByPrivateKey(encryptPong, privateKeyStr);
AuthDataModel pongModel = AuthDataModel.parsePongJson(pongJson);
if (pongModel != null && pongModel.isLoginFlag()) {
// 保存登录态核心参数
AuthUtils.setAppId(pongModel.getAppId());
AuthUtils.setShortToken(pongModel.getShortToken());
AuthUtils.setLoginStatus(true);
LogUtils.d(TAG, "testStep5SubmitPublicKeyPONG数据解析成功登录态参数已保存-appId=" + pongModel.getAppId());
// 临时存储明文私钥,用于后续心跳解密
byte[] priBytes = Base64.getDecoder().decode(privateKeyStr);
try {
PrivateKey privateKey = java.security.KeyFactory.getInstance("RSA")
.generatePrivate(new PKCS8EncodedKeySpec(priBytes));
AuthUtils.setTempPrivateKey(privateKey);
LogUtils.d(TAG, "testStep5SubmitPublicKey明文私钥已临时存储用于后续心跳流程");
} catch (Exception e) {
LogUtils.e(TAG, "testStep5SubmitPublicKey临时私钥存储失败", e);
}
updateStatus("步骤5成功公钥提交+登录鉴权完成获取AppId" + pongModel.getAppId());
LogUtils.i(TAG, "testStep5SubmitPublicKey步骤5执行成功公钥提交与鉴权登录均完成");
} else {
updateStatus("步骤5失败PONG数据解析失败鉴权未通过");
LogUtils.w(TAG, "testStep5SubmitPublicKey步骤5执行失败PONG数据解析失败或鉴权未通过");
}
}
}).start();
}
/**
* 步骤6测试心跳保活启动与运行
*/
private void testStep6Heartbeat() {
LogUtils.d(TAG, "testStep6Heartbeat开始执行步骤6-心跳保活测试");
new Thread(new Runnable() {
@Override
public void run() {
// 前置校验:是否已完成鉴权登录
if (!AuthUtils.isLogin()) {
updateStatus("步骤6失败请先完成步骤5登录鉴权");
LogUtils.w(TAG, "testStep6Heartbeat前置校验失败未完成登录鉴权未执行步骤5");
return;
}
// 初始化并启动心跳管理器
HeartbeatManager.init(SERVER_PUBLIC_KEY);
HeartbeatManager.startHeartbeat();
updateStatus("步骤6成功心跳保活已启动每分钟发送1次PING");
LogUtils.i(TAG, "testStep6Heartbeat步骤6执行成功心跳保活任务已启动间隔1分钟");
}
}).start();
}
// ===================== 全流程测试方法 =====================
/**
* 全流程自动化测试(按顺序执行所有步骤)
*/
private void testAllStep() {
LogUtils.d(TAG, "testAllStep全流程自动化测试启动按步骤顺序执行");
updateStatus("全流程测试开始,按步骤执行...");
new Thread(new Runnable() {
@Override
public void run() {
// 步骤1邮箱与服务地址校验
String email = etEmail.getText().toString().trim();
String serverUrl = etServerUrl.getText().toString().trim();
LogUtils.d(TAG, "testAllStep-步骤1入参-email=" + email + "serverUrl=" + serverUrl);
if (email.isEmpty() || !email.contains("@") || serverUrl.isEmpty() || (!serverUrl.startsWith("http://") && !serverUrl.startsWith("https://"))) {
updateStatus("全流程失败步骤1邮箱/服务地址无效");
LogUtils.w(TAG, "testAllStep-步骤1执行失败邮箱或服务地址格式无效");
return;
}
ServerUtils.initServerUrl(serverUrl);
updateStatus("全流程-步骤1校验通过");
LogUtils.d(TAG, "testAllStep-步骤1执行成功");
sleep(1000);
// 步骤2发送验证码
String sendResult = ServerUtils.sendVerifyCode(email);
if (sendResult == null || !sendResult.contains("success")) {
updateStatus("全流程失败步骤2验证码发送失败");
LogUtils.w(TAG, "testAllStep-步骤2执行失败验证码发送失败服务返回=" + sendResult);
return;
}
updateStatus("全流程-步骤2验证码发送成功请手动输入后继续");
LogUtils.d(TAG, "testAllStep-步骤2执行成功等待用户输入验证码");
sleep(3000);
// 步骤3验证码校验需用户提前输入
String code = etVerifyCode.getText().toString().trim();
String verifyResult = ServerUtils.verifyCode(email, code);
LogUtils.d(TAG, "testAllStep-步骤3入参-code=" + code);
if (verifyResult == null || !verifyResult.contains("success")) {
updateStatus("全流程失败步骤3验证码校验失败");
LogUtils.w(TAG, "testAllStep-步骤3执行失败验证码校验失败服务返回=" + verifyResult);
return;
}
updateStatus("全流程-步骤3验证码校验通过");
LogUtils.d(TAG, "testAllStep-步骤3执行成功");
sleep(1000);
// 步骤4NFC绑定与密钥存储
if (!NFCUtils.isNfcEnabled(AuthTestActivity.this)) {
updateStatus("全流程失败步骤4请开启NFC功能");
LogUtils.w(TAG, "testAllStep-步骤4执行失败NFC功能未开启");
return;
}
KeyPair keyPair = AuthUtils.generateRsaKeyPair();
if (keyPair == null) {
updateStatus("全流程失败步骤4密钥生成失败");
LogUtils.w(TAG, "testAllStep-步骤4执行失败RSA密钥对生成失败");
return;
}
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
appPublicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded());
encryptPrivateKey = AuthUtils.encryptByPublicKey(privateKeyBase64, SERVER_PUBLIC_KEY);
boolean saveSuccess = StorageUtils.saveEncryptPrivateKey(encryptPrivateKey);
if (!saveSuccess) {
updateStatus("全流程失败步骤4本地存储失败");
LogUtils.w(TAG, "testAllStep-步骤4执行失败加密私钥本地存储失败");
return;
}
updateStatus("全流程-步骤4NFC绑定(简化)+本地存储完成");
LogUtils.d(TAG, "testAllStep-步骤4执行成功简化");
sleep(1000);
// 步骤5提交公钥与鉴权登录
String encryptPong = ServerUtils.submitAppPublicKey(email, appPublicKeyStr);
String privateKeyStr = AuthUtils.decryptByPrivateKey(encryptPrivateKey, SERVER_PUBLIC_KEY);
String pongJson = AuthUtils.decryptByPrivateKey(encryptPong, privateKeyStr);
AuthDataModel pongModel = AuthDataModel.parsePongJson(pongJson);
if (pongModel == null || !pongModel.isLoginFlag()) {
updateStatus("全流程失败步骤5鉴权失败");
LogUtils.w(TAG, "testAllStep-步骤5执行失败鉴权未通过");
return;
}
AuthUtils.setAppId(pongModel.getAppId());
AuthUtils.setShortToken(pongModel.getShortToken());
AuthUtils.setLoginStatus(true);
// 临时存储私钥
byte[] priBytes = Base64.getDecoder().decode(privateKeyStr);
try {
PrivateKey privateKeyTemp = java.security.KeyFactory.getInstance("RSA")
.generatePrivate(new PKCS8EncodedKeySpec(priBytes));
AuthUtils.setTempPrivateKey(privateKeyTemp);
} catch (Exception e) {
LogUtils.e(TAG, "testAllStep-步骤5临时私钥存储失败", e);
}
updateStatus("全流程-步骤5鉴权成功获取AppId" + pongModel.getAppId());
LogUtils.d(TAG, "testAllStep-步骤5执行成功鉴权登录完成");
sleep(1000);
// 步骤6启动心跳保活
HeartbeatManager.init(SERVER_PUBLIC_KEY);
HeartbeatManager.startHeartbeat();
updateStatus("全流程测试完成:所有步骤执行成功,心跳已启动");
LogUtils.i(TAG, "testAllStep全流程自动化测试执行成功所有步骤完成心跳已启动");
}
}).start();
}
// ===================== 辅助工具方法 =====================
/**
* 主线程更新测试状态UI操作需主线程
*/
private void updateStatus(final String status) {
mainHandler.post(new Runnable() {
@Override
public void run() {
tvTestStatus.append("[" + System.currentTimeMillis() + "] " + status + "\n");
LogUtils.d(TAG, "测试状态更新:" + status);
Toast.makeText(AuthTestActivity.this, status, Toast.LENGTH_SHORT).show();
}
});
}
/**
* 线程休眠(简化流程等待,避免步骤执行过快)
*/
private void sleep(long millis) {
try {
Thread.sleep(millis);
LogUtils.d(TAG, "线程休眠完成,休眠时长=" + millis + "ms");
} catch (InterruptedException e) {
LogUtils.w(TAG, "线程休眠被中断", e);
}
}
/**
* 外部启动当前测试Activity的静态入口方法
*/
public static void start(Context context) {
LogUtils.d(TAG, "start外部调用启动鉴权测试页面");
Intent intent = new Intent(context, AuthTestActivity.class);
context.startActivity(intent);
}
}

View File

@@ -1,9 +1,8 @@
package cc.winboll.manager;
package cc.winboll.studio.authcenterapp.manager;
import cc.winboll.LogUtils;
import cc.winboll.model.AuthDataModel;
import cc.winboll.utils.AuthUtils;
import cc.winboll.utils.ServerUtils;
import cc.winboll.library.model.AuthDataModel;
import cc.winboll.library.utils.AuthUtils;
import cc.winboll.library.utils.ServerUtils;
import java.util.Timer;
import java.util.TimerTask;

View File

@@ -1,4 +1,4 @@
package cc.winboll;
package cc.winboll.studio.authcenterapp.utils;
import android.content.Context;
import android.content.SharedPreferences;

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center_horizontal">
<!-- 输入区域 -->
<EditText
android:id="@+id/et_test_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入登录邮箱"
android:inputType="textEmailAddress"
android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/et_test_server_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入服务地址(带http/https)"
android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/et_test_verify_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入6位验证码"
android:inputType="number"
android:maxLength="6"
android:layout_marginBottom="16dp"/>
<!-- 分步测试按钮 -->
<Button
android:id="@+id/btn_step1_check_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="步骤1校验邮箱+服务地址"/>
<Button
android:id="@+id/btn_step2_send_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="步骤2发送验证码"/>
<Button
android:id="@+id/btn_step3_verify_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="步骤3校验验证码"/>
<Button
android:id="@+id/btn_step4_nfc_bind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="步骤4NFC绑定+密钥存储"/>
<Button
android:id="@+id/btn_step5_submit_pubkey"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="步骤5提交公钥+鉴权登录"/>
<Button
android:id="@+id/btn_step6_heartbeat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="步骤6启动心跳保活"/>
<Button
android:id="@+id/btn_step_all_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="全流程测试(自动执行所有步骤)"
android:background="@android:color/holo_blue_dark"
android:textColor="@android:color/white"
android:layout_marginTop="8dp"/>
<!-- 状态显示区域 -->
<TextView
android:id="@+id/tv_test_status"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="16dp"
android:textSize="14sp"
android:scrollbars="vertical"
android:maxLines="20"/>
</LinearLayout>

View File

@@ -6,6 +6,9 @@
<item
android:id="@+id/item_ping_test"
android:title="Ping Test"/>
<item
android:id="@+id/item_auth_test"
android:title="Auth Test"/>
</menu>
</item>
</menu>

View File

@@ -21,5 +21,13 @@ android {
}
dependencies {
// WinBoLL库 nexus.winboll.cc 地址
api 'cc.winboll.studio:libaes:15.15.7'
api 'cc.winboll.studio:libappbase:15.15.4'
// WinBoLL备用库 jitpack.io 地址
//api 'com.github.ZhanGSKen:AES:aes-v15.15.7'
//api 'com.github.ZhanGSKen:APPBase:appbase-v15.15.4'
api fileTree(dir: 'libs', include: ['*.jar'])
}

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Thu Jan 15 02:32:25 GMT 2026
#Thu Jan 15 03:05:40 GMT 2026
stageCount=0
libraryProject=library
baseVersion=15.0
publishVersion=15.0.0
buildCount=47
buildCount=50
baseBetaVersion=15.0.1

View File

@@ -1,11 +1,6 @@
package cc.winboll;
import java.util.Date;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* 日志工具类对接Java原生java.util.logging简化分级调用+统一格式化输出
@@ -14,78 +9,6 @@ import java.util.logging.Logger;
* @Date 2026/01/14 00:00:00
* @LastEditTime 2026/01/15 02:42:35
*/
public class LogUtils {
// 全局日志实例,绑定当前工具类名
private static final Logger LOGGER = Logger.getLogger(LogUtils.class.getName());
// 静态代码块初始化日志配置(仅执行一次,全局生效)
static {
// 关闭父处理器,避免日志重复输出
LOGGER.setUseParentHandlers(false);
// 自定义控制台输出处理器,绑定格式化规则
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(new CustomLogFormatter());
LOGGER.addHandler(consoleHandler);
// 默认日志级别(全量输出,可按需调整屏蔽低级别日志)
LOGGER.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
}
// 私有化构造方法,禁止外部实例化
private LogUtils() {}
// 调试日志(细粒度开发调试信息,上线可屏蔽)
public static void d(String tag, String msg) {
LOGGER.fine(String.format("[%s] %s", tag, msg));
}
// 调试日志-重载版(带异常堆栈,便于调试阶段排查问题)
public static void d(String tag, String msg, Throwable throwable) {
LOGGER.log(Level.FINE, String.format("[%s] %s", tag, msg), throwable);
}
// 信息日志(常规运行状态、流程节点信息)
public static void i(String tag, String msg) {
LOGGER.info(String.format("[%s] %s", tag, msg));
}
// 信息日志-重载版(带异常堆栈,关联信息类场景的异常链路)
public static void i(String tag, String msg, Throwable throwable) {
LOGGER.log(Level.INFO, String.format("[%s] %s", tag, msg), throwable);
}
// 警告日志-基础版(非致命异常、潜在风险提示)
public static void w(String tag, String msg) {
LOGGER.warning(String.format("[%s] %s", tag, msg));
}
// 警告日志-重载版(带异常堆栈,便于排查警告根源)
public static void w(String tag, String msg, Throwable throwable) {
LOGGER.log(Level.WARNING, String.format("[%s] %s", tag, msg), throwable);
}
// 错误日志-基础版(致命异常、核心流程错误提示)
public static void e(String tag, String msg) {
LOGGER.severe(String.format("[%s] %s", tag, msg));
}
// 错误日志-重载版(带异常堆栈,定位错误完整链路)
public static void e(String tag, String msg, Throwable throwable) {
LOGGER.log(Level.SEVERE, String.format("[%s] %s", tag, msg), throwable);
}
// 自定义日志格式化器,统一输出格式,提升可读性
static class CustomLogFormatter extends Formatter {
@Override
public String format(LogRecord record) {
// 输出格式:[时间戳] [日志级别] [线程名] [日志源] - 日志内容
return String.format("[%1$tF %1$tT] [%2$s] [%3$s] %4$s - %5$s%n",
new Date(record.getMillis()),
record.getLevel().getName(),
Thread.currentThread().getName(),
record.getLoggerName(),
formatMessage(record));
}
}
public class LogUtils extends cc.winboll.studio.libappbase.LogUtils {
}

View File

@@ -1,4 +1,4 @@
package cc.winboll.model;
package cc.winboll.library.model;
import org.json.JSONObject;
import cc.winboll.LogUtils;

View File

@@ -1,4 +1,4 @@
package cc.winboll.utils;
package cc.winboll.library.utils;
import cc.winboll.LogUtils;
import java.security.KeyPair;

View File

@@ -1,4 +1,4 @@
package cc.winboll.utils;
package cc.winboll.library.utils;
import android.content.Context;
import android.nfc.FormatException;

View File

@@ -1,4 +1,4 @@
package cc.winboll.utils;
package cc.winboll.library.utils;
import cc.winboll.LogUtils;
import java.io.BufferedReader;