自动化测试-如何通过滑动验证码获取token

如果只需要账号和密码就可以登录,可以直接用playwright录制脚本即可,但是有些网址登录需要验证码。这里讨论的情况是滑动验证码的情况,步骤如下:

playwright输入账号、密码

def run(playwright: Playwright) -> str:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()
    page.goto("http://xxxxx/login")
    page.get_by_text("业务中台登录").click()
    page.get_by_placeholder("请输入用户名/电子邮件/移动电话/工号/AD域帐号").click()
    page.get_by_placeholder("请输入用户名/电子邮件/移动电话/工号/AD域帐号").fill("账号")
    page.get_by_placeholder("请输入密码").click()
    page.get_by_placeholder("请输入密码").fill("密码")
    page.get_by_role("button", name="登录").click()

处理滑动验证码

通常点击登录后,就会弹出滑动验证码

首先需要获取到验证码背景图和缺口图

#bg为背景图的base64地址
img1 = page.get_by_role("img").nth(1)
bg = img1.get_attribute('src')
#bg_width为背景图的宽度(当前playwright打开的界面中的背景图宽度)
bg_box = img1.bounding_box()
bg_width = bg_box["width"]
#hx为缺口的base64地址
img2 = page.get_by_role("img").nth(2)
hx = img2.get_attribute('src')

求出缺口需要移动的距离

def show(name):
    '''展示圈出来的位置'''
    cv2.imshow('Show', name)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

def _tran_canny(image):
    """消除噪声"""
    image = cv2.GaussianBlur(image, (3, 3), 0)
    return cv2.Canny(image, 100, 200)
    
def get_notch_location(hx, bg,bg_width):
    '''
    求出缺口需要移动的距离
    :param hx: 滑块图片的文件路径
    :param bg: 背景图片的文件路径
    :param bg_width: 背景图片目前的宽度
    :return:
    '''

    # 从Base64解码图片数据
    hx = base64.b64decode(hx.split(',')[1])
    bg = base64.b64decode(bg.split(',')[1])

    # 将解码后的数据转换为NumPy数组
    hx = np.frombuffer(hx, np.uint8)
    bg = np.frombuffer(bg, np.uint8)

    # 使用OpenCV读取图像
    hx_img = cv2.imdecode(hx, 0)
    bg_img = cv2.imdecode(bg, 0)

    # 寻找最佳匹配
    res = cv2.matchTemplate(_tran_canny(hx_img), _tran_canny(bg_img), cv2.TM_CCOEFF_NORMED)
    # 最小值,最大值,并得到最小值, 最大值的索引
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

    top_left = max_loc[0]  # 横坐标
    # 展示圈出来的区域
    # x, y = max_loc  # 获取x,y位置坐标
    #
    # w, h = hx_img.shape[::-1]  # 宽高
    # cv2.rectangle(bg_img, (x, y), (x + w, y + h), (7, 249, 151), 2)
    # show(bg_img)
    # 打印图像的高度和宽度
    # 读取输入图像


    # 获取图像的高度和宽度
    height, width = bg_img.shape
    # print("Image width:", width)
    # print("Image height:", height)
    # 因为原图大小可能和目前页面中图大小不一致
    # 所以需要使用bg_width/width换算得到当前缺口需要移动的距离
    top_left = top_left*(bg_width/width)
    return top_left

得到鼠标需要移动的距离

滑动验证码,最终需要使用鼠标移动,所以需要得到鼠标移动的距离
注意滑块移动长度和缺口移动长度不一定是相等的,但是是正相关的
在这里插入图片描述
最好的情况是,我们知道滑块移动距离和缺口移动距离的准确函数,但是这里我获取不到
这里我采用的方法,是手动收集几个点,然后算一个函数出来用(有大佬可以说说有什么更好的方法吗??(>人<;))

import numpy as np
from scipy import stats

# 提供的数据点
y = np.array([0, 40, 60, 100, 200,335.333])  # 滑块滑动距离
x = np.array([0, 35.0483, 52.3636, 88.1663, 178.12,296.65])  # 缺口滑动距离

# 进行线性回归拟合
slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)

# 打印拟合结果
print("斜率 (a):", slope)
print("截距 (b):", intercept)
print("相关系数 (R-squared):", r_value**2)

这样我们就得到了鼠标需要移动的距离

控制鼠标移动速度

过验证码就是需要尽量模拟人类的行为,人类滑动验证码肯定不会像机器这么快
这里我直接用的网友写的
https://www.cnblogs.com/carl-/p/15761861.html


#有的检测移动速度的 如果匀速移动会被识别出来,来个简单点的 渐进
#这里的总距离就是鼠标移动的距离
def get_track(distance):  # distance为传入的总距离
    # 移动轨迹
    track = []
    # 当前位移
    current = 0
    # 减速阈值
    mid = distance * 4 / 5
    # 计算间隔
    t = 0.2
    # 初速度
    v = 1

    while current < distance:
        if current < mid:
            # 加速度为2
            a = 4
        else:
            # 加速度为-2
            a = -3
        v0 = v
        # 当前速度
        v = v0 + a * t
        # 移动距离
        move = v0 * t + 1 / 2 * a * t * t
        # 当前位移
        current += move
        # 加入轨迹
        track.append(round(move))
    return track

至此我们就可以开始获取token的功能了

获取token

from playwright.sync_api import Playwright, sync_playwright, expect
import cv2
import requests
import numpy as np
import base64

