token获取

This commit is contained in:
xuelijun
2026-01-21 17:37:41 +08:00
parent 6f1c990d51
commit 01dd1d5191
6 changed files with 573 additions and 476 deletions

View File

@@ -14,7 +14,7 @@ public class LoginCrawler {
private final LoginService loginService;
// 构造函数注
//
public LoginCrawler(LoginService loginService) {
this.loginService = loginService;
}
@@ -24,6 +24,7 @@ public class LoginCrawler {
return ResponseEntity.ok(result);
}
//今日已结爬取
@GetMapping("/ocr/completedToday")
public ResponseEntity<String> completedToday() throws IOException, TesseractException {
String result = loginService.completedToday();

View File

@@ -0,0 +1,47 @@
package com.tem.bocai.entity;
import jakarta.persistence.*;
import lombok.Data;
import net.sourceforge.tess4j.TesseractException;
import org.springframework.http.ResponseEntity;
import java.io.IOException;
import java.util.Date;
import java.util.List;
@Entity //用户登入信息
@Table(name = "login_info")
@Data
public class LoginInfoResult {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_name", nullable = false)
private String username; //
@Column(name = "password", nullable = false)
private String password; //
@Column(name = "login_url", nullable = false)
private String loginUrl;
//限制赢多少
@Column(name = "win_num", nullable = false)
private Integer winNum;
//限制输多少
@Column(name = "lose_num", nullable = false)
private Integer loseNum;
/* @Column(name = "current_num", nullable = false)
private Integer currentNum;*/
@Column(name = "create_time", nullable = false)
private Date createTime;
@Column(name = "update_time", nullable = false)
private Date updateTime;
}

View File

@@ -5,9 +5,6 @@ public interface LoginService {
String loginAutomatic(String username, String password,String loginUrl,Integer winNum,Integer loseNum);
//获取token
String getToken(String username, String password, String loginUrl);
//获取token
String completedToday();

View File

@@ -1,10 +1,8 @@
package com.tem.bocai.service.impl;
import com.tem.bocai.entity.LoginInfoResult;
import com.tem.bocai.service.LoginService;
import com.tem.bocai.util.CompletedTodayCrawler;
import com.tem.bocai.util.LotteryDataPipeline;
import com.tem.bocai.util.LotteryWebMagicCrawler;
import com.tem.bocai.util.TokenCacheService;
import com.tem.bocai.util.*;
import org.springframework.stereotype.Service;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
@@ -24,6 +22,7 @@ import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -37,7 +36,7 @@ import us.codecraft.webmagic.Spider;
@Service
public class LoginServiceImpl implements LoginService {
private static final String BASE_URL = "https://4701268539-esh.qdk63ayw8g.com";
private static final int MAX_RETRY = 5;
private static final int MAX_RETRY = 10;
@Autowired
private Tesseract tesseract;
@Autowired
@@ -49,9 +48,11 @@ public class LoginServiceImpl implements LoginService {
for (int attempt = 1; attempt <= MAX_RETRY; attempt++) {
System.out.println("\n=== 第 " + attempt + " 次尝试 ===");
try {
token = attemptLogin();
token = tokenCacheService.attemptLogin();
tokenCacheService.saveToken(token);
if (token != null && !token.isEmpty()) {
//保存用户信息
addLoginInfo(username, password, loginUrl, winNum, loseNum);
// 2. 创建爬虫实例传入token
LotteryWebMagicCrawler crawler = new LotteryWebMagicCrawler(token);
@@ -75,30 +76,7 @@ public class LoginServiceImpl implements LoginService {
}
if (attempt < MAX_RETRY) {
waitForRetry(attempt);
}
} catch (Exception e) {
System.err.println("" + attempt + " 次尝试失败: " + e.getMessage());
e.printStackTrace();
}
}
return "";
}
@Override
public String getToken(String username, String password, String loginUrl) {
String token = "";
for (int attempt = 1; attempt <= MAX_RETRY; attempt++) {
System.out.println("\n=== 第 " + attempt + " 次尝试 ===");
try {
token = attemptLogin();
if (token != null && !token.isEmpty()) {
return token;
}
if (attempt < MAX_RETRY) {
waitForRetry(attempt);
tokenCacheService.waitForRetry(attempt);
}
} catch (Exception e) {
System.err.println("" + attempt + " 次尝试失败: " + e.getMessage());
@@ -127,309 +105,23 @@ public class LoginServiceImpl implements LoginService {
return "";
}
/**
* 单次登录尝试
*/
private String attemptLogin() throws IOException, TesseractException, InterruptedException {
CookieStore cookieStore = new BasicCookieStore();
try (CloseableHttpClient httpClient = createHttpClient(cookieStore)) {
// 1. 获取验证码
byte[] imageData = fetchCaptcha(httpClient);
if (imageData == null) {
return null;
}
// 2. OCR识别验证码
String code = processCaptcha(imageData);
if (code == null || code.length() != 4) {
return null;
}
// 3. 执行登录
return performLogin(httpClient, cookieStore, code);
} catch (Exception e) {
throw new IOException("登录尝试失败", e);
}
}
/**
* 创建HttpClient
* 添加登录信息
*/
private CloseableHttpClient createHttpClient(CookieStore cookieStore) {
return HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.build();
public boolean addLoginInfo(String username, String password,
String loginUrl, Integer winNum, Integer loseNum) {
LoginInfoResult loginInfo = new LoginInfoResult();
loginInfo.setUsername(username);
loginInfo.setPassword(password);
loginInfo.setLoginUrl(loginUrl);
loginInfo.setWinNum(winNum != null ? winNum : 0);
loginInfo.setLoseNum(loseNum != null ? loseNum : 0);
loginInfo.setCreateTime(new Date());
loginInfo.setUpdateTime(new Date());
return SQLiteUtil.addOrUpdateLoginInfo(loginInfo);
}
/**
* 获取验证码图片
*/
private byte[] fetchCaptcha(CloseableHttpClient httpClient)
throws IOException, InterruptedException {
System.out.println("获取验证码...");
// 添加随机延迟
Thread.sleep(1000 + (long) (Math.random() * 1000));
HttpGet getCaptcha = new HttpGet(BASE_URL + "/code");
setCommonHeaders(getCaptcha);
getCaptcha.setHeader("Referer", BASE_URL + "/login");
try (CloseableHttpResponse captchaResponse = httpClient.execute(getCaptcha)) {
int captchaStatus = captchaResponse.getStatusLine().getStatusCode();
System.out.println("验证码响应状态码: " + captchaStatus);
if (captchaStatus == 200) {
return EntityUtils.toByteArray(captchaResponse.getEntity());
} else if (captchaStatus == 429) {
System.out.println("获取验证码被限速,等待后重试...");
Thread.sleep(3000);
} else {
System.out.println("获取验证码失败: " + captchaStatus);
}
}
return null;
}
/**
* 处理验证码识别
*/
private String processCaptcha(byte[] imageData)
throws IOException, TesseractException {
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData));
String rawOcr = tesseract.doOCR(image);
// 清理验证码
String code = rawOcr.replaceAll("\\s+", "").trim();
code = code.replaceAll("[^0-9]", ""); // 只保留数字
System.out.println("OCR原始结果: " + rawOcr);
System.out.println("清理后验证码: [" + code + "] 长度: " + code.length());
// 保存图片用于调试
//saveCaptchaImage(image);
return code;
}
/**
* 保存验证码图片
*/
/* private void saveCaptchaImage(BufferedImage image) throws IOException {
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File output = new File("captcha_" + timestamp + ".png");
ImageIO.write(image, "png", output);
System.out.println("验证码图片已保存到: " + output.getAbsolutePath());
}*/
/**
* 执行登录请求
*/
private String performLogin(CloseableHttpClient httpClient,
CookieStore cookieStore,
String code) throws IOException, InterruptedException {
System.out.println("执行登录...");
// 等待一下再发送登录请求
Thread.sleep(1500 + (long) (Math.random() * 1000));
HttpPost loginPost = createLoginRequest(code);
try (CloseableHttpResponse loginResponse = httpClient.execute(loginPost)) {
return processLoginResponse(loginResponse, cookieStore);
}
}
/**
* 创建登录请求
*/
private HttpPost createLoginRequest(String code) throws UnsupportedEncodingException {
HttpPost loginPost = new HttpPost(BASE_URL + "/login");
// 设置请求头
setCommonHeaders(loginPost);
loginPost.setHeader("Referer", BASE_URL + "/login");
loginPost.setHeader("Origin", BASE_URL);
loginPost.setHeader("Accept", "application/json, text/plain, */*");
// 构建登录参数
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("type", "1"));
params.add(new BasicNameValuePair("account", "pmk1"));
params.add(new BasicNameValuePair("password", "Asd123123"));
params.add(new BasicNameValuePair("code", code));
loginPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
// 禁用自动重定向
RequestConfig requestConfig = RequestConfig.custom()
.setRedirectsEnabled(false)
.build();
loginPost.setConfig(requestConfig);
return loginPost;
}
/**
* 处理登录响应
*/
private String processLoginResponse(CloseableHttpResponse loginResponse,
CookieStore cookieStore) throws IOException, InterruptedException {
int statusCode = loginResponse.getStatusLine().getStatusCode();
System.out.println("登录响应状态码: " + statusCode);
// 处理限速
if (statusCode == 429) {
handleRateLimit(loginResponse);
return null;
}
// 打印响应头
printResponseHeaders(loginResponse);
// 检查重定向
if (statusCode == 302) {
if (checkRedirectForError(loginResponse)) {
return null;
}
}
// 读取响应体
String tokenFromBody = extractTokenFromResponseBody(loginResponse);
if (tokenFromBody != null) {
return tokenFromBody;
}
// 从cookies中提取token
return extractTokenFromCookies(cookieStore, statusCode);
}
/**
* 处理速率限制
*/
private void handleRateLimit(CloseableHttpResponse response) throws InterruptedException {
System.out.println("登录请求被限速 (429 Too Many Requests)");
Header retryAfterHeader = response.getFirstHeader("Retry-After");
if (retryAfterHeader != null) {
try {
int retryAfterSeconds = Integer.parseInt(retryAfterHeader.getValue());
System.out.println("服务器要求等待 " + retryAfterSeconds + "");
Thread.sleep(retryAfterSeconds * 1000L);
} catch (NumberFormatException e) {
System.out.println("等待5秒后重试");
Thread.sleep(5000);
}
} else {
System.out.println("等待3秒后重试");
Thread.sleep(3000);
}
}
/**
* 检查重定向是否包含错误
*/
private boolean checkRedirectForError(CloseableHttpResponse response) {
Header locationHeader = response.getFirstHeader("Location");
if (locationHeader != null) {
String location = locationHeader.getValue();
System.out.println("重定向到: " + location);
if (location.contains("e=3")) {
System.out.println("验证码错误 (e=3)");
return true;
}
}
return false;
}
/**
* 打印响应头
*/
private void printResponseHeaders(CloseableHttpResponse response) {
System.out.println("响应头:");
for (Header header : response.getAllHeaders()) {
System.out.println(" " + header.getName() + ": " + header.getValue());
}
}
/**
* 从响应体中提取token
*/
private String extractTokenFromResponseBody(CloseableHttpResponse response) throws IOException {
if (response.getEntity() != null) {
String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8");
if (responseBody != null && !responseBody.isEmpty()) {
System.out.println("响应体: " + responseBody);
// 检查响应体中是否有tokenJSON格式
if (responseBody.contains("\"token\"")) {
// 简单提取token
int start = responseBody.indexOf("\"token\":\"");
if (start != -1) {
start += 9;
int end = responseBody.indexOf("\"", start);
if (end != -1) {
String token = responseBody.substring(start, end);
System.out.println("\n[SUCCESS] 从响应体找到Token!");
System.out.println("Token: " + token);
return token;
}
}
}
}
// 消耗实体
EntityUtils.consume(response.getEntity());
}
return null;
}
/**
* 从cookies中提取token
*/
private String extractTokenFromCookies(CookieStore cookieStore, int statusCode) {
List<Cookie> cookies = cookieStore.getCookies();
System.out.println("所有cookies (" + cookies.size() + "个):");
String token = null;
for (Cookie cookie : cookies) {
System.out.println(" " + cookie.getName() + " = " + cookie.getValue());
if ("token".equals(cookie.getName()) ||
cookie.getName().toLowerCase().contains("token")) {
token = cookie.getValue();
}
}
if (token != null && !token.isEmpty()) {
System.out.println("\n[SUCCESS] Login OK!");
System.out.println("Token: " + token);
return token;
} else if (statusCode == 200) {
System.out.println("登录返回200但没有找到token可能需要检查其他认证方式");
}
return null;
}
/**
* 设置通用请求头
*/
private void setCommonHeaders(HttpRequestBase request) {
request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
request.setHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
request.setHeader("Accept-Encoding", "gzip, deflate, br");
request.setHeader("Connection", "keep-alive");
request.setHeader("Upgrade-Insecure-Requests", "1");
}
/**
* 等待重试
*/
private void waitForRetry(int attempt) throws InterruptedException {
System.out.println("\n等待2秒后进行下一次尝试...");
Thread.sleep(2000);
}
}

View File

@@ -1,5 +1,7 @@
package com.tem.bocai.util;
import com.tem.bocai.entity.LoginInfoResult;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
@@ -24,121 +26,6 @@ public class SQLiteUtil {
return DriverManager.getConnection(DB_URL);
}
/**
* 初始化数据库表结构
*/
/*public static void initDatabase() {
Connection conn = null;
Statement stmt = null;
try {
conn = getConnection();
stmt = conn.createStatement();
// 开启事务
conn.setAutoCommit(false);
*//* // 删除旧表
stmt.execute("DROP TABLE IF EXISTS lottery_results");
System.out.println("已删除旧表");*//*
// 检查表是否存在
boolean tableExists = false;
try (ResultSet rs = conn.getMetaData().getTables(null, null, "lottery_results", null)) {
tableExists = rs.next();
}
if (tableExists) {
System.out.println("表已存在,检查列结构...");
// checkAndUpdateTableStructure(conn);
} else {
System.out.println("表不存在,创建新表...");
createTable(conn);
}
// 提交事务
conn.commit();
System.out.println("数据库表初始化/更新成功");
} catch (SQLException e) {
try {
if (conn != null) conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
System.err.println("数据库表初始化失败: " + e.getMessage());
} finally {
closeResources(null, stmt, conn);
}
}*/
/**
* 创建新表
*/
private static void createTable(Connection conn) throws SQLException {
String createTableSQL = """
CREATE TABLE lottery_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
time TEXT,
result TEXT,
winner INTEGER,
gd1 TEXT,
gd2 TEXT,
sum1 INTEGER,
sum2 INTEGER,
glh_result TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""";
try (Statement stmt = conn.createStatement()) {
stmt.execute(createTableSQL);
System.out.println("表创建成功");
}
}
/**
* 检查并更新表结构
*/
private static void checkAndUpdateTableStructure(Connection conn) throws SQLException {
// 定义需要的列和类型
String[][] requiredColumns = {
{"result", "TEXT"},
{"winner", "INTEGER"},
{"gd1", "TEXT"},
{"gd2", "TEXT"},
{"sum1", "INTEGER"},
{"sum2", "INTEGER"},
{"glh_result", "TEXT"},
{"created_at", "TIMESTAMP"},
{"updated_at", "TIMESTAMP"}
};
// 获取现有列
List<String> existingColumns = new ArrayList<>();
try (ResultSet rs = conn.getMetaData().getColumns(null, null, "lottery_results", null)) {
while (rs.next()) {
existingColumns.add(rs.getString("COLUMN_NAME").toLowerCase());
}
}
// 添加缺失的列
try (Statement stmt = conn.createStatement()) {
for (String[] column : requiredColumns) {
String columnName = column[0];
String columnType = column[1];
if (!existingColumns.contains(columnName.toLowerCase())) {
String alterSQL = String.format(
"ALTER TABLE lottery_results ADD COLUMN %s %s",
columnName, columnType
);
stmt.execute(alterSQL);
System.out.println("已添加列: " + columnName);
}
}
}
}
/**
* 将爬虫数据写入SQLite数据库
@@ -356,34 +243,144 @@ public class SQLiteUtil {
}
}
/**
* 查看表结构
* 添加/更新登录信息 - 如果用户名存在则先删除再插入(更新)
*/
public static void showTableStructure() {
String sql = "PRAGMA table_info(lottery_results)";
public static boolean addOrUpdateLoginInfo(LoginInfoResult loginInfo) {
if (loginInfo == null || loginInfo.getUsername() == null) {
return false;
}
try (Connection conn = getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
Connection conn = null;
PreparedStatement deleteStmt = null;
PreparedStatement insertStmt = null;
System.out.println("表结构 lottery_results:");
System.out.println("==========================");
System.out.printf("%-5s %-15s %-10s %-5s %-5s %-5s%n",
"cid", "name", "type", "notnull", "dflt_value", "pk");
System.out.println("------------------------------------------------------------");
try {
conn = getConnection();
conn.setAutoCommit(false); // 开启事务
while (rs.next()) {
System.out.printf("%-5d %-15s %-10s %-5d %-10s %-5d%n",
rs.getInt("cid"),
rs.getString("name"),
rs.getString("type"),
rs.getInt("notnull"),
rs.getString("dflt_value"),
rs.getInt("pk"));
String username = loginInfo.getUsername();
// 1. 先检查用户是否存在
boolean exists = existsByUsername(conn, username);
// 2. 如果存在,先删除
if (exists) {
String deleteSQL = "DELETE FROM login_info WHERE user_name = ?";
deleteStmt = conn.prepareStatement(deleteSQL);
deleteStmt.setString(1, username);
int deleted = deleteStmt.executeUpdate();
System.out.println("删除已存在的用户记录: " + username + ", 删除条数: " + deleted);
}
// 3. 插入新记录
String insertSQL = """
INSERT INTO login_info
(user_name, password, login_url, win_num, lose_num, create_time, update_time)
VALUES (?, ?, ?, ?, ?, ?, ?)
""";
insertStmt = conn.prepareStatement(insertSQL);
insertStmt.setString(1, loginInfo.getUsername());
insertStmt.setString(2, loginInfo.getPassword());
insertStmt.setString(3, loginInfo.getLoginUrl());
insertStmt.setInt(4, loginInfo.getWinNum() != null ? loginInfo.getWinNum() : 0);
insertStmt.setInt(5, loginInfo.getLoseNum() != null ? loginInfo.getLoseNum() : 0);
insertStmt.setTimestamp(6, new Timestamp(
loginInfo.getCreateTime() != null ?
loginInfo.getCreateTime().getTime() :
System.currentTimeMillis()
));
insertStmt.setTimestamp(7, new Timestamp(
loginInfo.getUpdateTime() != null ?
loginInfo.getUpdateTime().getTime() :
System.currentTimeMillis()
));
int inserted = insertStmt.executeUpdate();
// 提交事务
conn.commit();
if (inserted > 0) {
System.out.println((exists ? "更新" : "添加") + "登录信息成功: " + username);
return true;
} else {
System.out.println("添加/更新登录信息失败: " + username);
return false;
}
} catch (SQLException e) {
try {
if (conn != null) conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
System.err.println("添加/更新登录信息失败: " + e.getMessage());
e.printStackTrace();
return false;
} finally {
closeResources(null, deleteStmt, null);
closeResources(null, insertStmt, null);
closeResources(null, null, conn);
}
}
/**
* 检查用户名是否存在(使用现有连接)
*/
private static boolean existsByUsername(Connection conn, String username) throws SQLException {
String sql = "SELECT COUNT(*) FROM login_info WHERE user_name = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, username);
try (ResultSet rs = pstmt.executeQuery()) {
return rs.next() && rs.getInt(1) > 0;
}
}
}
/**
* 根据ID查询
*/
public static LoginInfoResult getLoginInfo() {
String sql = "SELECT * FROM login_info ";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
return mapResultSetToLoginInfo(rs);
}
}
} catch (SQLException e) {
System.err.println("根据ID查询登录信息失败: " + e.getMessage());
e.printStackTrace();
}
return null;
}
/**
* 将ResultSet映射为LoginInfoResult对象
*/
private static LoginInfoResult mapResultSetToLoginInfo(ResultSet rs) throws SQLException {
LoginInfoResult loginInfo = new LoginInfoResult();
loginInfo.setId(rs.getLong("id"));
loginInfo.setUsername(rs.getString("user_name"));
loginInfo.setPassword(rs.getString("password"));
loginInfo.setLoginUrl(rs.getString("login_url"));
loginInfo.setWinNum(rs.getInt("win_num"));
loginInfo.setLoseNum(rs.getInt("lose_num"));
loginInfo.setCreateTime(rs.getTimestamp("create_time"));
loginInfo.setUpdateTime(rs.getTimestamp("update_time"));
return loginInfo;
}
}

View File

@@ -1,10 +1,35 @@
package com.tem.bocai.util;
import com.tem.bocai.service.LoginService;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import org.apache.http.Header;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
@Service
@@ -13,7 +38,10 @@ public class TokenCacheService {
private static final String TOKEN_CACHE_NAME = "tokenCache";
private static final String TOKEN_KEY = "login_token";
private static final String TOKEN_INFO_KEY = "token_info";
private static final int MAX_RETRY = 10;
private static final String BASE_URL = "https://4701268539-esh.qdk63ayw8g.com";
@Autowired
private Tesseract tesseract;
@Autowired
private CacheManager tokenCacheManager;
@@ -35,8 +63,11 @@ public class TokenCacheService {
Cache cache = tokenCacheManager.getCache(TOKEN_CACHE_NAME);
if (cache != null) {
return cache.get(TOKEN_KEY, String.class);
} else {
String token = getTokenSqlite();
System.out.println("重新登入获取的token==" + token);
return token;
}
return null;
}
/**
@@ -89,4 +120,336 @@ public class TokenCacheService {
return null;
}
public String getTokenSqlite() {
String token = "";
for (int attempt = 1; attempt <= MAX_RETRY; attempt++) {
System.out.println("\n=== 第 " + attempt + " 次尝试 ===");
try {
token = attemptLogin();
if (token != null && !token.isEmpty()) {
return token;
}
if (attempt < MAX_RETRY) {
waitForRetry(attempt);
}
} catch (Exception e) {
System.err.println("" + attempt + " 次尝试失败: " + e.getMessage());
e.printStackTrace();
}
}
return "";
}
/**
* 等待重试
*/
public void waitForRetry(int attempt) throws InterruptedException {
System.out.println("\n等待2秒后进行下一次尝试...");
Thread.sleep(2000);
}
/**
* 单次登录尝试
*/
public String attemptLogin() {
CookieStore cookieStore = new BasicCookieStore();
try (CloseableHttpClient httpClient = createHttpClient(cookieStore)) {
// 1. 获取验证码
byte[] imageData = fetchCaptcha(httpClient);
if (imageData == null) {
return null;
}
// 2. OCR识别验证码
String code = processCaptcha(imageData);
if (code == null || code.length() != 4) {
return null;
}
// 3. 执行登录
return performLogin(httpClient, cookieStore, code);
} catch (Exception e) {
System.out.println("登录尝试失败" + e);
}
return null;
}
/**
* 创建HttpClient
*/
private CloseableHttpClient createHttpClient(CookieStore cookieStore) {
return HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.build();
}
/**
* 获取验证码图片
*/
private byte[] fetchCaptcha(CloseableHttpClient httpClient)
throws IOException, InterruptedException {
System.out.println("获取验证码...");
// 添加随机延迟
Thread.sleep(1000 + (long) (Math.random() * 1000));
HttpGet getCaptcha = new HttpGet(BASE_URL + "/code");
setCommonHeaders(getCaptcha);
getCaptcha.setHeader("Referer", BASE_URL + "/login");
try (CloseableHttpResponse captchaResponse = httpClient.execute(getCaptcha)) {
int captchaStatus = captchaResponse.getStatusLine().getStatusCode();
System.out.println("验证码响应状态码: " + captchaStatus);
if (captchaStatus == 200) {
return EntityUtils.toByteArray(captchaResponse.getEntity());
} else if (captchaStatus == 429) {
System.out.println("获取验证码被限速,等待后重试...");
Thread.sleep(3000);
} else {
System.out.println("获取验证码失败: " + captchaStatus);
}
}
return null;
}
/**
* 处理验证码识别
*/
private String processCaptcha(byte[] imageData)
throws IOException, TesseractException {
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData));
String rawOcr = tesseract.doOCR(image);
// 清理验证码
String code = rawOcr.replaceAll("\\s+", "").trim();
code = code.replaceAll("[^0-9]", ""); // 只保留数字
System.out.println("OCR原始结果: " + rawOcr);
System.out.println("清理后验证码: [" + code + "] 长度: " + code.length());
// 保存图片用于调试
//saveCaptchaImage(image);
return code;
}
/**
* 设置通用请求头
*/
private void setCommonHeaders(HttpRequestBase request) {
request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
request.setHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
request.setHeader("Accept-Encoding", "gzip, deflate, br");
request.setHeader("Connection", "keep-alive");
request.setHeader("Upgrade-Insecure-Requests", "1");
}
/**
* 保存验证码图片
*/
/* private void saveCaptchaImage(BufferedImage image) throws IOException {
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File output = new File("captcha_" + timestamp + ".png");
ImageIO.write(image, "png", output);
System.out.println("验证码图片已保存到: " + output.getAbsolutePath());
}*/
/**
* 执行登录请求
*/
private String performLogin(CloseableHttpClient httpClient,
CookieStore cookieStore,
String code) throws IOException, InterruptedException {
System.out.println("执行登录...");
// 等待一下再发送登录请求
Thread.sleep(1500 + (long) (Math.random() * 1000));
HttpPost loginPost = createLoginRequest(code);
try (CloseableHttpResponse loginResponse = httpClient.execute(loginPost)) {
return processLoginResponse(loginResponse, cookieStore);
}
}
/**
* 创建登录请求
*/
private HttpPost createLoginRequest(String code) throws UnsupportedEncodingException {
HttpPost loginPost = new HttpPost(BASE_URL + "/login");
// 设置请求头
setCommonHeaders(loginPost);
loginPost.setHeader("Referer", BASE_URL + "/login");
loginPost.setHeader("Origin", BASE_URL);
loginPost.setHeader("Accept", "application/json, text/plain, */*");
// 构建登录参数
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("type", "1"));
params.add(new BasicNameValuePair("account", "pmk1"));
params.add(new BasicNameValuePair("password", "Asd123123"));
params.add(new BasicNameValuePair("code", code));
loginPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
// 禁用自动重定向
RequestConfig requestConfig = RequestConfig.custom()
.setRedirectsEnabled(false)
.build();
loginPost.setConfig(requestConfig);
return loginPost;
}
/**
* 处理登录响应
*/
private String processLoginResponse(CloseableHttpResponse loginResponse,
CookieStore cookieStore) {
try {
int statusCode = loginResponse.getStatusLine().getStatusCode();
System.out.println("登录响应状态码: " + statusCode);
// 处理限速
if (statusCode == 429) {
handleRateLimit(loginResponse);
return null;
}
// 打印响应头
printResponseHeaders(loginResponse);
// 检查重定向
if (statusCode == 302) {
if (checkRedirectForError(loginResponse)) {
return null;
}
}
// 读取响应体
String tokenFromBody = extractTokenFromResponseBody(loginResponse);
if (tokenFromBody != null) {
return tokenFromBody;
}
// 从cookies中提取token
return extractTokenFromCookies(cookieStore, statusCode);
} catch (Exception e) {
System.out.println("请求异常" + e);
}
return null;
}
/**
* 打印响应头
*/
private void printResponseHeaders(CloseableHttpResponse response) {
System.out.println("响应头:");
for (Header header : response.getAllHeaders()) {
System.out.println(" " + header.getName() + ": " + header.getValue());
}
}
/**
* 检查重定向是否包含错误
*/
private boolean checkRedirectForError(CloseableHttpResponse response) {
Header locationHeader = response.getFirstHeader("Location");
if (locationHeader != null) {
String location = locationHeader.getValue();
System.out.println("重定向到: " + location);
if (location.contains("e=3")) {
System.out.println("验证码错误 (e=3)");
return true;
}
}
return false;
}
/**
* 从cookies中提取token
*/
private String extractTokenFromCookies(CookieStore cookieStore, int statusCode) {
List<Cookie> cookies = cookieStore.getCookies();
System.out.println("所有cookies (" + cookies.size() + "个):");
String token = null;
for (Cookie cookie : cookies) {
System.out.println(" " + cookie.getName() + " = " + cookie.getValue());
if ("token".equals(cookie.getName()) ||
cookie.getName().toLowerCase().contains("token")) {
token = cookie.getValue();
}
}
if (token != null && !token.isEmpty()) {
System.out.println("\n[SUCCESS] Login OK!");
System.out.println("Token: " + token);
return token;
} else if (statusCode == 200) {
System.out.println("登录返回200但没有找到token可能需要检查其他认证方式");
}
return null;
}
/**
* 从响应体中提取token
*/
private String extractTokenFromResponseBody(CloseableHttpResponse response) throws IOException {
if (response.getEntity() != null) {
String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8");
if (responseBody != null && !responseBody.isEmpty()) {
System.out.println("响应体: " + responseBody);
// 检查响应体中是否有tokenJSON格式
if (responseBody.contains("\"token\"")) {
// 简单提取token
int start = responseBody.indexOf("\"token\":\"");
if (start != -1) {
start += 9;
int end = responseBody.indexOf("\"", start);
if (end != -1) {
String token = responseBody.substring(start, end);
System.out.println("\n[SUCCESS] 从响应体找到Token!");
System.out.println("Token: " + token);
return token;
}
}
}
}
// 消耗实体
EntityUtils.consume(response.getEntity());
}
return null;
}
/**
* 处理速率限制
*/
private void handleRateLimit(CloseableHttpResponse response) throws InterruptedException {
System.out.println("登录请求被限速 (429 Too Many Requests)");
Header retryAfterHeader = response.getFirstHeader("Retry-After");
if (retryAfterHeader != null) {
try {
int retryAfterSeconds = Integer.parseInt(retryAfterHeader.getValue());
System.out.println("服务器要求等待 " + retryAfterSeconds + "");
Thread.sleep(retryAfterSeconds * 1000L);
} catch (NumberFormatException e) {
System.out.println("等待5秒后重试");
Thread.sleep(5000);
}
} else {
System.out.println("等待3秒后重试");
Thread.sleep(3000);
}
}
}