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

View File

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<title>基础菜单 - Layui</title>
<title>用户中心</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
@ -12,143 +12,41 @@
<body class="layui-bg-gray">
<div id="header"></div>
<div class="layui-panel" style="width: 260px; margin: 16px;">
<ul class="layui-menu" id="demo-menu">
<li lay-options="{id: 100}">
<div class="layui-menu-body-title"><a href="javascript:;">menu item 1</a></div>
</li>
<li lay-options="{id: 101}">
<div class="layui-menu-body-title">
<a href="javascript:;">menu item 2 <span class="layui-badge-dot"></span></a>
</div>
</li>
<li class="layui-menu-item-divider"></li>
<li class="layui-menu-item-group layui-menu-item-down" lay-options="{type: 'group'}">
<div class="layui-menu-body-title">
menu group <i class="layui-icon layui-icon-up"></i>
</div>
<ul>
<li lay-options="{id: 103}">
<div class="layui-menu-body-title">menu item 3-1</div>
</li>
<li class="layui-menu-item-group" lay-options="{type: 'group', isAllowSpread: false}">
<div class="layui-menu-body-title">menu group 2</div>
<ul>
<li class="layui-menu-item-checked">
<div class="layui-menu-body-title">menu item 3-2-1</div>
</li>
<li>
<div class="layui-menu-body-title">menu item 3-2-2</div>
</li>
</ul>
</li>
<li>
<div class="layui-menu-body-title">menu item 3-3</div>
</li>
</ul>
</li>
<li class="layui-menu-item-divider"></li>
<li>
<div class="layui-menu-body-title">menu item 4 <span class="layui-badge">1</span></div>
</li>
<li>
<div class="layui-menu-body-title">menu item 5</div>
</li>
<li>
<div class="layui-menu-body-title">menu item 6</div>
</li>
<li class="layui-menu-item-parent" lay-options="{type: 'parent'}">
<div class="layui-menu-body-title">
menu item 7 Children
<i class="layui-icon layui-icon-right"></i>
</div>
<div class="layui-panel layui-menu-body-panel">
<ul>
<li class="layui-menu-item-parent" lay-options="{type: 'parent'}">
<div class="layui-menu-body-title">
menu item 7-1
<i class="layui-icon layui-icon-right"></i>
</div>
<div class="layui-panel layui-menu-body-panel">
<ul>
<li>
<div class="layui-menu-body-title">menu item 7-2-1</div>
</li>
<li>
<div class="layui-menu-body-title">menu item 7-2-2</div>
</li>
<li>
<div class="layui-menu-body-title">menu item 7-2-3</div>
</li>
<li>
<div class="layui-menu-body-title">menu item 7-2-4</div>
</li>
</ul>
</div>
</li>
<li>
<div class="layui-menu-body-title">menu item 7-2</div>
</li>
<li>
<div class="layui-menu-body-title">menu item 7-3</div>
</li>
</ul>
</div>
</li>
<li>menu item 8</li>
<li class="layui-menu-item-divider"></li>
<li class="layui-menu-item-group" lay-options="{type: 'group', isAllowSpread: false}">
<div class="layui-menu-body-title">menu group 9</div>
<ul>
<li>
<div class="layui-menu-body-title">menu item 9-1</div>
</li>
<li class="layui-menu-item-parent" lay-options="{type: 'parent'}">
<div class="layui-menu-body-title">
menu item 9-2
<i class="layui-icon layui-icon-right"></i>
</div>
<div class="layui-panel layui-menu-body-panel">
<ul>
<li>
<div class="layui-menu-body-title">menu item 9-2-1</div>
</li>
<li>
<div class="layui-menu-body-title">menu item 9-2-2</div>
</li>
<li>
<div class="layui-menu-body-title">menu item 9-2-3</div>
</li>
</ul>
</div>
</li>
<li>
<div class="layui-menu-body-title">menu item 9-31</div>
</li>
</ul>
</li>
<li class="layui-menu-item-divider"></li>
<li>
<div class="layui-menu-body-title">menu item 10</div>
</li>
</ul>
</div>
<table class="layui-hide" id="roomList" lay-filter="roomTable"></table>
<script src="/js/jquery-3.2.1.js"></script>
<script src="/js/CommonConfig.js"></script>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" onclick="createRoom()">添加新账号</button>
</div>
</script>
<script type="text/html" id="toolDemo">
<div class="layui-clear-space">
<a class="layui-btn layui-btn-xs" lay-event="edit">删除</a>
</div>
</script>
<script>
headerModel = 4;
$('#header').load("/html/header.html");
function createUser() {
layer.open({
type: 2,
title: "添加新房间",
area: ['600px', '500px'],
content: '/html/ui/createConfig.html?roomId='
});
}
function refreshCookie(data){
console.log(data)
}
</script>
<script src="/layui/layui.js"></script>
<script src="/layui/layui.js"></script>
<script src="/js/jquery-3.2.1.js"></script>
<script src="/js/CommonConfig.js"></script>
<script src="/js/httpUtils.js"></script>
<script>
@ -156,14 +54,46 @@
layui.use(function () {
var dropdown = layui.dropdown;
var layer = layui.layer;
var util = layui.util;
// 菜单点击事件
dropdown.on('click(demo-menu)', function (options) {
console.log(this, options);
var table = layui.table;
// 显示 - 仅用于演示
layer.msg(util.escape(JSON.stringify(options)));
table.render({
elem: '#roomList',
url: '/user/list',
toolbar: '#toolbarDemo',
totalRow: true, // 开启合计行
page: true,
response: {
statusCode: 100 // 重新规定成功的状态码为 200table 组件默认为 0
},
parseData: function (res) {
return {
"code": res.status, //解析接口状态
"msg": res.message, //解析提示文本
"data": res.data, //解析数据列表
"count": res.count
};
},
cols: [[
{ type: 'checkbox', fixed: 'left' },
{ field: 'uid', title: 'UID', width: 150, sort: true, fixed: 'left', templet: '<div><a href="https://space.bilibili.com/{{= d.uid}}" target="_blank">{{= d.uid}}</a>' },
{ field: 'uname', title: '用户名', width: 150, fixed: 'left' },
{ field: 'face', title: '头像', width: 80, templet: '<div><image src="" onerror="showImage(\'{{= d.face }}\',this);" style="width: 30px;height: 30px;"></div>' },
{ field: 'status', title: '状态', width: 150,templet:'<div> {{= d.status}} <button class="layui-btn layui-btn-xs" onclick="refreshCookie(\'{{= d.uid }}\')">刷新cookie</button>'},
{ field: 'sql_time', title: '添加时间', width: 190 },
{ fixed: "right", title: "操作", width: 80, align: "center", toolbar: "#toolDemo" }
]],
done: function () {
},
error: function (res, msg) {
console.log(res, msg)
}
});
});
</script>

8
Web/js/chimee.browser.min.js vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -139,6 +139,11 @@
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.26.0</version>
</dependency>
</dependencies>
<build>

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;
@ -22,6 +29,7 @@ public class BiliCookieManager {
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" +
"Uc/prcajMKXvkCKFCWhJYJcLkcM2DKKcSeFpD/j6Boy538YXnR6VhcuUJOhH2x71\n" +
@ -29,35 +37,11 @@ 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(){
try {
String refreshTime = String.format("refresh_%d", System.currentTimeMillis());
public static String getCorrespondPath(String plaintext) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
String publicKeyStr = PUBLIC_KEY
.replace("-----BEGIN PUBLIC KEY-----", "")
@ -73,7 +57,7 @@ public class BiliCookieManager {
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// Encode the plaintext to bytes
byte[] plaintextBytes = refreshTime.getBytes(StandardCharsets.UTF_8);
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);
@ -81,10 +65,26 @@ public class BiliCookieManager {
// 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);
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 {
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);
} 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());
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