利用Python实现某OA系统的自动定位功能


Posted in Python onMay 27, 2020

本文介绍了笔者通过python程序实现某OA系统自动考勤打卡功能及相关逻辑原理的解析。

Github: https://github.com/cahi1l1yn/eChecker

需求分析

疫情期间,笔者所在公司使用某OA系统的考勤功能代替原来的刷脸考勤,结果导致很多人经常忘记打卡,于是笔者寻思着能不能写个程序实现自动考勤,希望实现的主要功能是:指定用户名密码登录和指定时间签到签退,扩展功能是:自定义签到和签退的IP或定位地址。

系统逻辑分析

为了通过python实现上述功能,首先需要人工访问系统进行相关的操作,并抓包分析请求和返回数据,弄清逻辑原理,下面介绍分析过程:

登录

访问OA系统登录页面,点击输入登录信息后截取登录数据包,分析发现登录接口除了验证用户名和密码外,还会验证下图红框所示的cookie和token参数。因此我们需要找到这两个参数值从哪里获取。

利用Python实现某OA系统的自动定位功能

重新访问登录页面并抓取返回包,首先从返回包头部看到了JSESSIONID参数,而另一个lt参数则在返回页面的源码中。

利用Python实现某OA系统的自动定位功能

利用Python实现某OA系统的自动定位功能

弄清楚这两个参数的来源后,我们重新回到登录页面提交登录请求,获取并记录下会话cookie。

考勤

登录账号后,进入考勤模块进行打工并截取数据包,可以看出程序是通过向考勤接口提交参数值为CHECKIN和CHECKOUT的json字符串以实现签到和签退。

利用Python实现某OA系统的自动定位功能

另外可以看到请求包中携带了好几个cookie参数,经过不断的测试排除后,最终确定WEBID、JSESSIONID和ETEAMSID这三个为关键cookie,其余几个都可以忽略。

自定义考勤地址

上述测试过程是PC端的,由于其中并没有涉及到地址的参数,因此转到APP端进行测试。截取APP端的考勤请求包,可以看到checkaddress参数就是考勤定位地址。

利用Python实现某OA系统的自动定位功能

笔者尝试在PC端的考勤请求参数中插入checkaddress,从响应包中可以看出已经成功使用该参数自定义考勤地址进行考勤,同时这里如果再加入经纬度参数的话,即可高度模拟定位考勤。

利用Python实现某OA系统的自动定位功能

值得关注的是,笔者分析发现当考勤请求携带了PC端UA时,服务端会将客户端识别为PC端,此时不会处理checkaddress参数,签到地址就是客户端的真实IP地址。当考勤请求携带移动端UA或者pythonUA时,服务端会将客户端识别为移动端且处理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系统的自动定位功能

利用Python实现某OA系统的自动定位功能

利用Python实现某OA系统的自动定位功能

总结

本文分享了笔者利用python编写某OA系统自动考勤程序的过程,包括对系统逻辑的分析、程序结构的介绍和关键代码的解析等内容。

程序最终实现了用户自定义考勤时间、地址,并自动根据地址获取经纬度(如地址为IP地址则不获取),每天在指定时间以上述自定义信息进行考勤。

注:考勤地址可自定义的漏洞已经上报。

到此这篇关于利用Python实现某OA系统的自动定位功能的文章就介绍到这了,更多相关python实现OA系统自动定位内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python中使用PyQt把网页转换成PDF操作代码实例
Apr 23 Python
python实现图片变亮或者变暗的方法
Jun 01 Python
分享一下Python 开发者节省时间的10个方法
Oct 02 Python
python数字图像处理之骨架提取与分水岭算法
Apr 27 Python
python 将md5转为16字节的方法
May 29 Python
python打印直角三角形与等腰三角形实例代码
Oct 20 Python
Python实现图片添加文字
Nov 26 Python
Python模块/包/库安装的六种方法及区别
Feb 24 Python
python剪切视频与合并视频的实现
Mar 03 Python
Django添加bootstrap框架时无法加载静态文件的解决方式
Mar 27 Python
解决jupyter notebook打不开无反应 浏览器未启动的问题
Apr 10 Python
Python函数必须先定义,后调用说明(函数调用函数例外)
Jun 02 Python
Python中常见的数制转换有哪些
May 27 #Python
Python如何实现邮件功能
May 27 #Python
Python中内建模块collections如何使用
May 27 #Python
Python OpenCV实现测量图片物体宽度
May 27 #Python
Python中socket网络通信是干嘛的
May 27 #Python
Python中SQLite如何使用
May 27 #Python
Pycharm插件(Grep Console)自定义规则输出颜色日志的方法
May 27 #Python
You might like
php获取网页请求状态程序示例
2014/06/17 PHP
php中memcache 基本操作实例
2015/05/17 PHP
常用PHP封装分页工具类
2017/01/14 PHP
phpStudy配置多站点多域名和多端口的方法
2017/09/01 PHP
借用Google的Javascript API Loader来加速你的网站
2009/01/28 Javascript
Javascript Math ceil()、floor()、round()三个函数的区别
2010/03/09 Javascript
JQuery里面的几种选择器 查找满足条件的元素$(&quot;#控件ID&quot;)
2011/08/23 Javascript
jquery数据验证插件(自制,简单,练手)实例代码
2013/10/24 Javascript
jquery实现鼠标拖动图片效果示例代码
2014/01/09 Javascript
浅析jquery ajax异步调用方法中不能给全局变量赋值的原因及解决方法
2014/01/10 Javascript
JS 打印界面的CSS居中代码适用所有浏览器
2014/03/19 Javascript
jQuery的load()方法及其回调函数用法实例
2015/03/25 Javascript
jQuery+HTML5实现图片上传前预览效果
2015/08/20 Javascript
jquery 中toggle的2种用法详解(推荐)
2016/09/02 Javascript
jQuery插件echarts实现的去掉X轴、Y轴和网格线效果示例【附demo源码下载】
2017/03/04 Javascript
详解AngularJS 模块化
2017/06/14 Javascript
微信小程序实现天气预报功能
2018/07/18 Javascript
Node.js 使用axios读写influxDB的方法示例
2018/10/26 Javascript
vue路由守卫,限制前端页面访问权限的例子
2019/11/11 Javascript
Vue 实现简易多行滚动&quot;弹幕&quot;效果
2020/01/02 Javascript
[02:56]DOTA2矮人直升机 英雄基础教程
2013/11/26 DOTA
[01:07:53]RNG vs VG 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
Python的加密模块md5、sha、crypt使用实例
2014/09/28 Python
python2.7的编码问题与解决方法
2016/10/04 Python
CentOS 7 安装python3.7.1的方法及注意事项
2018/11/01 Python
对python pandas读取剪贴板内容的方法详解
2019/01/24 Python
linux环境下Django的安装配置详解
2019/07/22 Python
解决使用export_graphviz可视化树报错的问题
2019/08/09 Python
Python解释器及PyCharm工具安装过程
2020/02/26 Python
Python如何通过百度翻译API实现翻译功能
2020/04/02 Python
欧姆龙医疗欧洲有限公司:Omron Healthcare Europe B.V
2020/06/13 全球购物
校园报刊亭创业计划书
2014/01/02 职场文书
大学毕业生通用自我评价
2014/01/05 职场文书
会计大学生职业生涯规划书范文
2014/01/13 职场文书
Angular性能优化之第三方组件和懒加载技术
2021/05/10 Javascript
Golang bufio详细讲解
2022/04/21 Golang