mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-02-24 06:05:23 +00:00
fix: stabilize auth/decrypt flow and refresh donate account counts
This commit is contained in:
@@ -78,12 +78,17 @@ public class AuthParamCodec {
|
||||
}
|
||||
|
||||
try {
|
||||
// Step 1: URL解码
|
||||
String urlDecoded = URLDecoder.decode(encryptedAuth, StandardCharsets.UTF_8);
|
||||
log.debug("URL解码结果: {}", urlDecoded);
|
||||
// Step 1: URL解码(兼容:有些框架已自动解码,此处避免再次把 '+' 变成空格)
|
||||
String normalized = encryptedAuth;
|
||||
if (normalized.contains("%")) {
|
||||
normalized = URLDecoder.decode(normalized, StandardCharsets.UTF_8);
|
||||
}
|
||||
// 兼容 query 参数中 '+' 被还原为空格的情况
|
||||
normalized = normalized.replace(' ', '+');
|
||||
log.debug("认证参数规范化结果: {}", normalized);
|
||||
|
||||
// Step 2: Base64解码
|
||||
byte[] base64Decoded = Base64.getDecoder().decode(urlDecoded);
|
||||
byte[] base64Decoded = Base64.getDecoder().decode(normalized);
|
||||
log.debug("Base64解码成功,长度: {}", base64Decoded.length);
|
||||
|
||||
// Step 3: AES解密
|
||||
|
||||
@@ -28,6 +28,7 @@ import io.vertx.core.Promise;
|
||||
import io.vertx.core.http.HttpServerRequest;
|
||||
import io.vertx.core.http.HttpServerResponse;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -484,12 +485,13 @@ public class ParserApi {
|
||||
* 捐赠网盘账号
|
||||
*/
|
||||
@RouteMapping(value = "/donateAccount", method = RouteMethod.POST)
|
||||
public Future<JsonObject> donateAccount(HttpServerRequest request, JsonObject body) {
|
||||
public Future<JsonObject> donateAccount(RoutingContext ctx) {
|
||||
JsonObject body = ctx.body().asJsonObject();
|
||||
if (body == null || StringUtils.isBlank(body.getString("panType"))
|
||||
|| StringUtils.isBlank(body.getString("authType"))) {
|
||||
return Future.succeededFuture(JsonResult.error("panType and authType are required").toJsonObject());
|
||||
}
|
||||
String ip = request.remoteAddress().host();
|
||||
String ip = ctx.request().remoteAddress().host();
|
||||
body.put("ip", ip);
|
||||
return dbService.saveDonatedAccount(body);
|
||||
}
|
||||
|
||||
@@ -366,11 +366,21 @@ public class DbServiceImpl implements DbService {
|
||||
|
||||
return Future.all(usernameFuture, passwordFuture, tokenFuture, failureTokenFuture)
|
||||
.map(compositeFuture -> {
|
||||
String username = usernameFuture.result();
|
||||
String password = passwordFuture.result();
|
||||
String token = tokenFuture.result();
|
||||
|
||||
// 如果解密后没有任何可用凭证,返回空对象,避免把密文当作明文认证参数下发给前端
|
||||
if (StringUtils.isBlank(username) && StringUtils.isBlank(password) && StringUtils.isBlank(token)) {
|
||||
log.warn("random donated account has no usable credential after decrypt, accountId={}", row.getLong("id"));
|
||||
return JsonResult.data(new JsonObject()).toJsonObject();
|
||||
}
|
||||
|
||||
JsonObject account = new JsonObject();
|
||||
account.put("authType", row.getString("auth_type"));
|
||||
account.put("username", usernameFuture.result());
|
||||
account.put("password", passwordFuture.result());
|
||||
account.put("token", tokenFuture.result());
|
||||
account.put("username", username);
|
||||
account.put("password", password);
|
||||
account.put("token", token);
|
||||
account.put("donatedAccountToken", failureTokenFuture.result());
|
||||
return JsonResult.data(account).toJsonObject();
|
||||
});
|
||||
@@ -450,8 +460,10 @@ public class DbServiceImpl implements DbService {
|
||||
return Future.succeededFuture(value);
|
||||
}
|
||||
return CryptoUtil.decrypt(value).recover(e -> {
|
||||
log.warn("decrypt donated account field failed, fallback to plaintext", e);
|
||||
return Future.succeededFuture(value);
|
||||
// value 看起来像密文但无法解密,通常是密钥轮换/不一致导致;
|
||||
// 不应回退为明文,否则会把密文误当 token/cookie 返回给调用方
|
||||
log.warn("decrypt donated account field failed, fallback to null to avoid ciphertext leakage", e);
|
||||
return Future.succeededFuture((String) null);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -30,10 +30,15 @@ public class CryptoUtil {
|
||||
secretKeyFuture = ConfigUtil.readYamlConfig("secret", vertx)
|
||||
.map(config -> {
|
||||
String key = config.getJsonObject("encrypt").getString("key");
|
||||
if (key == null || key.length() != 32) {
|
||||
throw new IllegalArgumentException("Invalid AES key length in secret.yml. Key must be 32 bytes.");
|
||||
if (key != null) {
|
||||
key = key.trim();
|
||||
}
|
||||
return new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
|
||||
byte[] keyBytes = key == null ? null : key.getBytes(StandardCharsets.UTF_8);
|
||||
if (keyBytes == null || keyBytes.length != 32) {
|
||||
int currentLen = keyBytes == null ? 0 : keyBytes.length;
|
||||
throw new IllegalArgumentException("Invalid AES key length in secret.yml. Key must be 32 bytes. current=" + currentLen);
|
||||
}
|
||||
return new SecretKeySpec(keyBytes, "AES");
|
||||
})
|
||||
.onFailure(err -> logger.error("Failed to load encryption key from secret.yml", err));
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user