Closed Cdm2883 closed 6 months ago
同样的问题,在使用验证码登录那里,返回-3 API校验密匙错误
同样的问题,在使用验证码登录那里,返回-3 API校验密匙错误
你的问题可能与本问题不相关. -3 API校验密匙错误
这个报错应该是因为你的签名过程错误, 或者你没有进行签名
我的签名应该是没有问题的,使用相同的appkey
和appsec
,相同的算法计算sign
,其他接口都没有问题,只有这个接口返回-3 API校验密匙错误
,因此我怀疑是否为这个问题
我的签名应该是没有问题的,使用相同的
appkey
和appsec
,相同的算法计算sign
,其他接口都没有问题,只有这个接口返回-3 API校验密匙错误
,因此我怀疑是否为这个问题
可能是你所使用的接口只有特定客户端可使用, 需使用特定客户端的appkey
及appsec
. 经检查我的签名过程没有错误可以正常进行签名, 刚才已尝试使用其他客户端的appkey
和appsec
进行登录操作, 仍然会报错-105 验证码错误
发现原因了嘛?emm……遇到一毛一样的问题哩。
发现问题了,极验的手动登录验证太老旧了,换成新版本,把challenge参数更新一下。
发现问题了,极验的手动登录验证太老旧了,换成新版本,把 challenge 参数更新一下。
大佬,请问是 手动验证器 太老旧了吗?我使用 极验官方的 Android SDK也遇到这个问题,拿到 validate
后登录,仍然返回 -105 验证码错误
极验验证成功后会返回三个值: geetest_challenge; geetest_validate; geetest_seccode;
而手动验证器只返回了geetest_validate,geetest_seccode倆值,少个geetest_challenge(这个challenge和最开始的不一样),所以传给哔哩哔哩challenge值也就不对了。
手动验证器 的代码改一下就行,在 js代码的
validate.value = result.geetest_validate;
seccode.value = result.geetest_seccode;
后面加个
challenge.value = result.geetest_challenge;
把这个值也获取一下就行。
极验验证成功后会返回三个值: geetest_challenge; geetest_validate; geetest_seccode; 而手动验证器只返回了 geetest_validate,geetest_seccode 倆值,少个 geetest_challenge(这个 challenge 和最开始的不一样),所以传给哔哩哔哩 challenge 值也就不对了。
可以了,感谢大佬!
手动验证器 的代码改一下就行,在 js代码的
validate.value = result.geetest_validate; seccode.value = result.geetest_seccode;
后面加个
challenge.value = result.geetest_challenge;
把这个值也获取一下就行。
请问在哪里更改来获取这个值呀
手动验证器 的代码改一下就行,在 js代码的
validate.value = result.geetest_validate; seccode.value = result.geetest_seccode;
后面加个
challenge.value = result.geetest_challenge;
把这个值也获取一下就行。请问在哪里更改来获取这个值呀
在手动验证器的js里找类似于这种的代码:
const result = captchaObj.getValidate();
从这个 result 变量里面获取到返回的新的 challenge 值,challenge.value = result.geetest_challenge
这个新的 challenge 同 validate, seccode值一同当做人机验证器的返回值。
若是失败了,或许可以考虑从人机验证器官网文档处下载新的 gt.js (http://docs.geetest.com/sensebot/deploy/client/web) 文件。记得不是太清楚了。
手动验证器 的代码改一下就行,在 js代码的
validate.value = result.geetest_validate; seccode.value = result.geetest_seccode;
后面加个
challenge.value = result.geetest_challenge;
把这个值也获取一下就行。请问在哪里更改来获取这个值呀
在手动验证器的js里找类似于这种的代码:
const result = captchaObj.getValidate();
从这个 result 变量里面获取到返回的新的 challenge 值,
challenge.value = result.geetest_challenge
这个新的 challenge 同 validate, seccode值一同当做人机验证器的返回值。
若是失败了,或许可以考虑从人机验证器官网文档处下载新的 gt.js (http://docs.geetest.com/sensebot/deploy/client/web) 文件。记得不是太清楚了。
加了challenge.value = result.geetest_challenge;gt.js下载的最新的。 但是返回的challenge没有变化是为什么呢
手动验证器 的代码改一下就行,在 js代码的
validate.value = result.geetest_validate; seccode.value = result.geetest_seccode;
后面加个
challenge.value = result.geetest_challenge;
把这个值也获取一下就行。请问在哪里更改来获取这个值呀
在手动验证器的js里找类似于这种的代码:
const result = captchaObj.getValidate();
从这个 result 变量里面获取到返回的新的 challenge 值,challenge.value = result.geetest_challenge
这个新的 challenge 同 validate, seccode值一同当做人机验证器的返回值。 若是失败了,或许可以考虑从人机验证器官网文档处下载新的 gt.js (http://docs.geetest.com/sensebot/deploy/client/web) 文件。记得不是太清楚了。加了challenge.value = result.geetest_challenge;gt.js下载的最新的。 但是返回的challenge没有变化是为什么呢
之前发现要加这个值好像就是因为他有时候不一样,不清楚这个值具体含义,反正加上后就没出过什么问题。
我该如何正确从服务端获取登录信息?
我的登录过程
POST x-www-form-urlencoded
https://passport.bilibili.com/x/passport-login/oauth2/login
携带ts
(当前10位时间戳),username
(用户账户)及password
(密文密码)最后签名. 这一步服务端返回的内容结构如下:通过
json_root.data.url
的链接截取Geetest (极验)验证码的recaptcha_token
,gee_gt
及gee_challenge
gee_validate
https://passport.bilibili.com/x/passport-login/oauth2/login
但是附加携带gee_challenge
,gee_seccode
,gee_validate
,recaptcha_token
最后签名.在抓包中, 第五步哔哩哔哩漫画安卓客户端已经正确从服务端获取登录信息, 但是在我的实现过程中服务端仍然返回如第3步的内容即验证码错误.
签名过程
appkey
通过抓包获取.appsec
通过反编译获取, 位于com.bilibili.comic.push.PushHelper$1
d
函数处.经验证, 可以正常使用
Geetest (极验)验证码的
gee_validate
获取方式json_root.data.url
处链接, 抓包ajax.php
获取我的Java代码实现
代码经过处理和脱敏以便可以只用一个文件就能运行
需要Java 14 (因为使用了instanceof的模式, 如需再低版本Java运行可以将第241行取消注释再删除模式), 需要导入库:
代码过长, 请展开查看
```java package vip.cdms; import cn.hutool.core.codec.Base64; import com.alibaba.fastjson.JSONObject; import okhttp3.*; import okio.Buffer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.crypto.Cipher; import java.io.IOException; import java.math.BigInteger; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import java.util.TreeMap; public class BiliMangaLoginTest { public static final String appKey = "cc8617fd6961e070"; public static final String appSec = "3131924b941aac971e45189f265262be"; // 别问我怎么搞到的😋 public static class BiliAPIError extends Exception { private final int code; // private final String codeString; private final String message; public BiliAPIError(int code, String message) { this.code = code; // codeString = String.valueOf(code); this.message = message; } public BiliAPIError(String code, String message) { this.code = -1; // codeString = code; this.message = message; } public int getCode() { return code; } // public String getCodeString() { // return codeString; // } @Nullable @Override public String getMessage() { return message; } @NotNull @Override public String toString() { return this.getClass().getName() + ": code " + code + ", message " + message; } } public static class API { public interface JsonDataCallback {
void onFailure(Exception e, JSONObject json_root);
void onResponse(T json_root_data);
}
public interface JsonDataCallbackAutoE {
void onResponse(T json_root_data);
}
public static JsonDataCallback getJsonDataCallbackAutoE(
JsonDataCallback callback,
JsonDataCallbackAutoE jsonDataCallbackAutoE
) {
return new JsonDataCallback<>() {
@Override
public void onFailure(Exception e, JSONObject json_root) {
callback.onFailure(e, json_root);
}
@Override
public void onResponse(T json_root_data) {
jsonDataCallbackAutoE.onResponse(json_root_data);
}
};
}
public static class OkhttpJsonDataCallback implements Callback {
private final JsonDataCallback callback;
public OkhttpJsonDataCallback(JsonDataCallback callback) {
this.callback = callback;
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
callback.onFailure(e, null);
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) {
String result;
try {
assert response.body() != null;
result = response.body().string();
} catch (IOException e) {
callback.onFailure(e, null);
return;
}
try {
JSONObject json_root = JSONObject.parseObject(result);
Object code_ = json_root.get("code");
if (code_ instanceof Number) {
int code = (Integer) code_;
String msg = json_root.getString("msg");
if (msg == null) msg = json_root.getString("message");
if (code != 0) throw new BiliAPIError(code, msg/* + response.request().url().url().toString()*/);
} else if (code_ instanceof String) {
String code = (String) code_;
String msg = json_root.getString("msg");
if (msg == null) msg = json_root.getString("message");
throw new BiliAPIError(code, msg);
}
callback.onResponse((T) json_root.get("data"));
} catch (Exception e) {
JSONObject json_root = null;
try {
json_root = JSONObject.parseObject(result);
} catch (Exception ignored) {}
callback.onFailure(e, json_root);
}
}
}
}
public static class Geetest {
public String verify_url;
public String recaptcha_token;
public String gee_gt;
public String gee_challenge;
public String hash;
public String gee_validate;
@Override
public String toString() {
return super.toString() + "\n"
+ " | verify_url --> " + verify_url + "\n"
+ " | recaptcha_token --> " + recaptcha_token + "\n"
+ " | gee_gt --> " + gee_gt + "\n"
+ " | gee_challenge --> " + gee_challenge + "\n"
+ " | hash --> " + hash + "\n"
+ " | gee_validate --> " + gee_validate;
}
}
public interface LoginCallback extends API.JsonDataCallback {
void onFailure(Exception e, JSONObject json_root);
void onCaptcha(Geetest geetest);
void onResponse(JSONObject json_root_data);
}
public static void login(
String username,
String password,
@Nullable Geetest geetest,
LoginCallback callback
) {
OkHttpClient client = new OkHttpClient.Builder().build();
Request keyRequest = new Request.Builder()
.url(
HttpUrl.get("https://passport.bilibili.com/x/passport-login/web/key")
.newBuilder()
.addQueryParameter("appkey", appKey)
.build()
)
.build();
client.newCall(keyRequest).enqueue(new API.OkhttpJsonDataCallback<>(API.getJsonDataCallbackAutoE(callback, key_root_data -> {
String hash = key_root_data.getString("hash");
String key = key_root_data.getString("key");
String encodedPassword;
try {
encodedPassword = encodePassword(hash, key, password);
} catch (Exception e) {
throw new RuntimeException(e);
}
FormBody.Builder formBodyBuilder = new FormBody.Builder();
sign(new HashMap<>(){{
if (geetest != null) {
put("gee_challenge", geetest.gee_challenge);
put("gee_seccode", geetest.gee_validate + "|jordan");
put("gee_validate", geetest.gee_validate);
put("recaptcha_token", geetest.recaptcha_token);
}
put("ts", String.valueOf(System.currentTimeMillis() / 1000));
put("username", username);
put("password", encodedPassword);
// put("bili_local_id", "b50481149e09bbd4cead72e7346c576920230428174741576eeca49a783a7fe6");
// put("build", "36505100");
// put("buvid", "XY9D69892101BD675DEFC72F94764180A7746");
// put("c_locale", "");
// put("channel", "dedaobook");
// put("device", "phone");
//
// put("device_id", "【...】");
// put("device_meta", "【...】");
// put("device_name", "【...】");
// put("device_platform", "【...】");
// put("device_tourist_id", "");
// put("dt", "【...】");
//
// put("extend", "");
// put("from_pv", "");
// put("from_url", "");
// put("is_teenager", "0");
// put("local_id", "XY9D69892101BD675DEFC72F94764180A7746");
// put("login_session_id", "");
// put("mobi_app", "android_comic");
// put("no_recommend", "0");
// put("platform", "android");
// put("s_locale", "");
// put("spm_id", "");
}}, formBodyBuilder, appKey, appSec);
Request postRequest = new Request.Builder()
.url("https://passport.bilibili.com/x/passport-login/oauth2/login")
// .addHeader("buvid", "XY9D69892101BD675DEFC72F94764180A7746")
// .addHeader("user-agent", "Mozilla/5.0 BiliComic/5.5.1 os/android model/【...】 mobi_app/android_comic build/36505100 channel/dedaobook innerVer/36505100 osVer/10 network/2")
// .addHeader("x-bili-trace-id", "【...】")
.post(formBodyBuilder.build())
.build();
client.newCall(postRequest).enqueue(new API.OkhttpJsonDataCallback<>(new API.JsonDataCallback() {
@Override
public void onFailure(Exception e, JSONObject json_root) {
e.printStackTrace();
if (!(e instanceof BiliAPIError biliAPIError)) {
callback.onFailure(e, json_root);
return;
}
// BiliAPIError biliAPIError = (BiliAPIError) e;
if (biliAPIError.getCode() == -105) {
Geetest geetest = new Geetest();
String captchaUrl = json_root.getJSONObject("data").getString("url");
geetest.verify_url = captchaUrl;
String[] queries = captchaUrl.replace("https://www.bilibili.com/h5/project-msg-auth/verify?", "").split("&");
for (String query : queries) {
String[] kv = query.split("=");
String key = kv[0];
String value = kv[1];
switch (key) {
case "recaptcha_token":
geetest.recaptcha_token = value;
break;
case "gee_gt":
geetest.gee_gt = value;
break;
case "gee_challenge":
geetest.gee_challenge = value;
case "hash":
geetest.hash = value;
}
}
callback.onCaptcha(geetest);
} else {
callback.onFailure(biliAPIError, json_root);
}
}
@Override
public void onResponse(JSONObject json_root_data) {
callback.onResponse(json_root_data);
}
}));
})));
}
private static String encodePassword(String salt, String key, String password) throws Exception {
String[] keySplit = key.strip().split("\n");
String keyContent = keySplit[1] + keySplit[2] + keySplit[3] + keySplit[4];
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(keyContent));
PublicKey publicKey = keyFactory.generatePublic(keySpec);
// Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.PUBLIC_KEY, publicKey);
byte[] bytes = cipher.doFinal((salt + password).getBytes());
return /*new String(*/Base64.encode(bytes)/*.getBytes(), "ISO_8859_1")*/;
}
public static void sign(Map map, @Nullable FormBody.Builder out, String appKey, String appSec) {
// 按照key重新排序
Map sortedMap = new TreeMap<>(String::compareTo);
sortedMap.put("appkey", appKey);
for (String key : map.keySet()) sortedMap.put(key, map.get(key));
// 构造url query
FormBody.Builder formBuilder = new FormBody.Builder();
for (String key : sortedMap.keySet()) {
String value = sortedMap.get(key);
if (value == null) continue;
formBuilder.add(key, value);
if (out != null) out.add(key, value);
}
String urlQuery;
try (Buffer buffer = new Buffer()) {
formBuilder.build().writeTo(buffer);
urlQuery = buffer.readUtf8();
} catch (IOException e) {
return;
}
// 计算hash
StringBuilder sign = new StringBuilder(urlQuery + appSec);
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(sign.toString().getBytes());
sign = new StringBuilder(new BigInteger(1, md5.digest()).toString(16));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
while (sign.length() != 32) sign.insert(0, "0");
if (out != null) out.add("sign", sign.toString());
}
private static final Scanner scanner = new Scanner(System.in);
private static void login(String username, String password, Geetest geetest, API.JsonDataCallback callback) {
login(username, password, geetest, new LoginCallback() {
@Override
public void onFailure(Exception e, JSONObject json_root) {
callback.onFailure(e, json_root);
}
@Override
public void onCaptcha(Geetest geetest) {
System.out.println();
System.out.println(geetest.toString());
System.out.println("pls input 'gee_validate': ");
geetest.gee_validate = scanner.nextLine();
if (geetest.gee_validate.equals("stop")) System.exit(0);
login(username, password, geetest, callback);
}
@Override
public void onResponse(JSONObject json_root_data) {
callback.onResponse(json_root_data);
}
});
}
public static void main(String[] args) {
System.out.println("pls input 'username': ");
String username = scanner.nextLine();
System.out.println("pls input 'password': ");
String password = scanner.nextLine();
Geetest geetest = new Geetest();
login(username, password, geetest, new API.JsonDataCallback<>() {
@Override
public void onFailure(Exception e, JSONObject json_root) {
e.printStackTrace();
System.exit(0);
}
@Override
public void onResponse(JSONObject json_root_data) {
System.out.println();
System.out.println("onResponse");
System.out.println(json_root_data.toJSONString());
System.exit(0);
}
});
}
}
```