加密文章,需要的人才能读懂,其他人请忽略
@20-8-31
同事需要补充了# 最新版 ## 自动 OTP
另外获得 token 的方法有更简单的方式,通过 curl + cookie 获得 [未更新到博客上]
@20-7-29
补充使用 yaml 存储数据的方法,这样便于支持登录多个机器。
后续可以优化的地方
- 目前需要每次将软件打开放在左上角位置,这样便于鼠标找到点击位置(其实并不麻烦,只要开机打开一次,后面不要拖动就行,最小化或者被其他界面盖住也没事)。 可以利用pyautogui方法自动定位,但最早尝试过,找不准,如果改进考虑自行写图片匹配算法。[值得探索] – 果然我还是弄出来了,代码在后面。
- 示例程序是需要先运行获得第二个token程序,然后再自动登录,主要是目前目标网页跳转慢(并非程序慢),并且那个token变化时间并不快,有时几个小时都不变。其实完全可以放在一起全自动化登录,但是这样就要等8s左右,我自己这两种方法都用。
文章目录
问题描述
登录跳板机需要 TOTP token(6个动态数字),之前在 跳板机+谷歌验证码 ssh 自动登录 介绍的是需要知道个人的key 才能算出来,但是这里不提供。
并且登录跳板机再登录一个开发机还需要从某个网页上获得另一个 token
问题分析及解决方案
- 自动登录主要借助 expect,主要问题是获得那两个token
- 第一个 6 个数的动态密码,必须要打开一个软件,用鼠标点击才能复制到内存中。 那么解决办法就是设法打开软件,然后操作鼠标,最终实现点击
- 第二个 token 明显需要解析网页,之前还需要模拟登陆。
实际使用注意点
- 第二个 token 不经常变换,而且登陆跳转时间长,因此需要提前获得,并保存下来
- 第一个token,主要是需要定位软件按钮位置,开源的软件提供的方法不可靠,最终我选择通过将软件提前放到左上角,然后记录位置,直接写在程序中。
提供参考代码
第一个 token 获得
代码中有四个变量需要设置,其中 bx, by 用我的也行,因为只是个相对位置。
这也只是个参考代码,理解原理,需要根据自己的习惯,写相应的操作和配置。比如我这里直接把软件拖到左上角,这样这些位置都是固定的,如果软件没有在指定地方出现,可以通过手动移过去。
import time
def p(*info): print(*info)
import os
import pyautogui
dbg=False
#dbg=True
def mv_click(x, y, nc=1):
pyautogui.moveTo(x, y)
time.sleep(0.1)
while nc:
pyautogui.click(clicks=1)
nc-=1
time.sleep(0.1)
ox, oy = pyautogui.position()
# 打开那个软件
os.system("open /Applications/xxxxx.app")
# 注意着四个变量需要自己设置
x, y = 33, 367
bx, by = 923-378, 696-543
mv_click(x, y, 2)
if dbg:
time.sleep(1)
mv_click(x+bx, y+by, 1)
if not dbg:
time.sleep(0.1)
pyautogui.hotkey( "option", "o") # 这是我自己设置的快捷键
else:
time.sleep(1)
mv_click(ox, oy)
第二个 token 获得
解析网页部分需要自行分析,这里只提供最简单的。并且不同的 url 会导致不同的登录方式,比如有的需要进入iframe 再登录。
目前代码直接将 chromedirver 放入后台,这样不利于调试
import time
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
url2='https://xxxx/#/project/7/dtmachine/xxxx/detail'
url1='https://xxx/cas/login?service=https%3A%2F%2Fxxx%2Faccounts%2Flogin%2F%3Fnext%3D%252F'
#url1=url2
c_service = Service('/xxxx/software/chromedriver')
c_service.command_line_args()
c_service.start()
def func_final():
driver.close()
#driver.quit()
#c_service.stop()
exit()
def wait_get_ele(xpath, time):
btn = WebDriverWait(driver, time).until(
EC.presence_of_element_located((By.XPATH, xpath)) )
return btn
# 中间两行是为了隐藏
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
chrome_options.headless = True
driver = webdriver.Chrome(options=chrome_options)
#
driver.get(url1)
#time.sleep(0.5)
#wait_get_ele( "//iframe[@height='600']", 10 )
#driver.switch_to.frame(0)
uname = driver.find_element_by_id("username_sso")
upass = driver.find_element_by_id("password_sso")
uname.send_keys("xxxxxxxx")
upass.send_keys("xxxxxxxx")
#btn_sub = driver.find_element_by_xpath("//input[@class='btn btn-submit btn-block']").click()
#btn_sub.send_keys("\n")
wait_get_ele( "//input[@class='btn btn-submit btn-block']", 10 ).click()
#wait_get_ele( "//span[@class='menu-text']", 10 ).click()
wait_get_ele( "//i[@class='fa fa-plus']", 10 ).click()
driver.get(url2)
#wait_get_ele( "//button[@class='btn btn-xs btn-primary']", 10 ).click()
wait_get_ele( "//button[@class='btn btn-xs btn-info']", 10 ).click() #send_keys("\n") #.click()
time.sleep(0.5)
import pyperclip
text = pyperclip.paste()
with open("data/kml_token.txt", "w") as fp:
fp.write(text)
print(text)
func_final()
最终登录部分
#!/usr/bin/expect -f
#!/bin/bash
trap {
set rows [stty rows]
set cols [stty columns]
stty rows $rows columns $cols < $spawn_out(slave,name)
} WINCH
#set kml [lindex $argv 1]
#set kml $argv
# 就是调用第一个脚本
set path "xxxxx"
set token [exec python $path/scripts/get_6num_use.py ]
set token [exec pbpaste ]
set user_info xxx@xxxx
set password xxxx$token
set kml [ exec cat $path/data/kml_token.txt ]
puts $kml
puts $token
spawn ssh $user_info
expect {
#"yes/no" {send "yes\r"}
"Password: " {send "$password\r"}
}
expect {
"web_server*:~" { send "$kml\r" }
}
# 运行自己的命令
expect {
"*# " { send ". env.sh\r" }
}
interact
后记:
感觉就是为了学习了,如果不懂原理,也不要盲目配了,可能更不方便。
使用 yaml 的方式
第二个 token 获取
需要设置 url,并传参执行那个url,所有数据会保存在 yaml 中
#!/X/anaconda3/bin/python
import time
import os
import sys
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
url1='https://X'
import yaml
n = len(sys.argv)
if n>2:
print("Error augments"); exit()
uid = 1 if n==1 else int(sys.argv[1])
if uid==1:
url2='https://X'
elif uid==2:
url2="https://X"
else:
print("no such uid")
#url1=url2
fyaml="X/data/kml_token.yaml"
if os.path.exists(fyaml):
with open(fyaml) as fp:
db_yaml = yaml.load(fp.read(), Loader=yaml.FullLoader)
if db_yaml is None: db_yaml= {}
else:
db_yaml={ }
c_service = Service('X/chromedriver')
c_service.command_line_args()
c_service.start()
def func_final():
driver.close()
#driver.quit()
#c_service.stop()
exit()
def wait_get_ele(xpath, time):
btn = WebDriverWait(driver, time).until(
EC.presence_of_element_located((By.XPATH, xpath)) )
if btn==None:
print("no such data", xpath)
exit()
return btn
# 中间两行是为了隐藏
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
chrome_options.headless = True
driver = webdriver.Chrome(options=chrome_options)
#
driver.get(url1)
#time.sleep(0.5)
#wait_get_ele( "//iframe[@height='600']", 10 )
#driver.switch_to.frame(0)
uname = driver.find_element_by_id("username_sso")
upass = driver.find_element_by_id("password_sso")
uname.send_keys("X")
upass.send_keys("X")
wait_get_ele( "//input[@class='btn btn-submit btn-block']", 10 ).click()
#wait_get_ele( "//span[@class='menu-text']", 10 ).click()
wait_get_ele( "//i[@class='fa fa-plus']", 10 ).click()
driver.get(url2)
#wait_get_ele( "//button[@class='btn btn-xs btn-primary']", 10 ).click()
wait_get_ele( "//button[@class='btn btn-xs btn-info']", 10 ).click() #send_keys("\n") #.click()
time.sleep(0.5)
import pyperclip
text = pyperclip.paste()
db_yaml[str(uid)] = str(text)
with open(fyaml, "w") as fp:
yaml.dump(db_yaml, fp)
print(text)
func_final()
补充一个脚本用于读取上面的 token
之前说过上面那个脚本会遇到网页跳转慢的问题因此最好先执行保存数据,然后这个本用于被调用读取最终的数据
也需要根据参数获得对应的命令
#!X/python
import os
import sys
import yaml
import pyperclip
def pt_exit(*args):
print(args) ; exit()
n = len(sys.argv)
if n>2:
pt_exit("Error augments")
uid = 1 if n==1 else int(sys.argv[1])
fyaml="X/data/kml_token.yaml"
if os.path.exists(fyaml):
with open(fyaml) as fp:
db_yaml = yaml.load(fp.read(), Loader=yaml.FullLoader)
else:
pt_exit("Error: no such yaml file")
uid = str(uid)
if uid in db_yaml:
text = db_yaml[str(uid)]
else:
pt_exit("Error: no such uid", uid)
pyperclip.copy(text)
print(text)
expect 脚本相应修改
依然需要传参数
set path "X"
set token [exec python $path/scripts/get_6num_use.py ]
set token [exec pbpaste ]
set user_info X
set password X
#set kml [ exec cat $path/data/kml_token.txt ]
# 就是上面那个脚本
set kml [ exec kml_token $uid ]
set token [exec pbpaste ]
替换 pyautogui.locateOnScreen 的方案
以前我的定位都是用的 pyautogui,如 网站表单自动填写 和 问卷星自动填写,智能验证和滑动块
但这次发现它竟然找不到,于是尝试用其他算法实现。
最终发现了 aircv
核心代码如下
img1 = pyautogui.screenshot()
img2 = Image.open('data/3.png')
img1 = cv2.cvtColor(numpy.asarray(img1),cv2.COLOR_RGB2BGR)
img2 = cv2.cvtColor(numpy.asarray(img2),cv2.COLOR_RGB2BGR)
pos = ac.find_template(img1, img2)
cpos = [ int(x) for x in pos['result'] ])
# 到上一步就行了,下面是可视化
cv2.circle(img1, tuple(cpos), 50, (0, 255, 0), 10)
img = cv.resize(img1, (640, 480))
cv2.imshow('find', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
最新版
自动 OTP
jump
注意:
- 设置路径
- 在下面的位置设置你的密码
#!/usr/bin/expect -f
trap {
set rows [stty rows]
set cols [stty columns]
stty rows $rows columns $cols < $spawn_out(slave,name)
} WINCH
set user_info wangpengrui@relay03.kuaishou.com
spawn ssh $user_info
set path "/Users/xxx/chj/tools/" # !!! Change
expect {
#"yes/no" {send "yes\r"}
"Password: " {
set token [exec python $path/scripts/get_6num_use.py ]
set token [exec pbpaste ]
set password !!!yourpassd$token
send "$password\r"
}
}
interact
scripts/get_6num_use.py
注意:
- kim 位置
- kim 快捷键打开
- kim 最好在左上角,记录位置,通过截图工具
import time
def p(*info): print(*info)
import os
import pyautogui
dbg=False
#dbg=True
def mv_click(x, y, nc=1):
pyautogui.moveTo(x, y)
time.sleep(0.1)
while nc:
pyautogui.click(clicks=1)
nc-=1
time.sleep(0.1)
ox, oy = pyautogui.position()
os.system("open /Applications/Kim.app")
x, y = 33, 367
bx, by = 923-378, 696-543
mv_click(x, y, 2)
if dbg:
time.sleep(1)
mv_click(x+bx, y+by, 1)
if not dbg:
time.sleep(0.1)
pyautogui.hotkey( "option", "o")
else:
time.sleep(1)
mv_click(ox, oy)