This commit is contained in:
2024-11-08 18:12:02 +08:00
parent 6d00813311
commit 6e2cea8186
32 changed files with 396 additions and 201 deletions

View File

@@ -3,9 +3,11 @@ package com.yutou.biliapi.api;
import com.yutou.biliapi.bean.login.CheckCookieBean;
import com.yutou.biliapi.bean.login.LoginInfoBean;
import com.yutou.biliapi.bean.login.QRCodeGenerateBean;
import com.yutou.common.okhttp.FileBody;
import com.yutou.common.okhttp.HttpBody;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface LoginApi {
@@ -27,5 +29,13 @@ public interface LoginApi {
@GET("x/passport-login/web/cookie/info")
Call<HttpBody<CheckCookieBean>> checkCookie();
@GET("https://www.bilibili.com/correspond/1/{correspondPath}")
Call<FileBody<String>> getCorrespond(@Path("correspondPath") String correspondPath);
@GET("/x/passport-login/web/cookie/refresh")
Call<HttpBody<LoginInfoBean>> refreshCookie(@Query("csrf") String csrf
, @Query("refresh_csrf") String refresh_csrf
, @Query("source") String source
, @Query("refresh_token") String ac_time_value
);
}

View File

@@ -27,6 +27,8 @@ public class LoginCookieDatabaseBean extends AbsDatabasesBean {
String sid;
@JSONField(name = "gourl")
String gourl;
@JSONField(name ="refresh_token")
String refreshToken;
public LoginCookieDatabaseBean() {
super("login_cookie", System.currentTimeMillis());

View File

@@ -54,6 +54,9 @@ public class BiliBiliLoginDatabase extends SQLiteManager {
return null;
}
public List<LoginCookieDatabaseBean> getAllCookies(){
return super.get(cookie.getTableName(), LoginCookieDatabaseBean.class);
}
public LoginUserDatabaseBean getUser(String userId) {
List<LoginUserDatabaseBean> list = getAllUser();

View File

@@ -1,15 +1,22 @@
package com.yutou.biliapi.net;
import com.yutou.biliapi.bean.login.CheckCookieBean;
import com.yutou.common.inter.IHttpApiCheckCallback;
import com.yutou.common.okhttp.HttpCallback;
import com.yutou.biliapi.api.LoginApi;
import com.yutou.biliapi.bean.login.LoginCookieDatabaseBean;
import com.yutou.common.okhttp.FileBody;
import com.yutou.common.utils.Log;
import com.yutou.common.utils.RSAUtils;
import okhttp3.Headers;
import com.yutou.common.utils.WebClient;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import retrofit2.Response;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
@@ -19,8 +26,9 @@ import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class BiliCookieManager {
public static final int COOKIE_INVALID = -101;
public static final int COOKIE_SUCCESS = 0;
public static final int COOKIE_INVALID = -101;
public static final int COOKIE_SUCCESS = 0;
private static final String PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n" +
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLgd2OAkcGVtoE3ThUREbio0Eg\n" +
@@ -29,62 +37,54 @@ public class BiliCookieManager {
"JNrRuoEUXpabUzGB8QIDAQAB\n" +
"-----END PUBLIC KEY-----";
public void checkCookie(IHttpApiCheckCallback<Integer> callback){
BiliLoginNetApiManager.getInstance().getLoginApi(null)
.checkCookie().enqueue(new HttpCallback<CheckCookieBean>() {
@Override
public void onResponse(Headers headers, int code, String status, CheckCookieBean response, String rawResponse) {
if(code==-101){
// TODO cookie失效需要重新登录
callback.onError(COOKIE_INVALID,"cookie失效需要重新登录");
return;
}
if(response.isRefresh()){
refreshCookie();
}
callback.onSuccess(COOKIE_SUCCESS);
}
@Override
public void onFailure(Throwable throwable) {
}
});
}
/**
* <a href="https://socialsisteryi.github.io/bilibili-API-collect/docs/login/cookie_refresh.html#java">文档地址</a>
*/
private void refreshCookie(){
public static String getCorrespondPath(String plaintext) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
String publicKeyStr = PUBLIC_KEY
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replace("\n", "")
.trim();
byte[] publicBytes = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicBytes);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
String algorithm = "RSA/ECB/OAEPPadding";
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// Encode the plaintext to bytes
byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);
// Add OAEP padding to the plaintext bytes
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);
// Encrypt the padded plaintext bytes
byte[] encryptedBytes = cipher.doFinal(plaintextBytes);
// Convert the encrypted bytes to a Base64-encoded string
return new BigInteger(1, encryptedBytes).toString(16);
}
private static String getRefreshCsrf(String htmlContent) {
htmlContent=htmlContent.split("<div id=\"1-name\">")[1].split("</div>")[0];
return htmlContent;
}
public static void resetCookie(LoginCookieDatabaseBean cookie) {
try {
String refreshTime = String.format("refresh_%d", System.currentTimeMillis());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
String publicKeyStr = PUBLIC_KEY
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replace("\n", "")
.trim();
byte[] publicBytes = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicBytes);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
LoginApi api = BiliLoginNetApiManager.getInstance().getLoginApi(cookie.getDedeUserID());
String correspondPath = getCorrespondPath(String.format("refresh_%d", System.currentTimeMillis()));
System.out.println("correspondPath = " + correspondPath);
Response<FileBody<String>> body = api.getCorrespond(correspondPath).execute();
String string = IOUtils.toString(body.body().getInputStream(), StandardCharsets.UTF_8);
String refreshCsrf = getRefreshCsrf(string);
System.out.println("body = " + refreshCsrf);
String algorithm = "RSA/ECB/OAEPPadding";
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// Encode the plaintext to bytes
byte[] plaintextBytes = refreshTime.getBytes(StandardCharsets.UTF_8);
// Add OAEP padding to the plaintext bytes
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);
// Encrypt the padded plaintext bytes
byte[] encryptedBytes = cipher.doFinal(plaintextBytes);
// Convert the encrypted bytes to a Base64-encoded string
String encrypted = new BigInteger(1, encryptedBytes).toString(16);
}catch (Exception e){
Log.e(e);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -106,6 +106,7 @@ public class BiliLoginNetApiManager extends BaseApi {
if (!list.isEmpty()) {
ck.put("gourl", bd);
LoginCookieDatabaseBean cookie = JSONObject.parseObject(ck.toString(), LoginCookieDatabaseBean.class);
cookie.setRefreshToken(httpBody.getData().getRefresh_token());
cancel();
callback.onResponse(headers, LOGIN_SUCCESS, "ok", cookie, ck.toString());

View File

@@ -2,16 +2,37 @@ package com.yutou.bilibili.Controllers;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.biliapi.api.LoginApi;
import com.yutou.biliapi.bean.login.CheckCookieBean;
import com.yutou.biliapi.bean.login.LoginCookieDatabaseBean;
import com.yutou.biliapi.bean.login.LoginUserDatabaseBean;
import com.yutou.biliapi.bean.login.UserInfoBean;
import com.yutou.biliapi.databases.BiliBiliLoginDatabase;
import com.yutou.biliapi.net.BiliCookieManager;
import com.yutou.biliapi.net.BiliLoginNetApiManager;
import com.yutou.biliapi.net.BiliUserNetApiManager;
import com.yutou.bilibili.datas.ResultData;
import com.yutou.bilibili.services.LiveLoginService;
import com.yutou.common.okhttp.HttpBody;
import com.yutou.common.okhttp.HttpCallback;
import com.yutou.common.okhttp.HttpLoggingInterceptor;
import com.yutou.common.utils.Log;
import com.yutou.common.utils.RedisTools;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import okhttp3.Headers;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import retrofit2.Response;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Controller
public class BiliUserController {
@@ -24,11 +45,103 @@ public class BiliUserController {
JSONArray array = new JSONArray();
List<LoginUserDatabaseBean> allUser = loginService.getAllUser();
for (LoginUserDatabaseBean bean : allUser) {
JSONObject json=new JSONObject();
json.put("uid",bean.getUserInfo().getMid());
json.put("uname",bean.getUserInfo().getUname());
array.add(json);
LoginApi api = BiliLoginNetApiManager.getInstance().getLoginApi(bean.getUserInfo().getMid());
JSONObject json = new JSONObject();
json.put("uid", bean.getUserInfo().getMid());
json.put("uname", bean.getUserInfo().getUname());
json.put("face",bean.getUserInfo().getFace());
json.put("sql_time",bean.getSql_time());
try {
CheckCookieBean cookie = api.checkCookie().execute().body().getData();
if (cookie == null) {
json.put("status", "无效");
} else {
json.put("status", cookie.isRefresh()?"待刷新":"正常");
}
} catch (IOException e) {
Log.e(e);
json.put("status", "无效");
}
array.add(json);
}
return ResultData.success(array);
}
@ResponseBody
@RequestMapping("/user/login")
public JSONObject login(HttpServletRequest request) {
HttpSession session = request.getSession();
Object loginToken = session.getAttribute("loginToken");
if (loginToken == null) {
loginToken = UUID.randomUUID().toString();
session.setAttribute("loginToken", loginToken);
}
return ResultData.success(login(loginToken.toString()));
}
private JSONObject login(String loginToken) {
String ret = RedisTools.get(loginToken);
if (StringUtils.hasText(ret)) {
JSONObject json = JSONObject.parseObject(ret);
if (json.getIntValue("code") == BiliLoginNetApiManager.LOGIN_SUCCESS) {
RedisTools.remove(loginToken);
}
return json;
}
BiliLoginNetApiManager.getInstance().login(new HttpCallback<LoginCookieDatabaseBean>() {
@Override
public void onResponse(Headers headers, int code, String status, LoginCookieDatabaseBean response, String rawResponse) {
JSONObject json = new JSONObject();
json.put("code", code);
if (code == BiliLoginNetApiManager.LOGIN_QRCODE) {
json.put("qrcode", rawResponse);
} else if (code == BiliLoginNetApiManager.LOGIN_SUCCESS) {
Response<HttpBody<UserInfoBean>> execute = null;
try {
execute = BiliUserNetApiManager.getInstance().getUserApi(response).getUserInfo().execute();
if (execute.isSuccessful()) {
if (execute.body() != null) {
UserInfoBean data = execute.body().getData();
LoginUserDatabaseBean userBean = new LoginUserDatabaseBean(data);
BiliBiliLoginDatabase.getInstance().initData(response, userBean).close();
json.put("user", data);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
RedisTools.set(loginToken, json.toString(), 60 * 60);
}
@Override
public void onFailure(Throwable throwable) {
}
});
return null;
}
public static void main(String[] args) {
LiveLoginService loginService = new LiveLoginService();
/* List<LoginUserDatabaseBean> allUser = loginService.getAllUser();
for (LoginUserDatabaseBean user : allUser) {
System.out.println(user.getUserInfo().getMid());
LoginApi api = BiliLoginNetApiManager.getInstance().getLoginApi(user.getUserInfo().getMid());
try {
CheckCookieBean cookie = api.checkCookie().execute().body().getData();
System.out.println("cookie = " + cookie);
if(cookie.isRefresh()){
BiliCookieManager.resetCookie(loginService.getCookie(user.getUserInfo().getMid()));
}
} catch (IOException e) {
Log.e(e);
}finally {
return;
}
}*/
HttpLoggingInterceptor.setLog(true);
BiliCookieManager.resetCookie(loginService.getCookie("96300"));
}
}

View File

@@ -1,6 +1,7 @@
package com.yutou.bilibili.services;
import com.yutou.biliapi.api.LoginApi;
import com.yutou.biliapi.bean.login.LoginCookieDatabaseBean;
import com.yutou.biliapi.bean.login.LoginInfoBean;
import com.yutou.biliapi.bean.login.LoginUserDatabaseBean;
import com.yutou.biliapi.bean.login.QRCodeGenerateBean;
@@ -56,4 +57,10 @@ public class LiveLoginService {
return loginDatabase.getAllUser();
}
public List<LoginCookieDatabaseBean> getAllUserCookie(){
return loginDatabase.getAllCookies();
}
public LoginCookieDatabaseBean getCookie(String userId) {
return loginDatabase.getCookie(userId);
}
}

View File

@@ -60,7 +60,7 @@ public class Log {
StringBuilder sb = new StringBuilder();
for (Object obj : log) {
if (!sb.isEmpty()) {
sb.append("\n");
sb.append("|");
}
sb.append(obj);
}

View File

@@ -0,0 +1,116 @@
package com.yutou.common.utils;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.CapabilityType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class WebClient {
private WebDriver driver;
private static WebClient instance;
public static WebClient getInstance() {
if (instance == null || instance.driver == null) {
instance = new WebClient();
}
return instance;
}
public WebDriver getWebDriver() {
if (driver == null) {
driver = new ChromeDriver(getOptions());
}
return driver;
}
public void quit() {
driver.quit();
driver = null;
}
private WebClient() {
//System.setProperty("webdriver.http.factory", "jdk-http-client");
System.setProperty("webdriver.chrome.driver",
ConfigTools.load(ConfigTools.CONFIG, "chromedriver", String.class));
// System.setProperty("webdriver.chrome.whitelistedIps", "");
// java.util.logging.Logger.getLogger("org.openqa.selenium").setLevel(Level.OFF);
}
public static List<Cookie> loadCookie(JSONArray array) {
List<Cookie> list = new ArrayList<>();
for (Object o : array) {
JSONObject json = (JSONObject) o;
boolean containsDate = json.containsKey("expirationDate");
long t = 0;
if (containsDate) {
String _time = json.getString("expirationDate");
if (_time.contains(".")) {
_time = _time.split("\\.")[0];
}
t = Long.parseLong(_time);
} else {
t = (System.currentTimeMillis()) / 1000;
}
t *= 1000;
Cookie cookie = new Cookie(json.getString("name"),
json.getString("value"),
json.getString("domain"),
json.getString("path"),
new Date(t),
json.getBooleanValue("secure"),
json.getBooleanValue("httpOnly")
);
list.add(cookie);
}
return list;
}
static boolean headless = false;
public void setHeadless(boolean headless) {
WebClient.headless = headless;
}
public ChromeOptions getOptions() {
ChromeOptions options = new ChromeOptions();
options.addArguments("--remote-allow-origins=*");
// options.addArguments("--disable-gpu");
// options.addArguments("blink-settings=imagesEnabled=false");
String headless = RedisTools.get("chromedrive_headless");
String proxy = RedisTools.get("chromedrive_proxy");
if ("true".equals(proxy)) {
String url = "http://127.0.0.1:7890";
Proxy _proxy = new Proxy();
_proxy.setHttpProxy(url);
_proxy.setSslProxy(url);
options.setCapability(CapabilityType.PROXY, _proxy);
}
if ("true".equals(headless) || WebClient.headless) {
options.addArguments("--headless");
}
options.addArguments("--no-sandbox");
// options.addArguments("--incognito");
options.addArguments("--disable-plugins");
options.addArguments("--lang=zh-CN");
return options;
}
public static void main(String[] args) {
WebDriver driver1 = getInstance().getWebDriver();
driver1.get("https://www.tsdm39.net/forum.php");
}
}

View File

@@ -1,3 +1,3 @@
server.port=8880
server.port=8080
logging.file.path=./logs
logging.level.com.log.controller = trace