Python实现OA系统自动考勤与定位绕过技术分析
本文详细解析了使用Python实现某OA系统自动考勤及自定义定位的技术流程。通过抓包分析登录与考勤接口,提取JSESSIONID、lt等关键参数,结合Cookie维持会话。文章展示了如何通过修改User-Agent及提交checkAddress、经纬度等参数,实现指定时间签到、签退及IP/地理位置自定义,并提供了核心代码逻辑说明。
Python实现OA系统自动考勤与定位绕过技术分析
本文分享了作者利用Python程序实现某OA系统自动考勤打卡功能的具体实践,并深入解析了背后的技术原理。
项目源码地址:https://github.com/cahi1l1yn/eChecker
需求背景分析
需求背景分析
疫情期间,作者所在公司改用某OA系统进行考勤管理,取代原有的刷脸设备。由于部分员工经常忘记打卡,作者决定开发一款自动化工具来解决这一问题。核心需求包括:使用指定用户名和密码登录系统,并在设定时间自动完成签到和签退。此外,还希望具备扩展功能,支持自定义签到时的IP地址或地理定位信息。
系统接口逻辑分析
系统接口逻辑分析
为了实现上述Python自动化功能,首先需要人工操作OA系统,通过抓包工具截取请求与响应数据,从而理清系统的交互逻辑。以下是具体的分析步骤:
登录接口分析
访问OA系统的登录页面,输入账号密码后截取登录数据包。分析发现,登录接口除了校验用户名和密码外,还会验证图片中红框标注的cookie和token参数。因此,关键在于找到这两个参数的获取来源。

再次访问登录页面并抓取返回数据包,从响应头中发现了JSESSIONID参数,而另一个lt参数则存在于返回页面的HTML源码中。


明确了这两个参数的来源后,重新向登录页面提交登录请求,获取并保存会话相关的cookie信息。
考勤接口分析
考勤接口分析
登录账号后,进入考勤模块进行操作并截取数据包。可以看出,系统通过向考勤接口提交包含CHECKIN(签到)和CHECKOUT(签退)类型值的JSON字符串来执行考勤操作。

此外,请求包中携带了多个cookie参数。经过多次测试与排除,最终确定WEBID、JSESSIONID和ETEAMSID这三个cookie为关键参数,其余cookie可忽略。
自定义考勤地址实现
自定义考勤地址实现
上述测试基于PC端,由于其中未涉及地址参数,因此转向APP端进行测试。截取APP端的考勤请求包,发现checkaddress参数即为考勤定位地址。

尝试在PC端考勤请求参数中插入checkaddress,从响应包可知该参数已成功被系统识别并用于自定义考勤地址。若在此基础上进一步加入经纬度参数,即可高度模拟真实的定位考勤效果。

