針對帶驗證碼的登錄頁面,實現自動獲取驗證碼圖片、用戶輸入驗證碼、攜帶 Cookie 發起登錄請求、解析響應判斷結果、登錄後持久化 Cookie 訪問受保護頁面的完整流程。代碼基於 ThinkPHP 3.x 控制器結構,邏輯通用可移植。
class LoginController extends Controller {
protected $cookieName = ['cookie_verify', 'cookie_verify'];
protected $cookiePath = '/cookie/';
protected $cookiePathFile = [];
public function _initialize() {
foreach ($this->cookieName as $key => $name) {
$this->cookiePathFile[] = ROOT_PATH . $this->cookiePath . $name . '_xxx.txt';
}
}
// ...
public function getVerifyCode() {
$img = $this->_cookieRequest('https://xxx.com/VerifyCode_Login.aspx?id=' . rand(10000, 999999), null, true, 1);
echo $img; // 直接輸出圖片二進制流
}
前端 <img src="/login/getVerifyCode" onclick="this.src=this.src+'&t='+Math.random()"> 實現點擊刷新。
public function xxxLogin() {
$username = I('username');
$password = I('password');
$verifyCode = I('verify');
$loginData = array(
'__VIEWSTATE' => '/wEPDwUKMTU0MzAzOTU4NmQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgEFDExvZ2luX1N1Ym1pdL/yae69NsY163G3yuP0lxjz8oXu',
'__VIEWSTATEGENERATOR' => 'DC42DE27',
'txt_UserName' => $username,
'txt_PWD' => $password,
'txt_VerifyCode' => $verifyCode,
'SMONEY' => 'ABC',
'Login_Submit.x' => '52',
'Login_Submit.y' => '19',
);
$getBack = $this->_cookieRequest('https://xxx.com/noLogin.aspx', $loginData);
// 解析錯誤提示
if (preg_match('/<div[^<div]*?id\s*=\s*[\'"]{1}div_msg[\'"]{1}.*?>(.*?)<\/div>/s', $getBack, $match)) {
// 頁面包含錯誤 div
} else {
// 解析重定向 URL 參數判斷具體錯誤碼
$paramsFull = parse_url($getBack);
parse_str($paramsFull['query'], $paramsFull['parsedQuery']);
if (!empty($paramsFull['parsedQuery']['Warn'])) {
$msgMap = [
2 => '驗證碼錯誤', 3 => '帳號不存在', 5 => '賬戶已注銷',
6 => '密碼錯誤', 20 => '今日密碼錯誤3次', 21 => 'IP密碼錯誤9次',
22 => 'IP登錄帳號過多', 23 => '驗證碼失效', 32 => '帳號綁定衝突', 33 => '一天只能註冊一個'
];
$this->error($msgMap[$paramsFull['parsedQuery']['Warn']] ?? '登錄失敗');
} else {
$_SESSION['user_id'] = '123456';
$this->success('登錄成功', U('Index/index'));
}
}
}
private function _cookieRequest($url, $data = null, $redirect = false, $cookieNum = 0) {
$ch = curl_init();
$params = [
CURLOPT_URL => $url,
CURLOPT_HEADER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 5.1; rv:9.0.1) Gecko/20100101 Firefox/9.0.1',
];
if ($data) {
$params[CURLOPT_POST] = true;
$params[CURLOPT_POSTFIELDS] = http_build_query($data);
}
// Cookie 管理:優先讀取現有文件,否則創建新文件
if (!empty($_COOKIE[$this->cookieName[$cookieNum]]) && is_file($this->cookiePathFile[$cookieNum])) {
$params[CURLOPT_COOKIEFILE] = $this->cookiePathFile[$cookieNum];
} else {
$params[CURLOPT_COOKIEJAR] = $this->cookiePathFile[$cookieNum];
setcookie($this->cookieName[$cookieNum], $this->cookiePathFile[$cookieNum], time() + 120);
}
curl_setopt_array($ch, $params);
$content = curl_exec($ch);
$headers = curl_getinfo($ch);
curl_close($ch);
if ($url != $headers["url"] && !$redirect) {
return $headers["url"]; // 返回重定向地址
}
return $content;
}
CURLOPT_COOKIEJAR (寫入) + CURLOPT_COOKIEFILE (讀取) 配合文件存儲,跨請求保持會話img 標籤加載,用戶肉眼識別輸入__VIEWSTATE、__VIEWSTATEGENERATOR 等字段Warn 映射友好提示登錄成功後,_cookieRequest 可直接用於訪問其他需認證頁面,自動攜帶已保存 Cookie。