From 0ab2fdea666c82cfdae33e04a62e195964040b9f Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Mon, 29 Sep 2025 00:18:20 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B2=BE=E7=AE=80=E6=A8=A1=E5=9D=97=EF=BC=8C?= =?UTF-8?q?=E8=BD=AC=E7=A7=BB=E5=88=B0WinBoLL=E9=A1=B9=E7=9B=AE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apputils/build.properties | 4 +- libapputils/build.properties | 4 +- .../libapputils/models/ResponseData.java | 53 ---- .../libapputils/models/UserInfoModel.java | 92 ------ .../studio/libapputils/utils/RSAUtils.java | 222 -------------- .../studio/libapputils/utils/YunUtils.java | 281 ------------------ 6 files changed, 4 insertions(+), 652 deletions(-) delete mode 100644 libapputils/src/main/java/cc/winboll/studio/libapputils/models/ResponseData.java delete mode 100644 libapputils/src/main/java/cc/winboll/studio/libapputils/models/UserInfoModel.java delete mode 100644 libapputils/src/main/java/cc/winboll/studio/libapputils/utils/RSAUtils.java delete mode 100644 libapputils/src/main/java/cc/winboll/studio/libapputils/utils/YunUtils.java diff --git a/apputils/build.properties b/apputils/build.properties index 5667aaf..04952f8 100644 --- a/apputils/build.properties +++ b/apputils/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Fri Sep 26 21:10:25 HKT 2025 +#Sun Sep 28 16:17:23 GMT 2025 stageCount=1 libraryProject=libapputils baseVersion=15.10 publishVersion=15.10.0 -buildCount=0 +buildCount=1 baseBetaVersion=15.10.1 diff --git a/libapputils/build.properties b/libapputils/build.properties index fcc9a7f..04952f8 100644 --- a/libapputils/build.properties +++ b/libapputils/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Fri Sep 26 21:10:11 HKT 2025 +#Sun Sep 28 16:17:23 GMT 2025 stageCount=1 libraryProject=libapputils baseVersion=15.10 publishVersion=15.10.0 -buildCount=0 +buildCount=1 baseBetaVersion=15.10.1 diff --git a/libapputils/src/main/java/cc/winboll/studio/libapputils/models/ResponseData.java b/libapputils/src/main/java/cc/winboll/studio/libapputils/models/ResponseData.java deleted file mode 100644 index f940a9c..0000000 --- a/libapputils/src/main/java/cc/winboll/studio/libapputils/models/ResponseData.java +++ /dev/null @@ -1,53 +0,0 @@ -package cc.winboll.studio.libapputils.models; - -/** - * @Author ZhanGSKen - * @Date 2025/06/05 11:26 - */ - -public class ResponseData { - - public static final String STATUS_SUCCESS = "success"; - public static final String STATUS_ERROR = "error"; - - private String status; - private String message; - private UserInfoModel data; - - public ResponseData() { - this.status = ""; - this.message = ""; - this.data = new UserInfoModel(); - } - - public ResponseData(String status, String message, UserInfoModel data) { - this.status = status; - this.message = message; - this.data = data; - } - - public void setStatus(String status) { - this.status = status; - } - - public String getStatus() { - return status; - } - - public void setMessage(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - - public void setData(UserInfoModel data) { - this.data = data; - } - - public UserInfoModel getData() { - return data; - } -} - diff --git a/libapputils/src/main/java/cc/winboll/studio/libapputils/models/UserInfoModel.java b/libapputils/src/main/java/cc/winboll/studio/libapputils/models/UserInfoModel.java deleted file mode 100644 index 8794b89..0000000 --- a/libapputils/src/main/java/cc/winboll/studio/libapputils/models/UserInfoModel.java +++ /dev/null @@ -1,92 +0,0 @@ -package cc.winboll.studio.libapputils.models; - -/** - * @Author ZhanGSKen - * @Date 2025/06/04 19:14 - */ -import android.util.JsonReader; -import android.util.JsonWriter; -import cc.winboll.studio.libappbase.BaseBean; -import java.io.IOException; - -public class UserInfoModel extends BaseBean { - - public static final String TAG = "UserInfoModel"; - - String username; - String password; - String token; - - public UserInfoModel() { - this.username = ""; - this.password = ""; - this.token = ""; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getUsername() { - return username; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getPassword() { - return password; - } - - public void setToken(String token) { - this.token = token; - } - - public String getToken() { - return token; - } - - @Override - public String getName() { - return UserInfoModel.class.getName(); - } - - @Override - public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException { - super.writeThisToJsonWriter(jsonWriter); - jsonWriter.name("username").value(getUsername()); - jsonWriter.name("password").value(getPassword()); - jsonWriter.name("token").value(getToken()); - } - - @Override - public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException { - if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else { - if (name.equals("username")) { - setUsername(jsonReader.nextString()); - } else if (name.equals("password")) { - setPassword(jsonReader.nextString()); - } else if (name.equals("token")) { - setToken(jsonReader.nextString()); - } else { - return false; - } - } - return true; - } - - @Override - public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException { - jsonReader.beginObject(); - while (jsonReader.hasNext()) { - String name = jsonReader.nextName(); - if (!initObjectsFromJsonReader(jsonReader, name)) { - jsonReader.skipValue(); - } - } - // 结束 JSON 对象 - jsonReader.endObject(); - return this; - } -} diff --git a/libapputils/src/main/java/cc/winboll/studio/libapputils/utils/RSAUtils.java b/libapputils/src/main/java/cc/winboll/studio/libapputils/utils/RSAUtils.java deleted file mode 100644 index 1ef63ec..0000000 --- a/libapputils/src/main/java/cc/winboll/studio/libapputils/utils/RSAUtils.java +++ /dev/null @@ -1,222 +0,0 @@ -package cc.winboll.studio.libapputils.utils; - -/** - * @Author ZhanGSKen - * @Date 2025/06/04 13:36 - * @Describe RSA加密工具 - */ -import android.content.Context; -import android.util.Base64; -import cc.winboll.studio.libappbase.LogUtils; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; -import java.util.Objects; -import javax.crypto.Cipher; - -public class RSAUtils { - private static final String TAG = "RSAUtils"; - private static final int KEY_SIZE = 2048; - private static final String KEY_ALGORITHM = "RSA"; - private static final String PUBLIC_KEY_FILE = "public.key"; - private static final String PRIVATE_KEY_FILE = "private.key"; - private static final String CIPHER_ALGORITHM = KEY_ALGORITHM + "/ECB/PKCS1Padding"; // 保留原加密方式 - - private final String keyPath; - private static volatile RSAUtils INSTANCE; - - /** - * 构造方法:初始化密钥存储路径(内部存储) - */ - private RSAUtils(Context context) { - keyPath = context.getFilesDir() + File.separator + "keys" + File.separator; // 修正路径格式 - } - - /** - * 获取单例实例 - */ - public static synchronized RSAUtils getInstance(Context context) { - if (INSTANCE == null) { - INSTANCE = new RSAUtils(context); - } - return INSTANCE; - } - - /** - * 检查密钥文件是否存在 - */ - public boolean keysExist() { - File publicKeyFile = new File(keyPath + PUBLIC_KEY_FILE); - File privateKeyFile = new File(keyPath + PRIVATE_KEY_FILE); - return publicKeyFile.exists() && privateKeyFile.exists(); - } - - /** - * 生成密钥对并保存到文件 - */ - public void generateAndSaveKeys() throws Exception { - LogUtils.d(TAG, "开始生成 RSA 密钥对(2048位)"); - KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_ALGORITHM); - generator.initialize(KEY_SIZE); - KeyPair keyPair = generator.generateKeyPair(); - - saveKey(PUBLIC_KEY_FILE, keyPair.getPublic().getEncoded()); - saveKey(PRIVATE_KEY_FILE, keyPair.getPrivate().getEncoded()); - LogUtils.d(TAG, "密钥对生成并保存成功"); - } - - /** - * 获取或生成密钥对(线程安全) - */ - public KeyPair getOrGenerateKeys() throws Exception { - if (!keysExist()) { - synchronized (RSAUtils.class) { // 双重检查锁,避免多线程重复生成 - if (!keysExist()) { - generateAndSaveKeys(); - } - } - } - return readKeysFromFile(); - } - - /** - * 从文件读取密钥对 - */ - private KeyPair readKeysFromFile() throws Exception { - LogUtils.d(TAG, "读取密钥对文件"); - try { - byte[] publicKeyBytes = readFileToBytes(keyPath + PUBLIC_KEY_FILE); - byte[] privateKeyBytes = readFileToBytes(keyPath + PRIVATE_KEY_FILE); - - X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(publicKeyBytes); - PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKeyBytes); - - KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM); - PublicKey publicKey = factory.generatePublic(publicSpec); - PrivateKey privateKey = factory.generatePrivate(privateSpec); - - return new KeyPair(publicKey, privateKey); - } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) { - LogUtils.e(TAG, "密钥文件读取失败:" + e.getMessage()); - throw new Exception("密钥文件损坏或格式错误", e); - } - } - - /** - * 保存密钥到文件(通用方法) - */ - private void saveKey(String fileName, byte[] keyBytes) throws IOException { - Objects.requireNonNull(keyBytes, "密钥字节数据不可为空"); - File dir = new File(keyPath); - if (!dir.exists() && !dir.mkdirs()) { - throw new IOException("创建密钥目录失败:" + keyPath); - } - - FileOutputStream fos = null; - try { - fos = new FileOutputStream(keyPath + fileName); - fos.write(keyBytes); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - LogUtils.e(TAG, "关闭文件流失败:" + e.getMessage()); - } - } - } - } - - /** - * 读取文件为字节数组(Java 7 兼容) - */ - private byte[] readFileToBytes(String filePath) throws IOException { - File file = new File(filePath); - if (!file.exists() || file.isDirectory()) { - throw new IOException("文件不存在或为目录:" + filePath); - } - - FileInputStream fis = null; - try { - fis = new FileInputStream(file); - byte[] data = new byte[(int) file.length()]; - int bytesRead = fis.read(data); - if (bytesRead != data.length) { - throw new IOException("文件读取不完整"); - } - return data; - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - LogUtils.e(TAG, "关闭文件流失败:" + e.getMessage()); - } - } - } - } - - /** - * 公钥加密(带参数校验) - */ - public byte[] encryptWithPublicKey(String plainText, PublicKey publicKey) throws Exception { - Objects.requireNonNull(plainText, "明文不可为空"); - Objects.requireNonNull(publicKey, "公钥不可为空"); - - Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); - cipher.init(Cipher.ENCRYPT_MODE, publicKey); - - // 检查数据长度是否超过 RSA 限制(2048位密钥最大明文为 214字节,PKCS1Padding) - int maxPlainTextSize = cipher.getBlockSize() - 11; // PKCS1Padding 固定填充长度 - if (plainText.getBytes("UTF-8").length > maxPlainTextSize) { - throw new IllegalArgumentException("明文过长,最大支持 " + maxPlainTextSize + " 字节"); - } - - return cipher.doFinal(plainText.getBytes("UTF-8")); - } - - /** - * 私钥解密(带参数校验) - */ - public String decryptWithPrivateKey(byte[] encryptedData, PrivateKey privateKey) throws Exception { - Objects.requireNonNull(encryptedData, "密文不可为空"); - Objects.requireNonNull(privateKey, "私钥不可为空"); - - Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); - cipher.init(Cipher.DECRYPT_MODE, privateKey); - byte[] decryptedBytes = cipher.doFinal(encryptedData); - return new String(decryptedBytes, "UTF-8"); - } - /** - * 将 HTTP 传输的 Base64 字符串还原为加密字节数组(Java 7 兼容) - * @param httpString Base64 字符串(非 null) - * @return 加密字节数组 - * @throws IllegalArgumentException 解码失败时抛出 - */ - public byte[] httpStringToEncryptBytes(String httpString) { - Objects.requireNonNull(httpString, "HTTP 字符串不可为空"); - - // 计算缺失的填充符数量(Java 7 不支持 repeat(),手动拼接) - int pad = httpString.length() % 4; - StringBuilder paddedString = new StringBuilder(httpString); - if (pad != 0) { - for (int i = 0; i < pad; i++) { - paddedString.append('='); // 补全 '=' - } - } - - // 使用 Base64 解码(Android 原生 Base64 类兼容 Java 7) - return Base64.decode(paddedString.toString(), Base64.URL_SAFE); - } -} - diff --git a/libapputils/src/main/java/cc/winboll/studio/libapputils/utils/YunUtils.java b/libapputils/src/main/java/cc/winboll/studio/libapputils/utils/YunUtils.java deleted file mode 100644 index 99c3974..0000000 --- a/libapputils/src/main/java/cc/winboll/studio/libapputils/utils/YunUtils.java +++ /dev/null @@ -1,281 +0,0 @@ -package cc.winboll.studio.libapputils.utils; - -/** - * @Author ZhanGSKen - * @Date 2025/06/04 17:21 - * @Describe 应用登录与接口工具 - */ -import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import cc.winboll.studio.libappbase.LogUtils; -import cc.winboll.studio.libapputils.models.ResponseData; -import cc.winboll.studio.libapputils.models.UserInfoModel; -import com.google.gson.Gson; -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.util.concurrent.TimeUnit; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; - -public class YunUtils { - public static final String TAG = "YunUtils"; - // 私有静态实例,类加载时创建 - private static volatile YunUtils INSTANCE; - Context mContext; - UserInfoModel mUserInfoModel; - String token = ""; - String mDataFolderPath = ""; - String mUserInfoModelPath = ""; - - private static final int CONNECT_TIMEOUT = 15; // 连接超时时间(秒) - private static final int READ_TIMEOUT = 20; // 读取超时时间(秒) - private static volatile YunUtils instance; - private OkHttpClient okHttpClient; - private Handler mainHandler; // 主线程 Handler - - // 私有构造方法,防止外部实例化 - private YunUtils(Context context) { - LogUtils.d(TAG, "YunUtils"); - mContext = context; - mDataFolderPath = mContext.getExternalFilesDir(TAG).toString(); - File fTest = new File(mDataFolderPath); - if (!fTest.exists()) { - fTest.mkdirs(); - } - mUserInfoModelPath = mDataFolderPath + File.separator + "UserInfoModel.rsajson"; - - okHttpClient = new OkHttpClient.Builder() - .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS) - .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS) - .build(); - mainHandler = new Handler(Looper.getMainLooper()); // 获取主线程 Looper - } - - // 公共静态方法,返回唯一实例 - public static synchronized YunUtils getInstance(Context context) { - LogUtils.d(TAG, "getInstance"); - if (INSTANCE == null) { - INSTANCE = new YunUtils(context); - } - return INSTANCE; - } - - public void checkLoginStatus() { - String token = getLocalToken(); - LogUtils.d(TAG, String.format("checkLoginStatus token is %s", token)); - } - - String getLocalToken() { - UserInfoModel userInfoModel = loadUserInfoModel(); - return (userInfoModel == null) ?"": userInfoModel.getToken(); - } - - public void login(String host, UserInfoModel userInfoModel) { - LogUtils.d(TAG, "login"); - - // 发送 POST 请求 - String apiUrl = host + "/login/index.php"; - // 序列化对象为JSON - Gson gson = new Gson(); - String jsonData = gson.toJson(userInfoModel); // 自动生成标准JSON - //String jsonData = userInfoModel.toString(); - LogUtils.d(TAG, "要发送的数据 : " + jsonData); - - sendPostRequest(apiUrl, jsonData, new OnResponseListener() { - // 成功回调(主线程) - @Override - public void onSuccess(String responseBody) { - LogUtils.d(TAG, "onSuccess"); - LogUtils.d(TAG, String.format("responseBody %s", responseBody)); - Gson gson = new Gson(); - ResponseData result = gson.fromJson(responseBody, ResponseData.class); // 转为 Result 实例 - if(result.getStatus().equals(ResponseData.STATUS_SUCCESS)) { - - UserInfoModel userInfoModel = result.getData(); - if (userInfoModel != null) { - LogUtils.d(TAG, "收到网站 UserInfoModel"); - String token = userInfoModel.getToken(); - saveLocalToken(token); - checkLoginStatus(); - } - - } else if(result.getStatus().equals(ResponseData.STATUS_ERROR)) { - try { - String decodedMessage = URLDecoder.decode(result.getMessage(), "UTF-8"); - LogUtils.d(TAG, "服务器返回信息: " + decodedMessage); - } catch (UnsupportedEncodingException e) { - LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); - } - } - } - - // 失败回调(主线程) - @Override - public void onFailure(String errorMsg) { - LogUtils.d(TAG, errorMsg); - // 处理错误 - } - }); - } - - public void saveLocalToken(String token) { - UserInfoModel userInfoModel = new UserInfoModel(); - userInfoModel.setToken(token); - saveUserInfoModel(userInfoModel); - } - - UserInfoModel loadUserInfoModel() { - LogUtils.d(TAG, "loadUserInfoModel"); - if (new File(mUserInfoModelPath).exists()) { - try { - // 加载加密后的模型数据 - byte[] encryptedData = FileUtils.readByteArrayFromFile(mUserInfoModelPath); - // 加载 RSA 工具 - RSAUtils utils = RSAUtils.getInstance(mContext); - KeyPair keyPair = utils.getOrGenerateKeys(); - //PublicKey publicKey = keyPair.getPublic(); - PrivateKey privateKey = keyPair.getPrivate(); - // 私钥解密模型数据 - String szInfo = utils.decryptWithPrivateKey(encryptedData, keyPair.getPrivate()); - LogUtils.d(TAG, String.format("szInfo %s", szInfo)); - mUserInfoModel = UserInfoModel.parseStringToBean(szInfo, UserInfoModel.class); - if (mUserInfoModel == null) { - LogUtils.d(TAG, "模型数据解析为空数据。"); - } - LogUtils.d(TAG, "UserInfoModel 解密加载结束。"); - } catch (Exception e) { - LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); - } - } else { - LogUtils.d(TAG, "云服务登录信息不存在。"); - mUserInfoModel = null; - } - return mUserInfoModel; - } - - void saveUserInfoModel(UserInfoModel userInfoModel) { - LogUtils.d(TAG, "saveUserInfoModel"); - try { - String szInfo = userInfoModel.toString(); - LogUtils.d(TAG, "原始数据: " + szInfo); - - RSAUtils utils = RSAUtils.getInstance(mContext); - KeyPair keyPair = utils.getOrGenerateKeys(); - PublicKey publicKey = keyPair.getPublic(); - - // 公钥加密(传入字节数组,避免中间字符串转换) - byte[] encryptedData = utils.encryptWithPublicKey(szInfo, publicKey); - - // 保存加密字节数组到文件(直接操作字节,无需转字符串) - FileUtils.writeByteArrayToFile(encryptedData, mUserInfoModelPath); - LogUtils.d(TAG, "加密数据已保存"); - - // 测试解密(仅调试用) - String szInfo2 = utils.decryptWithPrivateKey(encryptedData, keyPair.getPrivate()); - LogUtils.d(TAG, "解密结果: " + szInfo2); - - mUserInfoModel = UserInfoModel.parseStringToBean(szInfo2, UserInfoModel.class); - if (mUserInfoModel == null) { - LogUtils.d(TAG, "模型解析失败"); - } - } catch (Exception e) { - LogUtils.d(TAG, "加密/解密失败: " + e.getMessage()); - } - } - - // 发送 POST 请求(JSON 数据) - public void sendPostRequest(String url, String data, OnResponseListener listener) { - RequestBody requestBody = RequestBody.create( - MediaType.parse("application/json; charset=utf-8"), // 关键头信息 - data.getBytes(StandardCharsets.UTF_8) - ); - - Request request = new Request.Builder() - .url(url) - .post(requestBody) - .addHeader("Content-Type", "application/json") // 显式添加头 - .build(); - - executeRequest(request, listener); - } - - // 发送 GET 请求 - public void sendGetRequest(String url, OnResponseListener listener) { - Request request = new Request.Builder() - .url(url) - .get() - .build(); - executeRequest(request, listener); - } - - // 执行请求(子线程处理) - private void executeRequest(final Request request, final OnResponseListener listener) { - okHttpClient.newCall(request).enqueue(new Callback() { - // 响应成功(子线程) - @Override - public void onResponse(Call call, Response response) throws IOException { - try { - if (!response.isSuccessful()) { - postFailure(listener, "响应码错误:" + response.code()); - return; - } - String responseBody = response.body().string(); - postSuccess(listener, responseBody); - } catch (Exception e) { - postFailure(listener, "解析失败:" + e.getMessage()); - } - } - - // 响应失败(子线程) - @Override - public void onFailure(Call call, IOException e) { - postFailure(listener, "网络失败:" + e.getMessage()); - } - - // 主线程回调(使用 Handler) - private void postSuccess(final OnResponseListener listener, final String msg) { - mainHandler.post(new Runnable() { - @Override - public void run() { - listener.onSuccess(msg); - } - }); - } - - private void postFailure(final OnResponseListener listener, final String msg) { - mainHandler.post(new Runnable() { - @Override - public void run() { - listener.onFailure(msg); - } - }); - } - }); - } - - public interface OnResponseListener { - /** - * 成功响应(主线程回调) - * @param responseBody 响应体字符串 - */ - void onSuccess(String responseBody); - - /** - * 失败回调(包含错误信息) - * @param errorMsg 错误描述 - */ - void onFailure(String errorMsg); - } -}