#需要滑动验证码登录
def run(playwright: Playwright) -> str:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()
    page.goto("http://xxxxx/login")
    page.get_by_text("业务中台登录").click()
    page.get_by_placeholder("请输入用户名/电子邮件/移动电话/工号/AD域帐号").click()
    page.get_by_placeholder("请输入用户名/电子邮件/移动电话/工号/AD域帐号").fill("账号")
    page.get_by_placeholder("请输入密码").click()
    page.get_by_placeholder("请输入密码").fill("密码")
    page.get_by_role("button", name="登录").click()
    # 因为没有得到鼠标移动距离和缺口移动距离的准确关系,所以可能会验证失败
    # 所以用while尝试直到验证成功才退出
    while True:
        img1 = page.get_by_role("img").nth(1)
        bg = img1.get_attribute('src')
        bg_box = img1.bounding_box()
        bg_width = bg_box["width"]
        img2 = page.get_by_role("img").nth(2)
        hx = img2.get_attribute('src')
        hx_box = img2.bounding_box()
        # 找到这个元素在当前页面的坐标(这个会返回一个字典里边四个数字)
        box = page.locator(".slider-move-box").bounding_box()
        # 移动鼠标到上边元素的中心(上边四个参数用途来了)
        page.mouse.move(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
        # 按下鼠标
        page.mouse.down()
        # 这里获取到x坐标中心点位置,x这里就是鼠标的开始的位置
        x = box["x"] + box["width"] / 2
        # 得到缺口的滑行长度
        hx_real_len = get_notch_location(hx, bg, bg_width)
        # box_real_len = 1.12754*hx_real_len + 0.33920这个计算参数
        # 就是通过刚刚采样点计算得到的
        box_real_len = 1.12754 * hx_real_len + 0.33920
        tracks = get_track(box_real_len)
        for track in tracks:
            # 循环鼠标按照轨迹移动
            # steps 是控制单次移动速度的比例是1/10 默认是1 相当于 传入的这个距离不管多远0.1秒钟移动完 越大越慢
            page.mouse.move(x + track, 0, steps=5)
            x += track
        # 移动结束鼠标抬起
        page.mouse.up()
        # 鼠标抬起后,要么验证成功,要么验证失败,等待3s查看结果
        # 设置等待时间(以毫秒为单位)
        wait_time_ms = 3000  # 3 秒
        # 等待一段时间后再查找页面元素
        page.wait_for_timeout(wait_time_ms)
        # 获取 <span> 元素,如果出现再试一次,说明验证失败重新验证
        # 如果没有就说明验证成功了
        span_element = page.query_selector(".slider-move-text")
        if span_element:
            # 获取 <span> 元素的文本内容
            span_text = span_element.text_content()

            # 检查文本是否包含特定字符串
            if '再试一次' not in span_text:
                break
            else:
                print("文本 '再试一次' 存在于 <span> 元素中。")
        else:
            print("未找到 .slider-move-text <span> 元素。")
            break


    page.wait_for_load_state('load')
    #这里顺利进入后,点击按钮,获取接口里的token和SESSION
    page.get_by_role("button", name="更多查询条件").click()
    page.get_by_role("button", name="查询", exact=True).click()
    storage = context.storage_state(path="state.json")
    # storage = context.storage_state()
    localStorage_list = storage['origins'][0]["localStorage"]
    token = [i['value'] for i in localStorage_list if i['name'] == 'token'][0]
    session = 'SESSION=' + storage['cookies'][0]['value']
    context.close()
    browser.close()
    return {'token': token, 'session': session}

参考链接

https://www.cnblogs.com/carl-/p/15761861.html
https://cloud.tencent.com/developer/article/1825224

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以使用Spring Boot中的RedisTemplate来集成Redis并获取token。以下是一个简单的示例: 1. 添加Redis依赖 在`pom.xml`文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 配置Redis连接信息 在`application.properties`文件中添加以下配置: ```properties # Redis服务器地址 spring.redis.host=127.0.0.1 # Redis服务器连接端口 spring.redis.port=6379 # Redis服务器连接密码(如果没有设置密码则不需要) spring.redis.password= # 连接池最大连接数(使用负值表示没有限制) spring.redis.jedis.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.jedis.pool.max-wait=-1 # 连接池中的最大空闲连接 spring.redis.jedis.pool.max-idle=8 # 连接池中的最小空闲连接 spring.redis.jedis.pool.min-idle=0 ``` 3. 编写获取token的方法 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @Component public class TokenUtil { @Autowired private RedisTemplate<String, String> redisTemplate; /** * 获取token * @param key 键 * @param value 值 * @param expireTime 过期时间(单位:秒) * @return token */ public String getToken(String key, String value, long expireTime) { String token = redisTemplate.opsForValue().get(key); if (token == null) { // 生成新的token token = UUID.randomUUID().toString().replaceAll("-", ""); redisTemplate.opsForValue().set(key, token, expireTime, TimeUnit.SECONDS); } return token; } } ``` 4. 使用获取token的方法 ```java @RestController public class TokenController { @Autowired private TokenUtil tokenUtil; @GetMapping("/token") public String getToken() { String token = tokenUtil.getToken("user1", "123456", 3600); return token; } } ``` 这里假设用户的账号是`user1`,密码是`123456`,token的过期时间是1小时(3600秒)。在访问`/token`接口时,将自动生成一个新的token或返回已存在的token
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值