值得注意的是,分析发现当考勤请求携带PC端User-Agent(UA)时,服务端会将客户端识别为PC端,此时不会处理checkaddress参数,签到地址直接记录为客户端的真实IP地址。而当请求携带移动端UA或Python默认UA时,服务端将其识别为移动端并处理checkaddress参数,从而实现自定义考勤地址(包括IP地址和地理位置)的功能。
整体流程梳理
整体流程梳理
通过上述分析,已明确登录接口和考勤接口的逻辑及请求格式。以下梳理相关流程,这也是后续编写程序的主要逻辑依据:
1.【用户访问登录页面】 || \/ 2.【登录页面返回一个cookie(JSESSIONID)和token(lt)】 || \/ 3.【用户携带cookie像登录接口提交token、用户名和密码】 || \/ 4.【登录接口验证成功后返回会话cookie(ETEAMSID\JSESSIONID\)】 || \/ 5.【用户携带会话cookie向考勤接口提交签到/签退请求】
功能实现方案
功能实现方案
回顾本程序的需求:指定用户名密码登录,并在指定时间进行签到和签退。基于前述逻辑梳理,已能实现指定用户登录及考勤操作,接下来需加入指定时间控制功能。再次梳理Python程序的主要逻辑如下:
1.【输入用户名、密码、签到签退时间运行程序】 || \/ 2.【登录系统获取会话cookie】 || \/ 3.【程序获取本地时间】 || \/ 4.【程序比对本地时间和用户设定时间】 || \/ 5.【在指定时间携带会话cookie进行考勤】
程序结构设计
程序结构设计
明确主要功能逻辑后,开始定义函数以分别实现各项功能。以下是程序的主要函数结构列表:
def get_cookie(user,passwd):登录系统,获取会话cookie,该函数实现了[逻辑梳理]中的第2-4步
def keep_session():维持会话cookie有效性,因cookie长期不活跃会失效,因此通过此函数访问系统以维持cookie,如果cookie已经失效,则会调用get_cookie函数重新登录获取cookie
def check_in():签到模块,携带cookie向考勤接口提交CHECKIN
def check_out():签退模块,携带cookie向考勤接口提交CHECKOUT
def get_position():定位模块,根据用户输入的地理位置获取经纬度
def check_time():获取本地时间并于用户设定时间作比对,触发考勤模块和会话维持模块
def main():程序入口函数,获取用户输入
关键代码解析
关键代码解析
基于上述介绍,已大致了解程序运行逻辑。以下对部分关键代码进行解析(常规代码已省略):
def get_cookie(user,passwd):
...........
token = re.search(r'LT\S+cn',html).group()
#urllib访问登录页面后,从页面中获取lt参数值,即token
pcookie = re.search(r'JSESSIONID=\S+',str(pres.info().headers)).group()
#访问登录页面后,从返回包头部中获取cookie,后续提交登录请求时需要携带该cookie
data ='lt='+token+'&execution=e1.2&j_pcClient=&_eventId=submit&isApplyed=false®isterSourceUrl=®isterSource=®isterDataSource=&username='+user+'&password='+passwd
#组合token和用户输入的登录信息,用于组成登录请求
req = urllib2.Request(lurl)
cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
#通过cookiejar记录登录成功后页面返回的会话cookie
opener.addheaders = [('Cookie',pcookie)]
#在登录请求包头中加入一开始获取到的cookie
try:
res = opener.open(lurl,data=data,timeout=10)
except urllib2.URLError:
print '[ERROR]Urlllib error, retry later'
try:
cookie = re.search(r'ETEAMSID=\w+',str(cj)).group()+';'+re.search(r'JSESSIONID=\w+',str(cj)).group()+';'+re.search(r'WEBID=\w+',str(cj)).group()
#到这一步已经登录成功,cookiejar已经记录了会话cookie,记录形式是这样的:
<CookieJar[<Cookie BIDUPSID=B681378758CB3586029EBFFFF16FBDE2 for .baidu.com/>, <Cookie PSTM=1532404690 for .baidu.com/>, <Cookie BD_NOT_HTTPS=1 for www.baidu.com/>]>
因此这里利用正则匹配出我们所需要的3个cookie值
print '[INFO]Login succeed, your cookie is:'+cookie
...........
def check_in():
............
req = urllib2.Request(curl)
req.add_header("Cookie",cookie)
req.add_header("Content-Type","application/json")
if stat == '0':
data = json.dumps({"type":"CHECKOUT","checkAddress":addr,"longitude":longi,'latitude':lati})
#当用户自定义了考勤地址时,且成功获取到经纬度信息时,提交的请求中加入了地理位置和经纬度参数,服务端默认将urllib的UA识别为移动端,故会记录用户提交的地理信息,完美模拟定位考勤效果
elif stat == '1':
data = json.dumps({"type":"CHECKOUT","checkAddress":addr})
#当用户自定义了考勤地址时,但未成功获取到经纬度信息时,提交的请求中只加入地理位
elif stat =='2':
req.add_header('User-Agent',ua)
data = json.dumps({"type":"CHECKOUT"})
#当用户未自定义考勤地址时,提交的请求按PC端原始格式,且此处需要加入自定义的PC端UA,否则服务端会将签到地址记录为空值
try:
res = urllib2.urlopen(req,data=data,timeout=5).read()
smsg = res.find('签到成功')
fmsg = res.find('签到失败')
if smsg > -1:
print '[INFO]'+time.strftime('%Y-%m-%d_%H:%M',time.localtime())+' Checkin succeed'
elif fmsg > -1:
print '[WARNING]'+time.strftime('%Y-%m-%d_%H:%M',time.localtime())+' Checkin fail:'+res
#以上代码通过在返回报文中查找成功和失败的字符,作为考勤是否成功的判断依据,并输出到终端提示用户
..........
def check_time():
while True:
ltime = time.strftime('%H:%M',time.localtime()).lstrip('0')
day = time.strftime('%a',time.localtime())
#获取当前的时间,lstrip去0是为了时针为0-9的个位数时进行格式统一
..........
if ltime == '4:30':
keep_session()
time.sleep(60)
#由于会话cookie在一定时间后(貌似是十几个小时)会失效,因此设定在凌晨调用keepsession()维持cookie
elif ltime == intime.lstrip('0') and day not in ('Sat','Sun'):
#比对本地时间与用户输入时间,且判断是否周末
keep_session()
#进行考勤前,再次检验cookie是否有效
rnd = random.randint(0,600)
print '[INFO]Checkin after ' + str(int(rnd)/60) + ' Min ' + str(int(rnd)%60) + ' Sec'
time.sleep(int(rnd))
check_in()
#为了避免用户设定一个时间后,程序每天都在同一时间点考勤,这里结合sleep和random实现在用户设定时间上正向浮动随机时间进行考勤
time.sleep(60)
........
........
check_time()
def get_position(addr):
global longi
global lati
url = 'http://api.map.baidu.com/geocoding/v3/?address='+addr+'&output=json&ak='+api_key+'&callback=showLocation'
html = urllib2.urlopen(url.encode('utf-8')).read()
longi = re.search(r'lng":\d+.\d+',html).group().lstrip('lng":')
lati = re.search(r'lat":\d+.\d+',html).group().lstrip('lat":')
#调用百度地图API获取经纬度信息,使用encode('utf-8')处理url可以避免中文乱码问题(需要注册APIKEY)
运行效果展示
运行效果展示



总结
总结
本文分享了利用Python编写某OA系统自动考勤程序的全过程,涵盖系统逻辑分析、程序结构介绍及关键代码解析等内容。
程序最终实现了用户自定义考勤时间和地址,并自动根据地址获取经纬度(若地址为IP则不获取经纬度),每天在指定时间以上述自定义信息进行考勤操作。
注:考勤地址可自定义的漏洞已向相关部门上报。
到此这篇关于利用Python实现某OA系统的自动定位功能的文章就介绍到这了,更多相关python实现OA系统自动定位内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









