API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。
Web API接口的访问方式,大概可以分为几类:
1)一个是使用用户令牌,通过Web API接口进行数据访问。这种方式,可以有效识别用户的身份,为用户接口返回用户相关的数据,如包括用户信息维护、密码修改、或者用户联系人等与用户身份相关的数据。
2)一种是使用安全签名进行数据提交。这种方式提交的数据,URL连接的签名参数是经过安全一定规则的加密的,服务器收到数据后也经过同样规则的安全加密,确认数据没有被中途篡改后,再进行数据修改处理。因此我们可以为不同接入方式,如Web/APP/Winfrom等不同接入方式指定不同的加密秘钥,但是秘钥是双方约定的,并不在网络连接上传输,连接传输的一般是这个接入的AppID,服务器通过这个AppID来进行签名参数的加密对比,这种方式,类似微信后台的回调处理机制,它们就是经过这样的处理。
3)一种方式是提供公开的接口调用,不需要传入用户令牌、或者对参数进行加密签名的,这种接口一般较少,只是提供一些很常规的数据显示而已。
下面图示就是这几种接入方式的说明和大概应用场景。
关于API安全认证问题的解决:
上面介绍到的参数,我们提及了几个参数,一个是加密签名字符串,一个是时间戳,一个是随机数,一个是应用接入ID,我们一般的处理规则如下所示。
1)Web API 为各种应用接入,如APP、Web、Winform等接入端分配应用AppID以及通信密钥AppSecret,双方各自存储。
2)接入端在请求Web API接口时需携带以下参数:signature、 timestamp、nonce、appid,签名是根据几个参数和加密秘钥生成。
3) Web API 收到接口调用请求时需先检查传递的签名是否合法,验证后才调用相关接口。
加密签名在服务端(Web API端)的验证流程参考微信的接口的处理方式,处理逻辑如下所示。
1)检查timestamp 与系统时间是否相差在合理时间内,如10分钟。
2)将appSecret、timestamp、nonce三个参数进行字典序排序
3)将三个参数字符串拼接成一个字符串进行SHA1加密
4)加密后的字符串可与signature对比,若匹配则标识该次请求来源于某应用端,请求是合法的。
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web import hashlib access_record = [ ] PID_LIST = [ #相当于通信密钥 'qwe', 'ioui', '234s', ] class MainHandler(tornado.web.RequestHandler): def get(self): import time # 获取url中全部数据 pid = self.get_argument('pid', None) # 获取变量 m5, client_time, i = pid.split('|') server_time = time.time() # 时间超过10s禁止 if server_time > float(client_time) + 10: #超过规定时间访问的不合法 self.write('滚') return # 处理10s内容重复的请求 if pid in access_record: self.write('滚') return access_record.append(pid) pid = PID_LIST[int(i)] ramdom_str = "%s|%s" %(pid, client_time) h = hashlib.md5() h.update(bytes(ramdom_str, encoding='utf-8')) server_m5 = h.hexdigest() # print(m5,server_m5) if m5 == server_m5: self.write("Hello, world") else: self.write('滚') application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
#!/usr/bin/env python # -*- coding:utf-8 -*- import time import requests import hashlib PID = 'qwe' current_time = time.time() ramdom_str = "%s|%s" %(PID, current_time) h = hashlib.md5() h.update(bytes(ramdom_str, encoding='utf-8')) UID = h.hexdigest() q = "%s|%s|0" %(UID, current_time) url = 'http://127.0.0.1:8888/index?pid=%s' % q print(url) ret = requests.get(url) print(ret.text)