mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-10-11 09:14:25 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ab9df7826 |
@@ -238,6 +238,8 @@ auths:
|
||||
```
|
||||
|
||||
> ⚠️ 注意:YAML 中 key 后面不写值(如 `authorization:` 空着)等同于没配置,不会生效,必须填入真实的账号密码或 token 内容。
|
||||
>
|
||||
> 若 123 云盘账号密码登录触发验证码或风控,解析会直接失败并提示改用 Authorization,不会尝试验证码。请改填上面的 `authorization` / `token`(浏览器 localStorage 的 `authorToken`,或请求头 `authorization` / Cookie `sso-token` 里的 JWT)。
|
||||
|
||||
如果只是临时调用一次,不想改动服务端配置,也可以用上面提到的 `auth` 参数临时传递(`authType` 可选 `password`/`accesstoken`/`authorization`),无需重启服务,仅本次请求生效。
|
||||
|
||||
|
||||
@@ -109,6 +109,8 @@ URL解码 → Base64解码 → AES解密 → JSON对象
|
||||
}
|
||||
```
|
||||
|
||||
> 123云盘(`ye`)账号密码登录如果返回验证码或风控,解析会直接失败,并提示改用 Authorization,不会尝试验证码。请在浏览器登录后复制 `authorToken` / 请求头 `authorization` / Cookie `sso-token`,用 `authType=authorization` 或 `accesstoken` 传入,或写入 `app-dev.yml` 的 `auths.ye.authorization`。
|
||||
|
||||
#### 4. 自定义认证
|
||||
```json
|
||||
{
|
||||
@@ -339,3 +341,4 @@ server:
|
||||
## 更新日志
|
||||
|
||||
- **2026-02-05**: 初始版本,支持 accesstoken、cookie、password、custom 认证类型
|
||||
- **2026-09-27**: 补充 123云盘账号密码登录触发验证码/风控时改用 Authorization 的说明
|
||||
|
||||
@@ -57,6 +57,8 @@
|
||||
}
|
||||
```
|
||||
|
||||
> 123云盘(`ye`)账号密码登录触发验证码/风控时,请改用 `authType=authorization` 或 `accesstoken`(`token` 填浏览器中的 Bearer token / JWT),或在 `app-dev.yml` 配置 `auths.ye.authorization`。解析不会尝试验证码。
|
||||
|
||||
### 自定义认证
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 账号密码登录撞上验证码/风控时的用户提示。
|
||||
* 只识别失败形态并引导改用 Authorization,不实现验证码求解、扫码或重试。
|
||||
*/
|
||||
public final class PasswordLoginRisk {
|
||||
|
||||
/**
|
||||
* 123 云盘(ye)账号密码登录被验证码/风控拦住时返回给调用方的固定说明。
|
||||
* 上游 code/message 会另附在这句话后面,便于和“密码错误”区分。
|
||||
*/
|
||||
public static final String AUTHORIZATION_HINT =
|
||||
"账号密码登录触发了验证码/风控,已停止用户名密码登录(不会尝试验证码)。"
|
||||
+ "请改为配置 Authorization(浏览器登录后的 Bearer token / JWT,"
|
||||
+ "例如 localStorage 的 authorToken、请求头 authorization 或 Cookie 的 sso-token)。"
|
||||
+ "静态配置写在 app-dev.yml 的 auths.ye.authorization(与 auths.ye.token 等价);"
|
||||
+ "临时请求使用 authType=authorization 或 authType=accesstoken,token 填该令牌(不要重复带 Bearer 前缀)。"
|
||||
+ "说明见 parser/doc/auth-param/。";
|
||||
|
||||
/**
|
||||
* 上游文案命中这些片段时视为验证码/风控,而不是单纯的密码错误。
|
||||
* 依据公开客户端(AList/OpenList)实际展示过的 123 登录失败文案,以及登录页上的滑块/人机验证提示。
|
||||
*/
|
||||
private static final String[] RISK_KEYWORDS = {
|
||||
"验证码",
|
||||
"风控",
|
||||
"安全风险",
|
||||
"安全验证",
|
||||
"安全校验",
|
||||
"滑块",
|
||||
"滑动验证",
|
||||
"拼图验证",
|
||||
"短信验证",
|
||||
"微信登录",
|
||||
"微信进行登录",
|
||||
"境外登录",
|
||||
"需要验证",
|
||||
"请验证",
|
||||
"请你验证",
|
||||
"进行验证",
|
||||
"完成验证",
|
||||
"二次验证",
|
||||
"人机验证",
|
||||
"操作频繁",
|
||||
"请求过于频繁",
|
||||
"请求频繁",
|
||||
"captcha",
|
||||
"recaptcha",
|
||||
"risk control"
|
||||
};
|
||||
|
||||
private PasswordLoginRisk() {
|
||||
}
|
||||
|
||||
public static boolean isLoginSuccess(JsonObject json) {
|
||||
if (json == null) {
|
||||
return false;
|
||||
}
|
||||
Integer code = readCode(json);
|
||||
return code != null && code == 200 && StringUtils.isNotBlank(readToken(json));
|
||||
}
|
||||
|
||||
public static boolean indicatesCaptchaOrRisk(JsonObject json) {
|
||||
if (json == null) {
|
||||
return false;
|
||||
}
|
||||
if (hasTruthyRiskField(json) || hasTruthyRiskField(dataObject(json))) {
|
||||
return true;
|
||||
}
|
||||
return textIndicatesCaptchaOrRisk(collectMessageText(json));
|
||||
}
|
||||
|
||||
public static boolean textIndicatesCaptchaOrRisk(String text) {
|
||||
if (StringUtils.isBlank(text)) {
|
||||
return false;
|
||||
}
|
||||
String lower = text.toLowerCase(Locale.ROOT);
|
||||
for (String keyword : RISK_KEYWORDS) {
|
||||
if (lower.contains(keyword.toLowerCase(Locale.ROOT))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 非成功登录响应的用户可见说明。验证码/风控走 Authorization 引导,其余保持“登录失败: 上游文案”。
|
||||
*/
|
||||
public static String describeLoginFailure(JsonObject json) {
|
||||
if (json == null) {
|
||||
return "登录响应格式异常";
|
||||
}
|
||||
if (indicatesCaptchaOrRisk(json)) {
|
||||
return AUTHORIZATION_HINT + " 上游返回:" + upstreamSummary(json);
|
||||
}
|
||||
Integer code = readCode(json);
|
||||
if (code != null && code == 200) {
|
||||
return "未获取到token";
|
||||
}
|
||||
String message = firstMessage(json);
|
||||
if (StringUtils.isBlank(message)) {
|
||||
return "登录失败";
|
||||
}
|
||||
return "登录失败: " + message;
|
||||
}
|
||||
|
||||
public static String nonJsonLoginFailure(String body) {
|
||||
if (textIndicatesCaptchaOrRisk(body)) {
|
||||
return AUTHORIZATION_HINT + " 上游返回:" + truncate(body, 300);
|
||||
}
|
||||
return "登录响应格式异常: " + body;
|
||||
}
|
||||
|
||||
private static String collectMessageText(JsonObject json) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
appendMessage(text, json);
|
||||
JsonObject data = dataObject(json);
|
||||
if (data != null) {
|
||||
appendMessage(text, data);
|
||||
}
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
private static void appendMessage(StringBuilder text, JsonObject json) {
|
||||
append(text, json.getString("message"));
|
||||
append(text, json.getString("msg"));
|
||||
}
|
||||
|
||||
private static void append(StringBuilder text, String value) {
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
if (text.length() > 0) {
|
||||
text.append(' ');
|
||||
}
|
||||
text.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasTruthyRiskField(JsonObject obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
for (String key : obj.fieldNames()) {
|
||||
if (!isRiskFieldName(key)) {
|
||||
continue;
|
||||
}
|
||||
if (isTruthyRiskValue(obj.getValue(key))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isRiskFieldName(String key) {
|
||||
String lower = key.toLowerCase(Locale.ROOT);
|
||||
return lower.contains("captcha")
|
||||
|| lower.contains("vcode")
|
||||
|| lower.contains("risk")
|
||||
|| "needverify".equals(lower)
|
||||
|| "need_verify".equals(lower)
|
||||
|| "slideverify".equals(lower)
|
||||
|| "verifycode".equals(lower)
|
||||
|| "verify_code".equals(lower);
|
||||
}
|
||||
|
||||
private static boolean isTruthyRiskValue(Object value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).doubleValue() != 0d;
|
||||
}
|
||||
if (value instanceof CharSequence) {
|
||||
String text = value.toString().trim();
|
||||
if (text.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
String lower = text.toLowerCase(Locale.ROOT);
|
||||
return !"false".equals(lower) && !"0".equals(lower) && !"null".equals(lower) && !"none".equals(lower);
|
||||
}
|
||||
if (value instanceof JsonObject) {
|
||||
return !((JsonObject) value).isEmpty();
|
||||
}
|
||||
if (value instanceof JsonArray) {
|
||||
return !((JsonArray) value).isEmpty();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String upstreamSummary(JsonObject json) {
|
||||
StringBuilder summary = new StringBuilder();
|
||||
Integer code = readCode(json);
|
||||
if (code != null) {
|
||||
summary.append("code=").append(code);
|
||||
}
|
||||
String message = firstMessage(json);
|
||||
if (StringUtils.isNotBlank(message)) {
|
||||
if (summary.length() > 0) {
|
||||
summary.append(", ");
|
||||
}
|
||||
summary.append("message=").append(message);
|
||||
}
|
||||
if (summary.length() == 0) {
|
||||
summary.append(json.encode());
|
||||
}
|
||||
return truncate(summary.toString(), 300);
|
||||
}
|
||||
|
||||
private static String firstMessage(JsonObject json) {
|
||||
String message = json.getString("message");
|
||||
if (StringUtils.isBlank(message)) {
|
||||
message = json.getString("msg");
|
||||
}
|
||||
if (StringUtils.isBlank(message)) {
|
||||
JsonObject data = dataObject(json);
|
||||
if (data != null) {
|
||||
message = data.getString("message");
|
||||
if (StringUtils.isBlank(message)) {
|
||||
message = data.getString("msg");
|
||||
}
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
static String readToken(JsonObject json) {
|
||||
JsonObject data = dataObject(json);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
Object token = data.getValue("token");
|
||||
return token == null ? null : token.toString();
|
||||
}
|
||||
|
||||
private static JsonObject dataObject(JsonObject json) {
|
||||
Object data = json.getValue("data");
|
||||
return data instanceof JsonObject ? (JsonObject) data : null;
|
||||
}
|
||||
|
||||
static Integer readCode(JsonObject json) {
|
||||
Object code = json.getValue("code");
|
||||
if (code instanceof Number) {
|
||||
return ((Number) code).intValue();
|
||||
}
|
||||
if (code instanceof String && StringUtils.isNumeric(((String) code).trim())) {
|
||||
return Integer.parseInt(((String) code).trim());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String truncate(String value, int max) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
if (trimmed.length() <= max) {
|
||||
return trimmed;
|
||||
}
|
||||
return trimmed.substring(0, max) + "...";
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package cn.qaiu.parser.impl;
|
||||
import cn.qaiu.entity.FileInfo;
|
||||
import cn.qaiu.entity.ShareLinkInfo;
|
||||
import cn.qaiu.parser.PanBase;
|
||||
import cn.qaiu.parser.PasswordLoginRisk;
|
||||
import cn.qaiu.parser.TokenCache;
|
||||
import cn.qaiu.util.CommonUtils;
|
||||
import cn.qaiu.util.FileSizeConverter;
|
||||
@@ -272,24 +273,27 @@ public class Ye2Tool extends PanBase {
|
||||
.putHeader("platform", "web")
|
||||
.sendJsonObject(loginBody)
|
||||
.onSuccess(res -> {
|
||||
JsonObject json = res.bodyAsJsonObject();
|
||||
JsonObject json;
|
||||
try {
|
||||
json = res.bodyAsJsonObject();
|
||||
} catch (Exception e) {
|
||||
promise.fail(PasswordLoginRisk.nonJsonLoginFailure(res.bodyAsString()));
|
||||
return;
|
||||
}
|
||||
if (json == null) {
|
||||
promise.fail("登录响应格式异常: " + res.bodyAsString());
|
||||
promise.fail(PasswordLoginRisk.nonJsonLoginFailure(res.bodyAsString()));
|
||||
return;
|
||||
}
|
||||
if (!json.containsKey("code")) {
|
||||
promise.fail("登录响应格式异常: " + res.bodyAsString());
|
||||
return;
|
||||
}
|
||||
if (json.getInteger("code") != 200) {
|
||||
promise.fail("登录失败: " + json.getString("message"));
|
||||
// 验证码/风控只提示改用 Authorization,不求解、不重试。
|
||||
if (!PasswordLoginRisk.isLoginSuccess(json)) {
|
||||
promise.fail(PasswordLoginRisk.describeLoginFailure(json));
|
||||
return;
|
||||
}
|
||||
JsonObject data = json.getJsonObject("data");
|
||||
if (data == null || !data.containsKey("token")) {
|
||||
promise.fail("未获取到token");
|
||||
return;
|
||||
}
|
||||
String ssoToken = data.getString("token");
|
||||
String expireStr = data.getString("expire");
|
||||
long expireMs;
|
||||
|
||||
@@ -55,8 +55,50 @@ def encode123(url, way, version, timestamp):
|
||||
# 返回加密后的URL参数
|
||||
return f"?{y}={time_long}-{a}-{final_crc}"
|
||||
|
||||
# 与 parser PasswordLoginRisk 保持一致:验证码/风控只提示改用 Authorization,不求解、不重试。
|
||||
_RISK_KEYWORDS = (
|
||||
"验证码", "风控", "安全风险", "安全验证", "安全校验", "滑块", "滑动验证",
|
||||
"拼图验证", "短信验证", "微信登录", "微信进行登录", "境外登录", "需要验证", "请验证",
|
||||
"请你验证", "进行验证", "完成验证", "二次验证", "人机验证", "操作频繁", "请求过于频繁",
|
||||
"请求频繁", "captcha", "recaptcha", "risk control",
|
||||
)
|
||||
_AUTH_HINT = (
|
||||
"账号密码登录触发了验证码/风控,已停止用户名密码登录(不会尝试验证码)。"
|
||||
"请改为配置 Authorization(浏览器登录后的 Bearer token / JWT,"
|
||||
"例如 localStorage 的 authorToken、请求头 authorization 或 Cookie 的 sso-token)。"
|
||||
"静态配置写在 app-dev.yml 的 auths.ye.authorization(与 auths.ye.token 等价);"
|
||||
"临时请求使用 authType=authorization 或 authType=accesstoken,token 填该令牌(不要重复带 Bearer 前缀)。"
|
||||
"说明见 parser/doc/auth-param/。"
|
||||
)
|
||||
|
||||
|
||||
def format_login_failure(result):
|
||||
"""把 123 登录失败响应转成用户可见说明。"""
|
||||
if not isinstance(result, dict):
|
||||
return "登录失败"
|
||||
message = result.get("message") or result.get("msg") or ""
|
||||
data = result.get("data") if isinstance(result.get("data"), dict) else {}
|
||||
data_message = data.get("message") or data.get("msg") or ""
|
||||
blob = f"{message} {data_message}".lower()
|
||||
risk_field = any(
|
||||
key.lower().find(flag) >= 0 and data.get(key) not in (None, False, 0, "", "false", "0")
|
||||
for key in data
|
||||
for flag in ("captcha", "vcode", "risk", "needverify", "need_verify", "slideverify", "verifycode", "verify_code")
|
||||
)
|
||||
if risk_field or any(keyword.lower() in blob for keyword in _RISK_KEYWORDS):
|
||||
code = result.get("code")
|
||||
upstream = message or data_message
|
||||
extra = f"code={code}" if code is not None else ""
|
||||
if upstream:
|
||||
extra = f"{extra}, message={upstream}" if extra else f"message={upstream}"
|
||||
return f"{_AUTH_HINT} 上游返回:{extra}" if extra else _AUTH_HINT
|
||||
if result.get("code") == 200:
|
||||
return "未获取到token"
|
||||
return f"登录失败: {message}" if message else "登录失败"
|
||||
|
||||
|
||||
def login_123pan(username, password):
|
||||
"""登录123盘获取token"""
|
||||
"""登录123盘获取token。成功返回 token,失败返回 None(错误已打印)。"""
|
||||
print(f"🔐 正在登录账号: {username}")
|
||||
|
||||
login_data = {
|
||||
@@ -73,16 +115,18 @@ def login_123pan(username, password):
|
||||
)
|
||||
result = response.json()
|
||||
|
||||
if result.get('code') == 200:
|
||||
if result.get('code') == 200 and result.get('data', {}).get('token'):
|
||||
token = result.get('data', {}).get('token', '')
|
||||
print(f"✅ 登录成功!")
|
||||
return token
|
||||
else:
|
||||
error_msg = result.get('message', '未知错误')
|
||||
print(f"❌ 登录失败: {error_msg}")
|
||||
error_msg = format_login_failure(result)
|
||||
login_123pan.last_error = error_msg
|
||||
print(f"❌ {error_msg}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"❌ 登录请求失败: {e}")
|
||||
login_123pan.last_error = f"登录请求失败: {e}"
|
||||
print(f"❌ {login_123pan.last_error}")
|
||||
return None
|
||||
|
||||
def get_share_info(share_key, password=''):
|
||||
@@ -189,7 +233,7 @@ def start(link, password='', username='', user_password=''):
|
||||
if not token:
|
||||
return {
|
||||
"code": 201,
|
||||
"message": "登录失败"
|
||||
"message": getattr(login_123pan, "last_error", None) or "登录失败"
|
||||
}
|
||||
else:
|
||||
print("⚠️ 未提供登录信息,某些文件可能无法下载")
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package cn.qaiu.parser;
|
||||
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PasswordLoginRiskTest {
|
||||
|
||||
@Test
|
||||
public void securityRiskMessageGuidesAuthorization() {
|
||||
JsonObject body = new JsonObject()
|
||||
.put("code", 401)
|
||||
.put("message", "当前账号存在安全风险,请使用短信验证码或者微信进行登录。");
|
||||
|
||||
assertTrue(PasswordLoginRisk.indicatesCaptchaOrRisk(body));
|
||||
String message = PasswordLoginRisk.describeLoginFailure(body);
|
||||
assertTrue(message.startsWith(PasswordLoginRisk.AUTHORIZATION_HINT));
|
||||
assertTrue(message.contains("验证码/风控"));
|
||||
assertTrue(message.contains("auths.ye.authorization"));
|
||||
assertTrue(message.contains("authType=authorization"));
|
||||
assertTrue(message.contains("authType=accesstoken"));
|
||||
assertTrue(message.contains("code=401"));
|
||||
assertTrue(message.contains("当前账号存在安全风险"));
|
||||
assertFalse(message.startsWith("登录失败:"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overseasRiskAndCaptchaPhrasesMatch() {
|
||||
assertRisk("当前账号存在境外登录风险,请使用短信验证码或者微信进行登录。");
|
||||
assertRisk("请完成安全验证");
|
||||
assertRisk("请进行滑块验证");
|
||||
assertRisk("操作频繁,请稍后再试");
|
||||
assertRisk("captcha required");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongPasswordStaysDistinct() {
|
||||
JsonObject body = new JsonObject()
|
||||
.put("code", 500)
|
||||
.put("message", "用户名或密码错误");
|
||||
|
||||
assertFalse(PasswordLoginRisk.indicatesCaptchaOrRisk(body));
|
||||
assertEquals("登录失败: 用户名或密码错误", PasswordLoginRisk.describeLoginFailure(body));
|
||||
|
||||
JsonObject account = new JsonObject().put("code", 1).put("msg", "账号或密码错误");
|
||||
assertFalse(PasswordLoginRisk.indicatesCaptchaOrRisk(account));
|
||||
assertEquals("登录失败: 账号或密码错误", PasswordLoginRisk.describeLoginFailure(account));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void riskFieldWithoutKeywordStillHints() {
|
||||
JsonObject body = new JsonObject()
|
||||
.put("code", 200)
|
||||
.put("message", "ok")
|
||||
.put("data", new JsonObject().put("needCaptcha", true));
|
||||
|
||||
assertFalse(PasswordLoginRisk.isLoginSuccess(body));
|
||||
assertTrue(PasswordLoginRisk.indicatesCaptchaOrRisk(body));
|
||||
assertTrue(PasswordLoginRisk.describeLoginFailure(body).contains("Authorization"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void falseCaptchaFlagDoesNotHint() {
|
||||
JsonObject body = new JsonObject()
|
||||
.put("code", 500)
|
||||
.put("message", "用户名或密码错误")
|
||||
.put("data", new JsonObject().put("needCaptcha", false));
|
||||
|
||||
assertFalse(PasswordLoginRisk.indicatesCaptchaOrRisk(body));
|
||||
assertEquals("登录失败: 用户名或密码错误", PasswordLoginRisk.describeLoginFailure(body));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successTokenIsNotTreatedAsFailure() {
|
||||
JsonObject body = new JsonObject()
|
||||
.put("code", 200)
|
||||
.put("message", "ok")
|
||||
.put("data", new JsonObject().put("token", "jwt-token"));
|
||||
|
||||
assertTrue(PasswordLoginRisk.isLoginSuccess(body));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blankTokenWithoutRiskKeepsMissingTokenMessage() {
|
||||
JsonObject body = new JsonObject()
|
||||
.put("code", 200)
|
||||
.put("message", "ok")
|
||||
.put("data", new JsonObject().put("token", ""));
|
||||
|
||||
assertFalse(PasswordLoginRisk.isLoginSuccess(body));
|
||||
assertEquals("未获取到token", PasswordLoginRisk.describeLoginFailure(body));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonJsonCaptchaBodyUsesHint() {
|
||||
String body = "<html>请完成滑块验证</html>";
|
||||
String message = PasswordLoginRisk.nonJsonLoginFailure(body);
|
||||
assertTrue(message.startsWith(PasswordLoginRisk.AUTHORIZATION_HINT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonJsonWrongPasswordStaysFormatError() {
|
||||
assertEquals("登录响应格式异常: 用户名或密码错误",
|
||||
PasswordLoginRisk.nonJsonLoginFailure("用户名或密码错误"));
|
||||
}
|
||||
|
||||
private static void assertRisk(String message) {
|
||||
JsonObject body = new JsonObject().put("code", 5202).put("message", message);
|
||||
assertTrue(message, PasswordLoginRisk.indicatesCaptchaOrRisk(body));
|
||||
assertTrue(PasswordLoginRisk.describeLoginFailure(body).startsWith(PasswordLoginRisk.AUTHORIZATION_HINT));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user