UI自动化测试中Cookie持久化方案:绕过验证码实现稳定登录

发布时间:2026/7/1 21:23:08
UI自动化测试中Cookie持久化方案:绕过验证码实现稳定登录 1. 项目概述当UI自动化遇上验证码登录做UI自动化测试的朋友估计都绕不开一个“老大难”问题登录。尤其是现在稍微有点安全意识的系统登录页面上验证码几乎是标配。图形验证码、滑块验证码、点选验证码……花样百出目的就一个拦住机器。我们辛辛苦苦写好的自动化脚本往往就卡在这登录的第一步每次运行都得手动输验证码那还叫“自动化”吗我最近接手的一个项目就遇到了这个典型场景。系统登录强制要求手机验证码纯UI操作根本无法绕过。最初的方案是让脚本运行到登录页就暂停等人手动输入验证码后再继续但这完全违背了自动化的初衷——无人值守、定时执行。经过一番摸索和踩坑我最终采用了一套稳定且高效的方案首次手动登录获取并持久化Cookie后续所有自动化操作直接携带Cookie访问实现真正的“免登录”。这个方法的核心思想很简单验证码防的是“非人”的登录请求但一旦登录成功服务器会颁发一个身份凭证通常是Cookie给浏览器。我们只要在第一次手动登录时把这个凭证“偷”出来保存好以后就让自动化脚本伪装成这个已登录的浏览器去访问系统。对于服务器来说收到的请求带着合法的Cookie它就会认为这是同一个用户的持续会话自然就放行了。这不仅仅是绕过验证码的取巧更是一种符合测试逻辑的策略。我们自动化测试的重点是验证登录后的业务功能而不是反复去挑战那个难以自动化的登录环节。通过Cookie复用我们可以将测试资源精准地投入到核心业务流的验证中。接下来我就把这套方法的完整设计思路、技术细节、实操步骤以及我踩过的坑毫无保留地分享出来。2. 核心思路与方案选型为什么是Cookie持久化面对验证码登录业内常见的思路主要有几种我们需要先做个权衡。2.1 常见方案对比第一种是验证码识别。利用OCR技术如Tesseract或第三方打码平台去识别图形验证码。对于简单的数字、字母验证码可能有效但面对复杂的干扰线、扭曲变形或行为验证码如滑块识别率会急剧下降且需要持续投入成本维护识别模型或支付打码费用。更重要的是很多系统的验证码设计初衷就是反自动化会不断升级导致识别方案非常脆弱。第二种是后端接口绕过。如果测试环境由开发团队控制可以请求他们提供一个“万能验证码”或关闭登录接口的验证码校验。这当然是最彻底的方案但严重依赖开发配合且在生产环境或预发布环境通常不可行无法模拟真实用户的登录流程。第三种是Cookie/Session复用也就是我们本次采用的方法。它的前提是系统在登录后使用Cookie或类似的Session机制来维持用户状态。我们手动完成一次登录获取服务器返回的Cookie并将其保存下来。后续的自动化脚本在发起HTTP请求或驱动浏览器时直接携带这个Cookie从而跳过登录页面。2.2 为什么最终选择Cookie持久化方案经过对比Cookie持久化方案的优势非常明显高成功率与稳定性它不依赖于破解验证码而是利用系统既有的会话机制。只要系统登录逻辑不变Cookie未过期方案就始终有效。真实模拟用户行为它模拟的是一个真实用户登录后的一系列操作测试的流程和产生的数据流与真实场景完全一致测试结果可信度高。对测试环境友好不依赖于修改后端代码或配置在测试、预发布甚至某些只读的生产环境探查中都可能适用。技术实现通用无论是基于Selenium的浏览器自动化还是基于Requests的接口自动化都能方便地植入和携带Cookie。这个方案的关键在于两个环节一是如何可靠地获取到有效的Cookie二是如何安全、持久地存储它并在自动化脚本中方便地复用。这听起来简单但在实际操作中从浏览器中提取Cookie、处理Cookie的更新机制、应对登录态过期等问题每一个环节都有细节需要注意。注意此方法仅适用于合法的测试目的。任何未经授权获取、使用他人Cookie以访问系统或获取数据的行为都是违法违规的。我们讨论的范畴严格限于自己拥有权限的、用于自动化测试的系统。3. 技术实现详解从获取到应用的全链路确定了方案我们来拆解每一步的技术实现。整个流程可以概括为手动登录 - 提取Cookie - 持久化存储 - 自动化脚本加载使用。3.1 Cookie的获取与提取最直接的方式是从浏览器中获取。这里以Chrome浏览器为例介绍两种主流方法。方法一通过浏览器开发者工具手动复制适合初次探索使用Chrome正常访问目标系统完成手动登录。按F12打开开发者工具切换到Network网络标签页。刷新页面或点击登录后跳转到的第一个页面。在网络请求列表中找到该页面的请求通常是第一个document类型的请求点击它。在右侧的Headers标头选项卡中向下找到Request Headers请求头部分。里面有一行Cookie: ...后面的一长串字符串就是当前会话的Cookie。右键点击它选择Copy value复制值。这种方法简单直观但缺点是每次Cookie过期都需要重新手动操作无法集成到自动化流程中。方法二使用浏览器调试协议自动获取推荐用于自动化这才是我们实现“首次半自动后续全自动”的关键。我们可以通过工具在浏览器登录时自动拦截并保存Cookie。这里以配合Selenium WebDriver为例但原理是通用的。核心是利用WebDriver提供的get_cookies()方法。我们可以在登录成功后调用此方法获取当前浏览器会话中的所有Cookie。from selenium import webdriver import json import time # 1. 启动浏览器 driver webdriver.Chrome() driver.get(https://your-test-system.com/login) # 2. **此处暂停手动完成登录操作输入账号、密码、验证码** print(请手动完成登录操作...) input(登录完成后按回车键继续...) # 3. 登录成功后获取Cookie cookies driver.get_cookies() print(f获取到 {len(cookies)} 个Cookie) # 4. 将Cookie保存为JSON文件 with open(session_cookies.json, w) as f: json.dump(cookies, f) print(Cookie已保存至 session_cookies.json) # 5. 可以关闭浏览器了 driver.quit()这段脚本的作用是打开登录页然后阻塞等待你手动完成包括输入验证码在内的所有登录步骤。等你按下回车它立刻将浏览器内存中当前所有的Cookie抓取下来并以结构化的JSON格式保存到本地文件。这个文件就是我们的“通行证”。3.2 Cookie的持久化存储保存为JSON文件是最简单的方式。但我们需要考虑更多安全性Cookie可能包含会话标识如JSESSIONID,token泄露有风险。文件应放在安全位置或进行简单的加密。在团队协作中切勿将包含真实Cookie的文件提交到代码仓库。结构化存储一个系统可能有多个测试账号。我们可以设计一个更结构化的存储方式例如一个字典键为账号或环境名值为对应的Cookie列表。# 进阶存储示例按账号和环境管理 cookies_data { test_env: { user_admin: [...], # admin账号的cookie列表 user_normal: [...], # 普通账号的cookie列表 }, preprod_env: { ... } } with open(cookies_repo.json, w) as f: json.dump(cookies_data, f, indent2) # indent使文件更易读过期管理Cookie有有效期expiry字段。在加载Cookie前可以检查是否已过期如果过期则触发重新登录流程或报警。3.3 在自动化脚本中加载和使用Cookie获取并保存了Cookie接下来就是在真正的UI自动化脚本中使用它。场景一在Selenium中复用CookieSelenium提供了add_cookie()方法但必须在当前域名下才能添加。因此流程是先访问目标域名下的任意页面通常是首页或登录后首页然后批量添加Cookie。from selenium import webdriver import json def create_driver_with_cookies(cookie_file_path): 创建一个携带已保存Cookie的浏览器实例 driver webdriver.Chrome() # 1. 首先访问目标域名这一步很重要 driver.get(https://your-test-system.com/) # 2. 删除可能存在的旧Cookie确保环境干净 driver.delete_all_cookies() # 3. 加载之前保存的Cookie with open(cookie_file_path, r) as f: cookies json.load(f) for cookie in cookies: # 添加前可以处理一下过期时间等字段确保Selenium能接受 # 例如将expiry从时间戳转换为整数 if expiry in cookie: cookie[expiry] int(cookie[expiry]) try: driver.add_cookie(cookie) except Exception as e: print(f添加Cookie {cookie.get(name)} 时出错: {e}) # 4. 再次访问首页或目标页面此时请求头中已包含Cookie driver.get(https://your-test-system.com/dashboard) # 检查是否成功跳过登录例如查找登录后才有的元素 try: welcome_element driver.find_element(id, welcome-user) print(Cookie复用成功已处于登录状态) except: print(Cookie可能已失效需要重新登录。) # 这里可以触发重新登录的流程 return driver # 使用函数 driver create_driver_with_cookies(session_cookies.json) # 接下来就可以直接进行登录后的操作了例如 # driver.find_element(link text, 我的订单).click()场景二在Requests库中复用Cookie如果你的自动化测试更偏向于API层或者需要与UI自动化结合如先通过接口准备数据那么requests库是更好的选择。我们可以将Selenium获取的Cookie转换为requests可用的CookieJar或字典格式。import requests import json def create_session_with_cookies(cookie_file_path): 创建一个携带Cookie的requests会话 session requests.Session() with open(cookie_file_path, r) as f: cookies_list json.load(f) # 将Cookie列表转换为字典格式{‘name’: ‘value’, ...} cookies_dict {cookie[name]: cookie[value] for cookie in cookies_list} # 为会话设置Cookie session.cookies.update(cookies_dict) return session # 使用会话发起请求自动携带Cookie session create_session_with_cookies(session_cookies.json) response session.get(https://your-test-system.com/api/user/profile) if response.status_code 200: print(接口调用成功用户已登录。) print(response.json()) else: print(f接口调用失败状态码{response.status_code})实操心得在Selenium中添加Cookie时务必确保先访问目标域名。因为浏览器遵循同源策略你不能在google.com下添加your-system.com的Cookie。先访问一下目标域名让浏览器建立起这个域的上下文是成功添加Cookie的前提。4. 工程化与最佳实践让方案更健壮一个能在项目中长期稳定运行的方案绝不能只是几行脚本。我们需要从工程化的角度考虑其健壮性、可维护性和团队协作。4.1 Cookie的生命周期管理与刷新机制Cookie不是永久的。服务器设置的Session Cookie在浏览器关闭后失效Persistent Cookie也有一个Expires或Max-Age时间。我们的自动化脚本必须能处理Cookie失效的情况。我的策略是**“惰性验证与主动刷新”**。惰性验证在每次使用Cookie发起关键请求前或定期不预先做复杂的有效性检查而是直接发起一个对登录态敏感的轻量级请求如获取用户昵称的接口。如果返回401/403或跳转到登录页则判定Cookie失效。主动刷新一旦判定失效立刻触发一个“重新登录流程”。这个流程可以是我们最初那个半自动的脚本也可以尝试其他备用方案如是否有备用测试账号的Cookie。最好能通过邮件、钉钉/企业微信机器人通知测试负责人。# 一个简单的Cookie有效性检查与刷新示例 class CookieManager: def __init__(self, cookie_file, login_url): self.cookie_file cookie_file self.login_url login_url self.session requests.Session() self.load_cookies() def load_cookies(self): if os.path.exists(self.cookie_file): with open(self.cookie_file, r) as f: cookies json.load(f) self.session.cookies.update({c[name]: c[value] for c in cookies}) def is_cookie_valid(self): 检查Cookie是否有效 check_url self.login_url.replace(/login, /api/check-auth) try: resp self.session.get(check_url, timeout5) return resp.status_code 200 and resp.json().get(authenticated) is True except: return False def refresh_cookie(self): 触发重新登录流程这里需要根据实际情况实现可能是半自动或调用其他接口 print(Cookie已失效需要重新登录。) # 例如调用一个外部脚本或者抛出一个需要人工干预的异常 # subprocess.run([python, manual_login.py]) raise Exception(Cookie失效请手动运行登录脚本更新Cookie。) def get_session(self): 获取会话如果无效则尝试刷新 if not self.is_cookie_valid(): self.refresh_cookie() self.load_cookies() # 刷新后重新加载 return self.session # 使用 manager CookieManager(cookies.json, https://xxx.com/login) session manager.get_session() # 这个方法会保证返回一个有效的session4.2 多环境与多账号管理实际项目中我们通常有测试环境、预发布环境并且需要测试不同角色管理员、普通用户的权限。我们的Cookie仓库需要支持这种多维管理。我建议的目录结构如下project/ ├── config/ │ ├── cookies/ │ │ ├── test_env_admin.json │ │ ├── test_env_user.json │ │ └── preprod_env_admin.json │ └── config.yaml # 配置当前运行的环境和角色 ├── utils/ │ └── cookie_manager.py # 封装的Cookie管理类 └── test_scripts/ └── test_order.py在config.yaml中定义当前运行配置current_env: test_env current_role: admincookie_manager.py根据配置自动加载对应环境的对应账号Cookie。4.3 安全注意事项这是重中之重。绝不提交将包含Cookie的JSON文件加入.gitignore确保不会意外提交到版本控制系统。本地加密对于特别敏感的项目可以对保存Cookie的文件进行简单的对称加密如使用cryptography库。虽然密钥同样需要保存但增加了一层防护。使用测试账号务必使用专门的测试账号避免使用高权限的个人账号Cookie。测试账号的密码可以定期修改即使Cookie泄露影响也有限。环境隔离生产环境的Cookie绝对不要用于自动化测试也不要在测试脚本中硬编码生产环境的域名和账号。5. 常见问题排查与实战技巧在实际落地过程中我遇到了不少坑。这里总结几个典型问题和解决方法希望能帮你节省时间。5.1 问题一添加Cookie后刷新页面仍然未登录现象按照步骤添加了Cookie但driver.get()目标页面后还是跳转到了登录页。排查思路域名检查确保你添加Cookie前访问的域名与你后续要测试的页面域名完全一致包括协议http/https、主域名、端口。www.example.com和example.com被认为是不同的域。Cookie路径检查获取的Cookie是否有path属性。如果Cookie的path是/admin那么它只在/admin及其子路径下有效。添加Cookie后你需要访问这个路径或子路径下的页面。Secure/HttpOnly属性如果Cookie设置了Secure属性它只能通过HTTPS连接传输。确保你的测试环境使用了HTTPS。HttpOnly属性意味着Cookie无法通过JavaScript如document.cookie访问但不影响Selenium的add_cookie。清理旧会话在添加新Cookie前务必执行driver.delete_all_cookies()。浏览器里可能残留了旧的、无效的会话Cookie干扰了新Cookie的生效。解决技巧写一个调试函数在添加Cookie后打印当前页面的所有Cookie与你保存的Cookie列表对比看看是否成功添加。5.2 问题二Cookie很快失效需要频繁手动更新现象今天保存的Cookie明天跑脚本就失效了。排查思路会话过期系统可能设置了很短的会话超时时间如15分钟。这不是Cookie方案的问题而是测试环境策略。可以与开发沟通能否为测试环境适当延长会话时间。动态Token有些系统特别是单点登录SSO使用的不是简单的Session Cookie而是如JWTJSON Web Token之类的Token并且Token可能有更短的刷新机制。你需要分析登录后的网络请求看是否有一个定期调用的/refresh-token接口。你的自动化脚本可能需要定期调用这个接口来更新Cookie/Token。IP或设备绑定高级安全策略可能会将登录会话与首次登录的IP或浏览器指纹绑定。如果你的自动化脚本运行在CI/CD服务器如Jenkins上而Cookie是在你本地电脑获取的可能会因IP不同导致失效。需要在同一网络环境下获取和使用Cookie。解决技巧实现上文提到的CookieManager类集成自动检测和半自动刷新机制。对于动态Token系统可能需要分析并模拟心跳请求。5.3 问题三部分页面功能异常提示权限不足现象登录成功了也能访问首页但点击某些菜单或按钮时报错。排查思路Cookie缺失登录成功后系统可能设置了多个Cookie如一个用于身份认证auth_token一个用于用户信息user_info一个用于CSRF防护XSRF-TOKEN。你保存的Cookie列表可能不完整。确保使用driver.get_cookies()获取了所有Cookie。CSRF Token很多Web框架如Spring Security, Django使用CSRF Token防止跨站请求伪造。这个Token通常放在一个特定的Cookie里如csrftoken和一个表单隐藏域或请求头里。你的后续操作如提交表单需要同时携带这个Cookie和对应的Token值。你需要从页面元素或接口响应中提取这个Token并将其添加到请求中。账号权限确认你使用的测试账号是否确实拥有操作该功能的权限。可能你需要的是管理员Cookie但实际加载的是普通用户Cookie。解决技巧使用浏览器开发者工具对比手动正常操作和自动化操作时发送的HTTP请求头特别是Cookie头和X-CSRF-TOKEN等自定义头有何不同。差异点往往就是问题的根源。5.4 一个提升效率的实战技巧使用浏览器Profile对于Selenium方案每次启动全新的浏览器实例即使添加了Cookie也可能因为浏览器指纹、本地存储等因素与手动登录时的环境有细微差异。一个更稳定的方法是复用浏览器用户数据目录。你可以在手动登录时让Selenium使用一个固定的用户数据目录user-data-dir。这样浏览器会保存所有的Cookie、LocalStorage、IndexedDB等数据。后续的自动化脚本也指定使用同一个目录这样启动的浏览器就是“已登录”的状态无需再手动添加Cookie。# 首次手动登录并保存环境 options webdriver.ChromeOptions() options.add_argument(r--user-data-dirC:\path\to\your\chrome\test\profile) # 指定一个目录 driver webdriver.Chrome(optionsoptions) driver.get(https://your-test-system.com/login) # ... 手动登录 ... # 登录成功后直接关闭浏览器即可。所有会话数据已保存在指定目录。 # 后续自动化脚本 options webdriver.ChromeOptions() options.add_argument(r--user-data-dirC:\path\to\your\chrome\test\profile) driver webdriver.Chrome(optionsoptions) driver.get(https://your-test-system.com/dashboard) # 此时应该直接就是登录状态这个方法非常可靠因为它100%还原了手动登录时的浏览器环境。但要注意这个用户数据目录是独占的不能同时被多个Chrome实例使用。在并行测试时需要为每个线程或进程创建独立的目录副本。