自动化测试中利用Cookie绕过验证码实现登录状态保持

📅 2026/8/2 20:39:13
自动化测试中利用Cookie绕过验证码实现登录状态保持
1. 项目概述当自动化测试遇上验证码登录做接口自动化测试的朋友估计都绕不开一个经典的“拦路虎”登录。尤其是那些带验证码的登录接口每次跑脚本前还得手动去瞅一眼图片输几个歪歪扭扭的字符这自动化不就“自动”不起来了吗更头疼的是很多业务场景要求保持登录状态去执行后续一系列需要鉴权的操作比如查询订单、提交数据等等。今天要聊的就是一个在自动化测试圈里老生常谈但又非常实用的技巧利用Cookie绕过验证码实现自动登录并保持会话状态。这听起来有点像“走后门”但实际上它遵循的是HTTP协议无状态特性的标准解决方案。核心思路不是去“破解”或“识别”验证码那属于另一个复杂的领域如OCR或行为验证码逆向而是在一次成功的手动登录后获取系统颁发的身份凭证——Cookie并在后续的自动化请求中复用这个Cookie。这样一来就模拟了一个“已登录用户”的状态从而跳过登录环节直接进行业务测试。这对于需要频繁执行回归测试、数据准备或监控脚本的场景来说能极大提升效率。接下来我会结合自己趟过的坑详细拆解这里面的门道、具体怎么操作以及如何让它稳定可靠。2. 核心原理Session、Cookie与Token的三角关系在动手之前必须得把底层原理捋清楚不然就是瞎搞遇到问题根本无从排查。我们常说的“保持登录状态”本质是服务器要能认出连续的多个请求来自同一个用户。因为HTTP协议本身是无状态的所以需要一些“信物”。2.1 为什么是CookieCookie是浏览器实现的一种机制用于在客户端存储少量数据通常由服务器通过Set-Cookie响应头设置。当浏览器再次向同一服务器发起请求时会自动带上这些Cookie通过Cookie请求头。服务器通过解析Cookie中的内容最常见的是一个Session ID就能找到对应的会话信息从而识别用户身份。在自动化测试中我们用的不是浏览器而是像requestsPython、HttpClientJava这样的HTTP客户端库。这些库同样支持手动设置和发送Cookie。所以我们的任务就是模拟浏览器的行为获取有效的Cookie并在后续请求中正确携带。2.2 与Session、Token的区别与联系网络热词里也提到了cookie session token区别这里简单厘清避免混淆Session会话是一个服务器端的概念。服务器为每个用户创建一块内存空间或持久化存储用来存放该用户的临时数据如登录状态、用户信息。这块空间需要一个唯一的钥匙来访问这把钥匙就是Session ID。Cookie是一个客户端的存储机制是存放Session ID或其他身份信息的“盒子”之一。它最常用于在浏览器中传递Session ID。但Cookie不是唯一途径URL参数、请求头如Authorization也能传。Token令牌是一种更现代的身份验证方式如JWT。它也是身份凭证但特点是将用户信息、有效期等直接编码在令牌字符串里服务器无需维护会话状态通过验证令牌的签名即可。Token通常放在请求头如Authorization: Bearer token中发送也可以放在Cookie里。对于我们这个“绕过验证码登录”的场景主要打交道的是基于Session-Cookie的认证机制。我们的目标就是拿到那个包含有效Session ID的Cookie。注意有些系统可能采用Token认证或者Session ID并不通过Cookie传递虽然不常见。在动手前先用浏览器开发者工具的“网络(Network)”选项卡抓一次登录请求观察身份凭证具体是通过Cookie请求头、Authorization请求头还是请求体里的某个字段传递的。这是最关键的一步。3. 实操准备工具选择与环境搭建工欲善其事必先利其器。一套顺手的工具链能让整个过程事半功倍。3.1 测试框架与HTTP客户端对于接口自动化Python的pytestrequests组合是轻量级快速上手的不二之选。requests库对Cookie的支持非常友好有现成的Session对象来维持会话。# 安装核心库 pip install requests pytest如果你所在团队使用Java那么HttpClient配合TestNG或JUnit也是标准做法。思路完全一致只是语法不同。本文主要以Pythonrequests为例进行演示因为其代码更简洁直观。3.2 抓包分析工具看清数据流向这是至关重要的一步。不要凭感觉猜一定要用工具看。浏览器开发者工具 (F12)最直接。打开“网络(Network)”选项卡勾选“保留日志(Preserve log)”。进行一次完整的手动登录包括输入验证码然后观察登录请求通常是POST和登录后第一个请求的请求头、响应头。专业抓包工具如Fiddler或Charles。它们能捕获所有HTTP/HTTPS流量对于分析复杂的重定向、查看加密前的数据非常有用。特别是当登录过程涉及多个跳转时它们比浏览器工具更清晰。你需要重点关注登录请求它的URL、请求方法、请求体你的用户名、密码、验证码以及**响应头(Response Headers)**里有没有Set-Cookie。登录后的请求登录成功后你点击的第一个需要登录状态的页面或接口。观察它的请求头(Request Headers)看里面Cookie字段的内容是什么。这个Cookie就是你要复用的“信物”。3.3 验证码的“绕过”策略澄清再次强调我们这里不涉及任何验证码识别技术如OCR识别图片、破解滑动轨迹。我们的策略是首次/手动获取通过一次手动操作可以是半自动脚本提示你输入验证码完成登录并程序化地保存服务器返回的Cookie。后续/自动复用在Cookie有效期内由服务器设置的Expires或Max-Age决定直接使用保存的Cookie发起请求实现“已登录”状态。因此这个方案的有效性完全依赖于Cookie的生命周期。对于会话CookieSession Cookie浏览器关闭即失效只要你的自动化脚本进程不终止并且服务器Session未过期就可以一直用。对于持久化Cookie设置了较长过期时间甚至可以保存到文件多次运行脚本时读取。4. 核心步骤拆解与代码实现下面我们以一个虚构的https://example.com/login登录接口为例分步拆解如何实现。4.1 步骤一手动登录并捕获Cookie首先我们写一个函数完成“输入验证码”的登录并保存关键的Cookie。这里假设登录接口需要username,password,captcha三个参数。import requests from http.cookies import SimpleCookie def manual_login_and_save_cookie(username, password, login_url): 手动登录并保存Cookie。 需要手动输入验证码。 # 1. 创建一个会话对象它会自动管理Cookie session requests.Session() # 2. 通常登录前可能需要先访问登录页获取一些初始Cookie或Token如CSRF Token # 这里假设登录页是 login_url print(f正在访问登录页: {login_url}) resp_pre session.get(login_url) # 可以在这里解析页面获取可能的CSRF token这里省略 # 3. 获取验证码假设验证码图片地址是固定的或者从响应中解析 # 这里需要根据实际系统调整。可能是独立的/captcha接口也可能是登录页图片。 captcha_url https://example.com/captcha.jpg print(f正在获取验证码图片: {captcha_url}) captcha_resp session.get(captcha_url) # 将验证码图片保存到本地方便人工查看 with open(captcha.png, wb) as f: f.write(captcha_resp.content) print(验证码图片已保存为 captcha.png请打开查看并输入。) # 4. 手动输入验证码 captcha_code input(请输入验证码图片中的字符: ) # 5. 构造登录请求数据 login_data { username: username, password: password, captcha: captcha_code, # 可能还有其他隐藏字段如 csrf_token # _csrf: csrf_token } # 6. 发送登录请求 print(正在发送登录请求...) login_resp session.post(login_url, datalogin_data) # 7. 检查登录是否成功根据实际接口返回判断 if login_resp.status_code 200: # 假设成功返回的JSON里包含 {success: true} if login_resp.json().get(success): print(登录成功) # 获取并保存Cookie # session.cookies 是一个 RequestsCookieJar 对象 cookies_dict requests.utils.dict_from_cookiejar(session.cookies) print(f获取到的Cookie: {cookies_dict}) # 将Cookie保存到文件以字典形式使用json保存 import json with open(cookies.json, w) as f: json.dump(cookies_dict, f) print(Cookie已保存至 cookies.json) return session.cookies # 返回cookiejar对象 else: print(f登录失败: {login_resp.text}) return None else: print(f登录请求异常状态码: {login_resp.status_code}) return None # 使用示例第一次运行时调用 if __name__ __main__: my_username your_username my_password your_password login_url https://example.com/api/login saved_cookies manual_login_and_save_cookie(my_username, my_password, login_url)关键点解析requests.Session()这是核心。Session对象会跨请求自动保持某些参数最常用的就是Cookie。你用同一个session发请求它自动处理Set-Cookie和后续请求的Cookie头。dict_from_cookiejar将CookieJar对象转为普通的字典方便序列化保存到文件。验证码处理这里是最“手动”的部分。脚本帮你下载图片你人工识别并输入。这实现了“一次手动长期自动”。4.2 步骤二加载Cookie并保持会话状态有了保存好的Cookie文件后续的自动化脚本就可以直接加载无需再走登录流程。import requests import json def create_session_with_cookies(cookie_filecookies.json): 从文件加载Cookie创建一个携带Cookie的会话。 try: with open(cookie_file, r) as f: cookies_dict json.load(f) except FileNotFoundError: print(fCookie文件 {cookie_file} 不存在请先运行手动登录。) return None # 创建一个新的会话 session requests.Session() # 将字典形式的Cookie添加到会话中 # 注意这里需要将字典转换回RequestsCookieJar cookies_jar requests.utils.cookiejar_from_dict(cookies_dict) session.cookies.update(cookies_jar) print(f已从 {cookie_file} 加载Cookie。) return session def test_authenticated_api(session, api_url): 使用带Cookie的会话测试需要登录的接口。 if session is None: print(会话创建失败无法测试。) return resp session.get(api_url) print(f请求 {api_url}) print(f状态码: {resp.status_code}) # 根据业务逻辑判断是否依然处于登录状态 # 例如返回用户信息成功或者没有跳转到登录页 if resp.status_code 200: # 假设成功返回用户相关数据 data resp.json() if data.get(user): print(f接口调用成功当前用户: {data[user][name]}) else: print(接口返回成功但可能会话已过期。) print(f响应内容: {data}) elif resp.status_code 302 or resp.status_code 401: # 常见的重定向到登录页或未授权状态码 print(会话可能已过期需要重新登录。) else: print(f请求失败响应: {resp.text}) # 使用示例后续自动化测试中调用 if __name__ __main__: # 加载Cookie创建会话 authed_session create_session_with_cookies() if authed_session: # 测试一个需要登录的接口例如用户信息接口 user_info_url https://example.com/api/user/profile test_authenticated_api(authed_session, user_info_url) # 可以继续用同一个session测试其他接口 order_url https://example.com/api/orders test_authenticated_api(authed_session, order_url)关键点解析cookiejar_from_dict将保存的字典还原为CookieJar对象用于更新会话的Cookie。会话复用所有使用authed_session发起的请求都会自动带上之前登录获得的Cookie服务器就会把这些请求当作已登录用户的请求来处理。状态检查在test_authenticated_api函数中我们不仅检查HTTP状态码200成功302重定向401未授权等还根据接口返回的业务数据判断登录状态是否真正有效。这是必要的因为Cookie可能还在但服务器端的Session已经超时销毁了。4.3 步骤三Cookie的维护与更新策略Cookie不是永久的需要维护。过期处理Cookie有生命周期。在加载Cookie发起请求时如果服务器返回401或跳转到登录页说明Cookie已失效。我们的脚本需要能捕获这种状态并触发重新登录流程即回调步骤一的函数可能需要再次手动输验证码。定时刷新对于长期运行的监控脚本可以设计一个定时任务定期比如在Cookie过期前半小时调用一个“心跳”或“刷新令牌”的接口如果系统提供来延长Session有效期。如果没有此类接口则需要定期重新登录。安全存储cookies.json文件包含了你的身份凭证务必妥善保管不要提交到代码仓库。可以通过.gitignore忽略它或者使用环境变量、加密配置文件来管理敏感信息。# 一个简单的带过期重试的封装示例 def safe_api_call(api_url, max_retry1): 安全的API调用如果发现Cookie失效尝试重新登录一次。 session create_session_with_cookies() if not session: print(无法创建会话退出。) return for i in range(max_retry 1): resp session.get(api_url) if resp.status_code 200: # 业务逻辑判断这里简单化 return resp.json() elif resp.status_code in [401, 403] and i max_retry: print(会话失效尝试重新登录...) # 这里需要调用你的 manual_login_and_save_cookie 函数 # 注意重新登录可能需要人工干预输入验证码 # new_cookies manual_login_and_save_cookie(...) # if new_cookies: # session.cookies.update(new_cookies) # else: # break print(重新登录逻辑需根据实际情况实现) break # 示例中直接跳出 else: print(fAPI调用失败状态码: {resp.status_code}) return None return None5. 高级话题与常见问题排查掌握了基础操作我们再来看看一些更复杂的情况和容易踩的坑。5.1 当Cookie不是唯一凭证时有些现代Web应用可能采用更复杂的认证机制Cookie Token混合登录后Cookie中存一个Session ID同时响应体里返回一个access_token后续某些API需要将这个token放在Authorization头里。你需要同时保存两者。双重Cookie如认证Cookie 安全Cookie系统可能设置多个Cookie例如SESSIONID和XSRF-TOKEN。你需要确保把所有必要的Cookie都保存和发送。动态Cookie或签名Cookie的值可能每次请求都会变化或者包含了请求签名。这种情况单纯复用固定的Cookie值可能无效。需要分析其生成逻辑但这通常意味着此方案不适用。应对策略抓包时务必仔细。不仅要看Cookie请求头还要看其他自定义请求头如X-Requested-With,X-CSRF-Token,Authorization以及请求体。保存凭证时可能需要保存一个包含多种信息的“凭证包”。5.2 关于验证码的那些“坑”热词里提到了各种验证码如滑块验证码绕过、阿里云滑块验证码、vaptcha手势验证码逆向。我们的方案是“绕过”而非“破解”。但如果系统升级验证码成为强校验的一部分例如每次关键操作前都需要验证我们的方案就会失效。此时可能需要寻找后端测试接口联系开发团队看能否在测试环境提供免验证码的登录接口这是最规范、最稳定的做法。使用验证码识别服务谨慎对于简单的图形验证码可以集成第三方OCR API如腾讯云、百度AI的OCR服务。但这会产生费用且识别率并非100%。模拟滑动/手势行为对于滑块、点选等验证码理论上可以通过Selenium等UI自动化工具模拟鼠标操作但这极其复杂、不稳定且容易被反爬机制检测不推荐在自动化测试中作为主要方案仅作为最后的研究手段。5.3 典型问题排查清单FAQ在实际操作中你可能会遇到以下问题问题现象可能原因排查步骤与解决方案加载Cookie后请求仍返回登录页或401。1. Cookie已过期。2. Cookie不完整漏存了关键Cookie。3. 服务器Session存储异常或重启。4. 请求的域名、路径与Cookie的Domain/Path属性不匹配。1. 检查Cookie文件的生成时间手动在浏览器登录看是否还能用。2. 对比浏览器中成功请求的Cookie头和你保存的Cookie字典看是否缺失。用session.cookies属性查看所有Cookie的详细信息。3. 重新运行手动登录流程生成新Cookie。4. 确保你的脚本请求的URL协议、域名、端口与登录时完全一致。登录请求成功但保存的Cookie是空的。1. 登录接口没有通过Set-Cookie响应头下发Cookie可能用了其他方式如Token在响应体。2. 登录实际上失败了比如验证码错误但返回了200状态码和错误信息。1. 仔细检查登录请求的响应头确认有无Set-Cookie。检查响应体看是否有token等字段。2. 加强登录成功与否的判断逻辑不仅看状态码更要解析响应内容。同一个Cookie在浏览器好用在脚本里不好用。1.请求头(User-Agent等)不一致。有些服务器会校验User-Agent。2.缺少必要的Referer或Origin头。3.Cookie的作用域问题Secure, HttpOnly标志。requests默认支持HttpOnly Cookie但需注意。1. 在脚本的Session中设置与浏览器一致的Headers特别是User-Agent。2. 从浏览器成功请求中复制完整的请求头除了Cookie尝试在脚本中设置。3. 使用session.headers.update()来统一添加头信息。遇到重定向循环类似热词中“重定向你太多次”。通常是因为Cookie无效或缺失导致服务器不断试图将你重定向到登录页但重定向逻辑有缺陷。1. 确保Cookie正确加载且未过期。2. 在Session中设置allow_redirectsFalse观察中间的重定向响应定位问题环节。3. 检查重定向的Location地址看是否是登录页。5.4 安全与最佳实践隔离测试数据用于自动化登录的测试账号最好与日常使用的账号分开。避免测试操作影响真实数据。密码管理不要在代码中硬编码密码。使用环境变量、配置文件或密钥管理服务来存储敏感信息。Cookie文件安全生成的cookies.json文件包含会话密钥应像对待密码一样保护它。将其加入.gitignore避免泄露。会话超时处理在你的自动化测试框架中加入全局的会话健康检查机制。在测试套件开始或关键步骤前先用一个轻量级的“心跳”接口检查Cookie是否有效无效则提前报错或触发登录流程而不是等到业务接口失败才发现。明确适用范围此方案主要适用于测试环境的自动化。在生产环境或安全要求极高的场景下应使用更规范的认证方式如服务端Token、OAuth2.0客户端凭证等。6. 集成到自动化测试框架最后我们看看如何把这套机制优雅地集成到像pytest这样的测试框架中使其成为自动化测试流程的一部分。核心思想是利用pytest的fixture机制提供一个可依赖的、已登录的session给各个测试用例。# conftest.py import pytest import requests import json import os pytest.fixture(scopesession) # 作用域为整个测试会话所有测试用例共用同一个登录状态 def authenticated_session(): 提供一个已认证的requests会话。 如果已有有效的cookie文件则直接加载。 否则提示手动登录这里简化实际需集成手动登录逻辑。 cookie_file test_cookies.json session requests.Session() # 可以设置一些通用请求头模拟浏览器 session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..., Accept: application/json, text/html, */*, }) # 尝试加载现有Cookie if os.path.exists(cookie_file): try: with open(cookie_file, r) as f: cookies_dict json.load(f) cookies_jar requests.utils.cookiejar_from_dict(cookies_dict) session.cookies.update(cookies_jar) print(f从 {cookie_file} 加载Cookie。) # 可选做一个快速的心跳检查 check_resp session.get(https://example.com/api/heartbeat) if check_resp.status_code 200: print(Cookie有效直接使用。) return session else: print(Cookie已失效需要重新登录。) os.remove(cookie_file) # 删除无效文件 except Exception as e: print(f加载Cookie失败: {e}) # 如果没有有效Cookie则执行登录流程这里需要你实现 print(无有效Cookie开始登录流程...) # 这里应调用你的 manual_login_and_save_cookie 函数 # 为了示例我们假设登录成功并获得了新的cookiejar: new_cookies # new_cookies manual_login_and_save_cookie(...) # if new_cookies: # session.cookies.update(new_cookies) # # 保存新的Cookie到文件 # cookies_dict requests.utils.dict_from_cookiejar(session.cookies) # with open(cookie_file, w) as f: # json.dump(cookies_dict, f) # else: # pytest.fail(登录失败无法获取认证会话。) # 示例中我们直接返回一个未登录的session实际项目需替换上述注释部分 print(示例中跳过真实登录返回基础session) return session # test_user_profile.py class TestUserApis: 测试用户相关接口 def test_get_profile(self, authenticated_session): 测试获取用户资料 url https://example.com/api/user/profile resp authenticated_session.get(url) assert resp.status_code 200 data resp.json() assert user in data assert data[user][username] is not None print(f当前用户: {data[user][username]}) def test_update_profile(self, authenticated_session): 测试更新用户资料 url https://example.com/api/user/profile update_data {nickname: 自动化测试用户} resp authenticated_session.patch(url, jsonupdate_data) assert resp.status_code 200 # ... 更多断言通过fixture我们将登录状态的管理封装起来测试用例编写者无需关心Cookie如何获取和刷新只需声明需要authenticated_session就可以直接编写业务逻辑测试使得测试代码更加清晰、可维护。整个方案从原理到实践从基础操作到框架集成核心就是理解并模拟浏览器维护会话的过程。它虽然不是万能的对于验证码强校验或动态Token机制会失效但在大多数基于Session-Cookie的传统Web系统测试中是一个稳定、高效且易于实现的方案。关键在于细心抓包分析妥善处理Cookie的生命周期和异常情况。