diff --git a/parser/src/main/java/cn/qaiu/parser/impl/Ye2Tool.java b/parser/src/main/java/cn/qaiu/parser/impl/Ye2Tool.java index 55959fb..1d3e894 100644 --- a/parser/src/main/java/cn/qaiu/parser/impl/Ye2Tool.java +++ b/parser/src/main/java/cn/qaiu/parser/impl/Ye2Tool.java @@ -641,7 +641,14 @@ public class Ye2Tool extends PanBase { return; } - String normalizedShareKey = YeShareHostUtil.normalizeShareKey(shareLinkInfo.getShareKey()); + String rawShareKey = paramJson.getString("shareKey"); + if (StringUtils.isBlank(rawShareKey)) { + rawShareKey = paramJson.getString("ShareKey"); + } + if (StringUtils.isBlank(rawShareKey)) { + rawShareKey = shareLinkInfo.getShareKey(); + } + String normalizedShareKey = YeShareHostUtil.normalizeShareKey(rawShareKey); if (StringUtils.isNotEmpty(normalizedShareKey)) { JsonObject v2Body = new JsonObject() .put("ShareKey", normalizedShareKey) diff --git a/pom.xml b/pom.xml index 6aa4d87..7255521 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 0.3.9 + 0.4.0 17 17 17 diff --git a/web-front/package.json b/web-front/package.json index 4083d08..4f9e7ec 100644 --- a/web-front/package.json +++ b/web-front/package.json @@ -1,6 +1,6 @@ { "name": "nfd-web", - "version": "0.3.9", + "version": "0.4.0", "private": true, "scripts": { "serve": "vue-cli-service serve", diff --git a/web-service/src/main/java/cn/qaiu/lz/common/util/ParserAuthUtil.java b/web-service/src/main/java/cn/qaiu/lz/common/util/ParserAuthUtil.java new file mode 100644 index 0000000..cd14629 --- /dev/null +++ b/web-service/src/main/java/cn/qaiu/lz/common/util/ParserAuthUtil.java @@ -0,0 +1,203 @@ +package cn.qaiu.lz.common.util; + +import cn.qaiu.entity.ShareLinkInfo; +import cn.qaiu.lz.web.model.AuthParam; +import cn.qaiu.lz.web.service.DbService; +import cn.qaiu.parser.ParserCreate; +import cn.qaiu.vx.core.util.ConfigConstant; +import cn.qaiu.vx.core.util.VertxHolder; +import io.vertx.core.Future; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.json.JsonObject; +import io.vertx.core.shareddata.LocalMap; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.util.Map; + +@Slf4j +public class ParserAuthUtil { + + public static final String SKIP_CLIENT_LINKS = "_skipClientLinks"; + public static final String TEMP_AUTH_ADDED = "__TEMP_AUTH_ADDED"; + public static final String DONATED_ACCOUNT_TOKEN = "__AUTO_DONATED_ACCOUNT_TOKEN"; + + private ParserAuthUtil() { + } + + public static JsonObject buildOtherParam(HttpServerRequest request, String auth, String requestOrigin) { + return buildOtherParam(request, auth, requestOrigin, false); + } + + public static JsonObject buildOtherParam(HttpServerRequest request, String auth, String requestOrigin, + boolean skipClientLinks) { + JsonObject otherParam = JsonObject.of( + "UA", request.headers().get("user-agent"), + "_requestOrigin", requestOrigin + ); + if (skipClientLinks) { + otherParam.put(SKIP_CLIENT_LINKS, true); + } + + if (StringUtils.isNotBlank(auth)) { + AuthParam authParam = AuthParamCodec.decode(auth); + if (authParam != null && authParam.hasValidAuth()) { + otherParam.put("authType", authParam.getAuthType()); + otherParam.put("authToken", authParam.getPrimaryCredential()); + otherParam.put("authPassword", authParam.getPassword()); + otherParam.put("authInfo1", authParam.getExt1()); + otherParam.put("authInfo2", authParam.getExt2()); + otherParam.put("authInfo3", authParam.getExt3()); + otherParam.put("authInfo4", authParam.getExt4()); + otherParam.put("authInfo5", authParam.getExt5()); + if (StringUtils.isNotBlank(authParam.getDonatedAccountToken())) { + otherParam.put("donatedAccountToken", authParam.getDonatedAccountToken()); + } + log.debug("已解码认证参数: authType={}", authParam.getAuthType()); + } + } + + return otherParam; + } + + public static Future applyAuthParamsAndDonatedFallback(ParserCreate parserCreate, JsonObject otherParam, + DbService dbService) { + JsonObject params = otherParam == null ? new JsonObject() : otherParam; + parserCreate.getShareLinkInfo().getOtherParam().putAll(params.getMap()); + + if (params.containsKey("authType") || params.containsKey("authToken")) { + log.debug("从otherParam中检测到临时认证参数"); + URLParamUtil.addTempAuthParam(parserCreate, + params.getString("authType"), + params.getString("authToken"), + params.getString("authPassword"), + params.getString("authInfo1"), + params.getString("authInfo2"), + params.getString("authInfo3"), + params.getString("authInfo4"), + params.getString("authInfo5")); + } + + return applyDonatedAccountFallback(parserCreate, dbService); + } + + public static void recordDonatedAccountFailureIfNeeded(DbService dbService, JsonObject otherParam, + Throwable cause) { + if (!isLikelyAuthFailure(cause) || otherParam == null) { + return; + } + String donatedAccountToken = otherParam.getString("donatedAccountToken"); + if (StringUtils.isBlank(donatedAccountToken)) { + return; + } + dbService.recordDonatedAccountFailureByToken(donatedAccountToken) + .onFailure(e -> log.warn("记录捐赠账号失败次数失败", e)); + } + + public static void recordAutoDonatedFailureIfNeeded(DbService dbService, ShareLinkInfo shareLinkInfo, + Throwable cause) { + if (shareLinkInfo == null || !isLikelyAuthFailure(cause)) { + return; + } + Object tokenObj = shareLinkInfo.getOtherParam().get(DONATED_ACCOUNT_TOKEN); + if (!(tokenObj instanceof String) || StringUtils.isBlank((String) tokenObj)) { + return; + } + dbService.recordDonatedAccountFailureByToken((String) tokenObj) + .onFailure(e -> log.warn("记录自动捐赠账号失败次数失败", e)); + } + + private static Future applyDonatedAccountFallback(ParserCreate parserCreate, DbService dbService) { + ShareLinkInfo shareLinkInfo = parserCreate.getShareLinkInfo(); + Map otherParam = shareLinkInfo.getOtherParam(); + if (Boolean.TRUE.equals(otherParam.get(TEMP_AUTH_ADDED))) { + return Future.succeededFuture(); + } + String type = shareLinkInfo.getType(); + if (StringUtils.isBlank(type) || hasUsableStaticAuthConfig(type)) { + return Future.succeededFuture(); + } + return dbService.getRandomDonatedAccount(type.toUpperCase()) + .compose(res -> { + if (!Integer.valueOf(200).equals(res.getInteger("code"))) { + return Future.succeededFuture(); + } + JsonObject data = res.getJsonObject("data"); + if (data == null || data.isEmpty()) { + return Future.succeededFuture(); + } + String username = data.getString("username"); + String password = data.getString("password"); + String token = data.getString("token"); + if (StringUtils.isBlank(username) && StringUtils.isBlank(password) && StringUtils.isBlank(token)) { + return Future.succeededFuture(); + } + MultiMap tempAuth = MultiMap.caseInsensitiveMultiMap(); + if (StringUtils.isNotBlank(username)) { + tempAuth.set("username", username); + } + if (StringUtils.isNotBlank(password)) { + tempAuth.set("password", password); + } + if (StringUtils.isNotBlank(token)) { + tempAuth.set("token", token); + } + otherParam.put(ConfigConstant.AUTHS, tempAuth); + otherParam.put(TEMP_AUTH_ADDED, true); + String donatedAccountToken = data.getString("donatedAccountToken"); + if (StringUtils.isNotBlank(donatedAccountToken)) { + otherParam.put(DONATED_ACCOUNT_TOKEN, donatedAccountToken); + } + log.debug("已自动应用捐赠账号: type={}", type); + return Future.succeededFuture(); + }) + .recover(err -> { + log.warn("自动获取捐赠账号失败: type={}", type, err); + return Future.succeededFuture(); + }); + } + + private static boolean hasUsableStaticAuthConfig(String type) { + LocalMap localMap = VertxHolder.getVertxInstance().sharedData() + .getLocalMap(ConfigConstant.LOCAL); + if (!localMap.containsKey(ConfigConstant.AUTHS)) { + return false; + } + JsonObject auths = (JsonObject) localMap.get(ConfigConstant.AUTHS); + JsonObject cfg = auths.getJsonObject(type); + if (cfg == null) { + return false; + } + for (String key : cfg.fieldNames()) { + Object value = cfg.getValue(key); + if (value != null && StringUtils.isNotBlank(value.toString())) { + return true; + } + } + return false; + } + + private static boolean isLikelyAuthFailure(Throwable cause) { + if (cause == null) { + return false; + } + String msg = cause.getMessage(); + if (StringUtils.isBlank(msg)) { + return false; + } + String lower = msg.toLowerCase(); + return lower.contains("auth") + || lower.contains("token") + || lower.contains("cookie") + || lower.contains("password") + || lower.contains("credential") + || lower.contains("401") + || lower.contains("403") + || lower.contains("unauthorized") + || lower.contains("forbidden") + || lower.contains("expired") + || lower.contains("登录") + || lower.contains("认证"); + } +} \ No newline at end of file diff --git a/web-service/src/main/java/cn/qaiu/lz/web/controller/ParserApi.java b/web-service/src/main/java/cn/qaiu/lz/web/controller/ParserApi.java index cb925c6..6e16346 100644 --- a/web-service/src/main/java/cn/qaiu/lz/web/controller/ParserApi.java +++ b/web-service/src/main/java/cn/qaiu/lz/web/controller/ParserApi.java @@ -5,6 +5,7 @@ import cn.qaiu.entity.FileInfo; import cn.qaiu.entity.ShareLinkInfo; import cn.qaiu.lz.common.cache.CacheManager; import cn.qaiu.lz.common.util.AuthParamCodec; +import cn.qaiu.lz.common.util.ParserAuthUtil; import cn.qaiu.lz.common.util.URLParamUtil; import cn.qaiu.lz.web.model.AuthParam; import cn.qaiu.lz.web.model.CacheLinkInfo; @@ -159,7 +160,8 @@ public class ParserApi { } @RouteMapping("/getFileList") - public Future> getFileList(HttpServerRequest request, String pwd, String dirId, String uuid) { + public Future> getFileList(HttpServerRequest request, String pwd, String dirId, String uuid, + String auth) { String url = URLParamUtil.parserParams(request); ParserCreate parserCreate; try { @@ -168,6 +170,7 @@ public class ParserApi { return Future.failedFuture(e); } String linkPrefix = getLinkPrefix(request); + JsonObject otherParam = ParserAuthUtil.buildOtherParam(request, auth, linkPrefix); parserCreate.getShareLinkInfo().getOtherParam().put("domainName", linkPrefix); parserCreate.getShareLinkInfo().getOtherParam().put("_requestOrigin", linkPrefix); if (StringUtils.isNotBlank(dirId)) { @@ -176,13 +179,22 @@ public class ParserApi { if (StringUtils.isNotBlank(uuid)) { parserCreate.getShareLinkInfo().getOtherParam().put("uuid", uuid); } - IPanTool tool = parserCreate.createTool(); - return IPanTool.closeAfter(tool, tool::parseFileList); + return ParserAuthUtil.applyAuthParamsAndDonatedFallback(parserCreate, otherParam, dbService) + .compose(v -> { + URLParamUtil.addParam(parserCreate); + IPanTool tool = parserCreate.createTool(); + return IPanTool.closeAfter(tool, tool::parseFileList) + .onFailure(t -> { + ParserAuthUtil.recordDonatedAccountFailureIfNeeded(dbService, otherParam, t); + ParserAuthUtil.recordAutoDonatedFailureIfNeeded(dbService, + parserCreate.getShareLinkInfo(), t); + }); + }); } // 目录解析下载文件 // @RouteMapping("/getFileDownUrl/:type/:param") - public Future getFileDownUrl(HttpServerRequest request, String type, String param) { + public Future getFileDownUrl(HttpServerRequest request, String type, String param, String auth) { ParserCreate parserCreate = ParserCreate.fromType(type).shareKey("-") // shareKey not null .setShareLinkInfoPwd("-"); @@ -198,17 +210,28 @@ public class ParserApi { // domainName String linkPrefix = getLinkPrefix(request); + JsonObject otherParam = ParserAuthUtil.buildOtherParam(request, auth, linkPrefix, true); shareLinkInfo.getOtherParam().put("domainName", linkPrefix); shareLinkInfo.getOtherParam().put("_requestOrigin", linkPrefix); - IPanTool tool = parserCreate.createTool(); - return IPanTool.closeAfter(tool, tool::parseById); + return ParserAuthUtil.applyAuthParamsAndDonatedFallback(parserCreate, otherParam, dbService) + .compose(v -> { + URLParamUtil.addParam(parserCreate); + IPanTool tool = parserCreate.createTool(); + return IPanTool.closeAfter(tool, tool::parseById) + .onFailure(t -> { + ParserAuthUtil.recordDonatedAccountFailureIfNeeded(dbService, otherParam, t); + ParserAuthUtil.recordAutoDonatedFailureIfNeeded(dbService, + parserCreate.getShareLinkInfo(), t); + }); + }); } @RouteMapping("/redirectUrl/:type/:param") - public Future redirectUrl(HttpServerRequest request, HttpServerResponse response, String type, String param) { + public Future redirectUrl(HttpServerRequest request, HttpServerResponse response, String type, String param, + String auth) { Promise promise = Promise.promise(); - getFileDownUrl(request, type, param) + getFileDownUrl(request, type, param, auth) .onSuccess(res -> { ResponseUtil.redirect(response, res, promise); }) @@ -283,11 +306,12 @@ public class ParserApi { @RouteMapping("/viewUrl/:type/:param") - public Future viewUrl(HttpServerRequest request, HttpServerResponse response, String type, String param) { + public Future viewUrl(HttpServerRequest request, HttpServerResponse response, String type, String param, + String auth) { Promise promise = Promise.promise(); String viewPrefix = SharedDataUtil.getJsonConfig("server").getString("previewURL"); - getFileDownUrl(request, type, param) + getFileDownUrl(request, type, param, auth) .onSuccess(res -> { String url = viewPrefix + URLEncoder.encode(res, StandardCharsets.UTF_8); ResponseUtil.redirect(response, url, promise); diff --git a/web-service/src/main/java/cn/qaiu/lz/web/controller/ServerApi.java b/web-service/src/main/java/cn/qaiu/lz/web/controller/ServerApi.java index 282d444..1536dc8 100644 --- a/web-service/src/main/java/cn/qaiu/lz/web/controller/ServerApi.java +++ b/web-service/src/main/java/cn/qaiu/lz/web/controller/ServerApi.java @@ -1,8 +1,7 @@ package cn.qaiu.lz.web.controller; -import cn.qaiu.lz.common.util.AuthParamCodec; +import cn.qaiu.lz.common.util.ParserAuthUtil; import cn.qaiu.lz.common.util.URLParamUtil; -import cn.qaiu.lz.web.model.AuthParam; import cn.qaiu.lz.web.model.CacheLinkInfo; import cn.qaiu.lz.web.service.CacheService; import cn.qaiu.lz.web.service.DbService; @@ -29,7 +28,7 @@ import lombok.extern.slf4j.Slf4j; @RouteHandler("/") public class ServerApi { - private static final String SKIP_CLIENT_LINKS = "_skipClientLinks"; + private static final String SKIP_CLIENT_LINKS = ParserAuthUtil.SKIP_CLIENT_LINKS; private final CacheService cacheService = AsyncServiceUtil.getAsyncServiceInstance(CacheService.class); private final DbService dbService = AsyncServiceUtil.getAsyncServiceInstance(DbService.class); @@ -47,7 +46,7 @@ public class ServerApi { addCacheHeaders(response, res), res.getDirectLink(), promise)) .onFailure(t -> { - recordDonatedAccountFailureIfNeeded(otherParam, t); + ParserAuthUtil.recordDonatedAccountFailureIfNeeded(dbService, otherParam, t); promise.tryFail(t); }); return promise.future(); @@ -58,14 +57,14 @@ public class ServerApi { String url = URLParamUtil.parserParams(request); JsonObject otherParam = buildOtherParam(request, auth); return cacheService.getCachedByShareUrlAndPwd(url, pwd, otherParam) - .onFailure(t -> recordDonatedAccountFailureIfNeeded(otherParam, t)); + .onFailure(t -> ParserAuthUtil.recordDonatedAccountFailureIfNeeded(dbService, otherParam, t)); } public Future parseJsonForRedirect(HttpServerRequest request, String pwd, String auth) { String url = URLParamUtil.parserParams(request); JsonObject otherParam = buildOtherParam(request, auth, true); return cacheService.getCachedByShareUrlAndPwd(url, pwd, otherParam) - .onFailure(t -> recordDonatedAccountFailureIfNeeded(otherParam, t)); + .onFailure(t -> ParserAuthUtil.recordDonatedAccountFailureIfNeeded(dbService, otherParam, t)); } @RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET) @@ -146,66 +145,6 @@ public class ServerApi { } private JsonObject buildOtherParam(HttpServerRequest request, String auth, boolean skipClientLinks) { - JsonObject otherParam = JsonObject.of("UA", request.headers().get("user-agent"), "_requestOrigin", resolveOrigin(request)); - if (skipClientLinks) { - otherParam.put(SKIP_CLIENT_LINKS, true); - } - - // 解码认证参数 - if (auth != null && !auth.isEmpty()) { - AuthParam authParam = AuthParamCodec.decode(auth); - if (authParam != null && authParam.hasValidAuth()) { - // 将认证参数放入 otherParam - otherParam.put("authType", authParam.getAuthType()); - otherParam.put("authToken", authParam.getPrimaryCredential()); - otherParam.put("authPassword", authParam.getPassword()); - otherParam.put("authInfo1", authParam.getExt1()); - otherParam.put("authInfo2", authParam.getExt2()); - otherParam.put("authInfo3", authParam.getExt3()); - otherParam.put("authInfo4", authParam.getExt4()); - otherParam.put("authInfo5", authParam.getExt5()); - if (authParam.getDonatedAccountToken() != null && !authParam.getDonatedAccountToken().isBlank()) { - otherParam.put("donatedAccountToken", authParam.getDonatedAccountToken()); - } - log.debug("已解码认证参数: authType={}", authParam.getAuthType()); - } - } - - return otherParam; - } - - private void recordDonatedAccountFailureIfNeeded(JsonObject otherParam, Throwable cause) { - if (!isLikelyAuthFailure(cause)) { - return; - } - String donatedAccountToken = otherParam.getString("donatedAccountToken"); - if (donatedAccountToken == null || donatedAccountToken.isBlank()) { - return; - } - dbService.recordDonatedAccountFailureByToken(donatedAccountToken) - .onFailure(e -> log.warn("记录捐赠账号失败次数失败", e)); - } - - private boolean isLikelyAuthFailure(Throwable cause) { - if (cause == null) { - return false; - } - String msg = cause.getMessage(); - if (msg == null || msg.isBlank()) { - return false; - } - String lower = msg.toLowerCase(); - return lower.contains("auth") - || lower.contains("token") - || lower.contains("cookie") - || lower.contains("password") - || lower.contains("credential") - || lower.contains("401") - || lower.contains("403") - || lower.contains("unauthorized") - || lower.contains("forbidden") - || lower.contains("expired") - || lower.contains("登录") - || lower.contains("认证"); + return ParserAuthUtil.buildOtherParam(request, auth, resolveOrigin(request), skipClientLinks); } } diff --git a/web-service/src/main/java/cn/qaiu/lz/web/service/impl/CacheServiceImpl.java b/web-service/src/main/java/cn/qaiu/lz/web/service/impl/CacheServiceImpl.java index 6a8cb73..a6b5513 100644 --- a/web-service/src/main/java/cn/qaiu/lz/web/service/impl/CacheServiceImpl.java +++ b/web-service/src/main/java/cn/qaiu/lz/web/service/impl/CacheServiceImpl.java @@ -5,6 +5,7 @@ import cn.qaiu.entity.ShareLinkInfo; import cn.qaiu.lz.common.cache.CacheConfigLoader; import cn.qaiu.lz.common.cache.CacheManager; import cn.qaiu.lz.common.cache.CacheTotalField; +import cn.qaiu.lz.common.util.ParserAuthUtil; import cn.qaiu.lz.common.util.URLParamUtil; import cn.qaiu.lz.web.model.CacheLinkInfo; import cn.qaiu.lz.web.service.CacheService; @@ -15,15 +16,10 @@ import cn.qaiu.parser.clientlink.ClientLinkGeneratorFactory; import cn.qaiu.parser.clientlink.ClientLinkType; import cn.qaiu.vx.core.annotaions.Service; import cn.qaiu.vx.core.util.AsyncServiceUtil; -import cn.qaiu.vx.core.util.ConfigConstant; -import cn.qaiu.vx.core.util.VertxHolder; import io.vertx.core.Future; -import io.vertx.core.MultiMap; import io.vertx.core.Promise; import io.vertx.core.json.JsonObject; -import io.vertx.core.shareddata.LocalMap; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateFormatUtils; import java.util.Date; @@ -34,9 +30,7 @@ import java.util.Map; @Slf4j public class CacheServiceImpl implements CacheService { - private static final String SKIP_CLIENT_LINKS = "_skipClientLinks"; - private static final String TEMP_AUTH_ADDED = "__TEMP_AUTH_ADDED"; - private static final String DONATED_ACCOUNT_TOKEN = "__AUTO_DONATED_ACCOUNT_TOKEN"; + private static final String SKIP_CLIENT_LINKS = ParserAuthUtil.SKIP_CLIENT_LINKS; private final CacheManager cacheManager = new CacheManager(); private final DbService dbService = AsyncServiceUtil.getAsyncServiceInstance(DbService.class); @@ -82,7 +76,7 @@ public class CacheServiceImpl implements CacheService { return; } IPanTool.closeAfter(tool, tool::parse).onFailure(err -> { - recordAutoDonatedFailureIfNeeded(shareLinkInfo, err); + ParserAuthUtil.recordAutoDonatedFailureIfNeeded(dbService, shareLinkInfo, err); }).onSuccess(redirectUrl -> { // 使用 effectiveCacheDuration long expires = System.currentTimeMillis() + effectiveCacheDuration * 60 * 1000L; @@ -153,124 +147,6 @@ public class CacheServiceImpl implements CacheService { return DateFormatUtils.format(new Date(ts), "yyyy-MM-dd HH:mm:ss"); } - /** - * 当本次请求没有携带临时认证参数(auth=xxx),且该网盘类型在 app-dev.yml 中也没有配置可用的 - * 静态账号密码/token 时,自动从捐赠账号池中随机挑选一个可用账号使用, - * 这样捐赠者捐赠账号后,普通请求(不带 auth 参数)也能直接受益,无需前端额外拼接参数。 - */ - private Future applyDonatedAccountFallback(ParserCreate parserCreate) { - ShareLinkInfo shareLinkInfo = parserCreate.getShareLinkInfo(); - Map otherParam = shareLinkInfo.getOtherParam(); - if (Boolean.TRUE.equals(otherParam.get(TEMP_AUTH_ADDED))) { - // 请求已经携带了个人配置/捐赠账号的临时认证参数,无需再自动回退 - return Future.succeededFuture(); - } - String type = shareLinkInfo.getType(); - if (StringUtils.isBlank(type) || hasUsableStaticAuthConfig(type)) { - return Future.succeededFuture(); - } - return dbService.getRandomDonatedAccount(type.toUpperCase()) - .compose(res -> { - if (!Integer.valueOf(200).equals(res.getInteger("code"))) { - return Future.succeededFuture(); - } - JsonObject data = res.getJsonObject("data"); - if (data == null || data.isEmpty()) { - return Future.succeededFuture(); - } - String username = data.getString("username"); - String password = data.getString("password"); - String token = data.getString("token"); - if (StringUtils.isBlank(username) && StringUtils.isBlank(password) && StringUtils.isBlank(token)) { - return Future.succeededFuture(); - } - MultiMap tempAuth = MultiMap.caseInsensitiveMultiMap(); - if (StringUtils.isNotBlank(username)) { - tempAuth.set("username", username); - } - if (StringUtils.isNotBlank(password)) { - tempAuth.set("password", password); - } - if (StringUtils.isNotBlank(token)) { - tempAuth.set("token", token); - } - otherParam.put(ConfigConstant.AUTHS, tempAuth); - otherParam.put(TEMP_AUTH_ADDED, true); - String donatedAccountToken = data.getString("donatedAccountToken"); - if (StringUtils.isNotBlank(donatedAccountToken)) { - otherParam.put(DONATED_ACCOUNT_TOKEN, donatedAccountToken); - } - log.debug("已自动应用捐赠账号: type={}", type); - return Future.succeededFuture(); - }) - .recover(err -> { - log.warn("自动获取捐赠账号失败: type={}", type, err); - return Future.succeededFuture(); - }); - } - - /** - * 检查 app-dev.yml 中该网盘类型是否已配置了可用的静态认证信息(非空的 username/password/token/authorization 等)。 - * 如果已配置,则不需要再自动回退到捐赠账号池。 - */ - private boolean hasUsableStaticAuthConfig(String type) { - LocalMap localMap = VertxHolder.getVertxInstance().sharedData() - .getLocalMap(ConfigConstant.LOCAL); - if (!localMap.containsKey(ConfigConstant.AUTHS)) { - return false; - } - JsonObject auths = (JsonObject) localMap.get(ConfigConstant.AUTHS); - JsonObject cfg = auths.getJsonObject(type); - if (cfg == null) { - return false; - } - for (String key : cfg.fieldNames()) { - Object value = cfg.getValue(key); - if (value != null && StringUtils.isNotBlank(value.toString())) { - return true; - } - } - return false; - } - - /** - * 自动回退使用的捐赠账号解析失败时,记录一次失败次数(达到阈值后台账号会被自动禁用)。 - */ - private void recordAutoDonatedFailureIfNeeded(ShareLinkInfo shareLinkInfo, Throwable cause) { - Object tokenObj = shareLinkInfo.getOtherParam().get(DONATED_ACCOUNT_TOKEN); - if (!(tokenObj instanceof String) || StringUtils.isBlank((String) tokenObj)) { - return; - } - if (!isLikelyAuthFailure(cause)) { - return; - } - dbService.recordDonatedAccountFailureByToken((String) tokenObj) - .onFailure(e -> log.warn("记录自动捐赠账号失败次数失败", e)); - } - - private boolean isLikelyAuthFailure(Throwable cause) { - if (cause == null) { - return false; - } - String msg = cause.getMessage(); - if (msg == null || msg.isBlank()) { - return false; - } - String lower = msg.toLowerCase(); - return lower.contains("auth") - || lower.contains("token") - || lower.contains("cookie") - || lower.contains("password") - || lower.contains("credential") - || lower.contains("401") - || lower.contains("403") - || lower.contains("unauthorized") - || lower.contains("forbidden") - || lower.contains("expired") - || lower.contains("登录") - || lower.contains("认证"); - } - private boolean shouldGenerateClientLinks(ShareLinkInfo shareLinkInfo) { if (shareLinkInfo == null || shareLinkInfo.getOtherParam() == null) { return true; @@ -408,10 +284,8 @@ public class CacheServiceImpl implements CacheService { } catch (Exception e) { return Future.failedFuture(e); } - parserCreate.getShareLinkInfo().getOtherParam().putAll(otherParam.getMap()); - ParserCreate finalParserCreate = parserCreate; - return applyDonatedAccountFallback(finalParserCreate) + return ParserAuthUtil.applyAuthParamsAndDonatedFallback(finalParserCreate, otherParam, dbService) .compose(v -> getAndSaveCachedShareLink(finalParserCreate)); } @@ -423,24 +297,8 @@ public class CacheServiceImpl implements CacheService { } catch (Exception e) { return Future.failedFuture(e); } - parserCreate.getShareLinkInfo().getOtherParam().putAll(otherParam.getMap()); - - // 检查是否有临时认证参数 - if (otherParam.containsKey("authType") || otherParam.containsKey("authToken")) { - log.debug("从otherParam中检测到临时认证参数"); - URLParamUtil.addTempAuthParam(parserCreate, - otherParam.getString("authType"), - otherParam.getString("authToken"), - otherParam.getString("authPassword"), - otherParam.getString("authInfo1"), - otherParam.getString("authInfo2"), - otherParam.getString("authInfo3"), - otherParam.getString("authInfo4"), - otherParam.getString("authInfo5")); - } - ParserCreate finalParserCreate = parserCreate; - return applyDonatedAccountFallback(finalParserCreate) + return ParserAuthUtil.applyAuthParamsAndDonatedFallback(finalParserCreate, otherParam, dbService) .compose(v -> getAndSaveCachedShareLink(finalParserCreate)); } }