API认证及数据加密

简介: API认证API认证加密过程:客户端和服务端保留一个相同的随机字符串,如:key = 'uiakjsdfasjdf898';将随机字符串key与当前时间戳进行拼接,如:uiakjsdfasjdf898|1540192216171,将拼接生成的新...

API认证

API认证加密过程:

  1. 客户端和服务端保留一个相同的随机字符串,如:key = 'uiakjsdfasjdf898';
  2. 将随机字符串key与当前时间戳进行拼接,如:uiakjsdfasjdf898|1540192216171,将拼接生成的新字符串通过md5()加密;
  3. 客户端将第2步生成的md5值和当前时间戳做为URL参数传给服务端,URL如:http://127.0.0.1:8000/test/?sign=fb7005761539b0d18d130455a9de9914&ctime=1540192216171
  4. 服务端接到客户端传递过来的md5和时间戳后,用服务器端保留的随机字符串key(此字符串与客户端保留的字符串一致)和客户端传过来的时间,并使用与第2步相同的算法生成一个md5值,然后将服务端的md5值与客户端传递过来的md5值做比较,如果相同,则证明此客户端是受信任的;

注意:不要认为至此认证就结束了,此认证机制还存在以下两点漏洞:

  1. 如果客户端在向服务端发起请求时,URL被黑客拿到,黑客也可以利用此URL向服务端发起请求;
    解决思路:在服务端维护一个md5值的字典,将每次认证过的md5值存储起来,下次请求来时判断客户端传过来的md5值是否已经存在,如果存在就拒绝访问;
  2. 就算解决了第1点问题,还有一个问题:客户端在向服务端发起请求时,URL被黑客拿到,如果黑客的请求比受信客户端的请求先到达服务端,那么受信客户端的请求就会被拒;
    解决思路:在服务端接收到请求时也生成一个当前时间戳,并与客户端传递过来的时间戳做比较,如果时间差大与3秒(此时间可根据实际情况自定义),则拒绝访问;

客户端代码 :

import requests
import time
import hashlib

# 生成md5值
def gen_sign(ctime):
    key = 'uiakjsdfasjdf898'
    val = '%s|%s' %(key,ctime,)
    obj = hashlib.md5()
    obj.update(val.encode('utf-8'))
    return obj.hexdigest()

# 客户端发起post请求
ctime = int(time.time() * 1000)
result = requests.post(
    url='http://127.0.0.1:8000/test/',
    params={'sign':gen_sign(ctime),'ctime':ctime},    # url参数
    data='adfasdfasdfasdfasdf'                        # 发送给服务端的数据,
)

print(result.text)
print(result.url)
print(result.ok)

输入结果:

{"status":true,"data":666}
http://127.0.0.1:8000/test/?sign=b5abce59bd5776db5e4a107b403775c5&ctime=1540194227854
True

服务端代码:
定义一个API认证的类,在dispatch()方法中实现API认证,需要经过认证的视图类需要继承这个认证类。

import hashlib
import time
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from utils.security import decrypt

key = 'uiakjsdfasjdf898'

def gen_sign(ctime):
    val = '%s|%s' %(key, ctime,)
    obj = hashlib.md5()
    obj.update(val.encode('utf-8'))
    return obj.hexdigest()

SIGN_RECORD = {}
# 定义一个API认证的类,在`dispatch()`方法中实现API认证,需要经过认证的视图类需要继承这个认证类。
class APIAuthView(APIView):
    def dispatch(self, request, *args, **kwargs):
        client_sign = request.GET.get('sign')  # 客户端签名
        client_ctime = int(request.GET.get('ctime'))  # 客户端时间
        server_time = int(time.time() * 1000)  # 服务端时间

        # 请求时间大于3秒,拒绝访问
        if server_time - client_ctime > 3000:
            return Response({'status': False, 'error': '你在路上的时间太久了'})
        
        # 客户端URL携带的MD5如果已经验证过,则拒绝访问
        if client_sign in SIGN_RECORD:
            return Response({'status': False, 'error': '签名已经被使用过了'})

        # 如果客户端的md5值与服务端的md5值不相同,则说明说请求url被篡改,拒绝访问
        server_sign = gen_sign(client_ctime)
        if server_sign != client_sign:
            return Response({'status': False, 'error': '签名错误'})

        SIGN_RECORD[client_sign] = client_ctime
        return super().dispatch(request, *args, **kwargs)

# 需要经过API认证的视图类需要继承认证类`APIAuthView`
class TestView(APIAuthView):
    def post(self,request):
        print(request.data)
        print(request.url)
        return Response({'status':True,'data':666})

注意:此示例中的服务端代码是一个Django程序,在验证示例时,还需要在urls.py文件是添加如下代码:

from django.contrib import admin
from api.views import TestView
from django.urls import path

urlpatterns = [
   path('admin/', admin.site.urls),
   path('test/',TestView.as_view()),
]


数据加密

数据加密算法这里我用RSA。

安装rsa模块

pip3 install rsa

生成一对公钥和私钥

# ######### 1. 生成公钥私钥 #########
pub_key_obj, priv_key_obj = rsa.newkeys(1024)   # 128 - 11 = 117,只能对117个字符加密
# 公钥字符串
pub_key_str = pub_key_obj.save_pkcs1()
pub_key_code = base64.standard_b64encode(pub_key_str)   # 对公钥再次进行base64编码
print(pub_key_code)
# 私钥字符串
priv_key_str = priv_key_obj.save_pkcs1()
priv_key_code = base64.standard_b64encode(priv_key_str)    # 对私钥再次进行base64编码
print(priv_key_code)

注意:公钥用来对数据进行加密,私钥用对加密后的数据进行解密;所以客户端生的私钥需要发送到服务端并保存起来;

输出结果:

# 公钥字符串 ,字节类型
b'LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dCQUtiTGluemJqMkttb1hOUVRvVlBtV2JzVDVqV3F6cm1scjJRU09HR0o0TnVzM0FFMDhiL0RESHgKaW5BTkN1djRVcVB2M3FlWWJiKzRhR3cvMXhZaTJNekVDL2h1cWQwZXdLMk9ha1Y1aWwvZEpMdlB3SHJLN2IrZQpSQnhZaUoyOUh3QWhUemJEaFcvQjBUaGh0M0dmVThPQjVnWVNhS29MTTJndlpxMURIb1kvQWdNQkFBRT0KLS0tLS1FTkQgUlNBIFBVQkxJQyBLRVktLS0tLQo='

# 私钥字符串 ,字节类型
b'LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlDWUFJQkFBS0JnUUNteTRwODI0OWlwcUZ6VUU2RlQ1bG03RStZMXFzNjVwYTlrRWpoaGllRGJyTndCTlBHCi93d3g4WXB3RFFycitGS2o3OTZubUcyL3VHaHNQOWNXSXRqTXhBdjRicW5kSHNDdGptcEZlWXBmM1NTN3o4QjYKeXUyL25rUWNXSWlkdlI4QUlVODJ3NFZ2d2RFNFliZHhuMVBEZ2VZR0VtaXFDek5vTDJhdFF4NkdQd0lEQVFBQgpBb0dBWjBuVVVNMkdWWWpxb2dZeEdjelpLaXRjZjBFd2VDRWpaL0Jac1k3cUdUSU1YR29nMnpKRjB3Zkl1dXJZCndKZmVWVGJOb3V0NXl5ZmZRbW1sdkwwNE5WZ2FRZFM5eHZSUmtUbkZ5WFZja2x5eTFFSklNQ1JhdXd4U3JadEgKOGRvUC9RSE93dm1IeXVvNDRaT1A4d3o1T1lwRitvSnpWYlZEWW9EdDlCMkJrd0VDUlFEVWFoSGJqTERzTEVkUApyQW4rWEs1UFQrUEFxQjhuSDcvTTBRZ2s3MURoMlpuZzdpZlZiVWhBWE4zVmxBeU5tcytQZTBWbUdvUDBNc1BUCjdEbVdDQldaUk92MWp3STlBTWtGSWppeGNhMW9aRUJJd1cyaFJXVndlN2grYmhHWVhzMk5BZEloOWpZUW85bGgKcGJJRnJ3YnRVQWg0VkVLenROMWFyeVIydk1rekFZcnNVUUpGQUtUVm52LzV3TDIxYXExSCt3VlpsS2JWZnc3ZApLRGVyS3FMZFAyMnlETmtHaktRQkRBWlNaTFFWbk13RnRHd2F5NkV6YnRwYUR6WHNRd3pzam85L3ZJc1E4ZTYvCkFqd2swUWJpZ1VWRHNFSGtNQzhWQ0J0d3A3aFJJdjYveER0Z3hEbVlKZFkxTXJqL29FMjduQ1RoVE9lQ2xaOUIKRkM3RTk4M3FETUVvekdtMDZ2RUNSQ05UaGFTT05aWHVUZnFDRXVtdm9YYTFJcDg1NTdYaXFQU2ZlbXNXYXZmcApFOWJDZy9URUEwa2dzeFd1c0RjVzQ5S2IyVlhWekJrUUJsWWxiRElqdmhCV3czbVUKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K'

数据加密:

# # ######### 2. 加密 #########
def encrypt(pub_key_code,value):
    key_str = base64.standard_b64decode(pub_key_code)
    pk = rsa.PublicKey.load_pkcs1(key_str)
    result = rsa.encrypt(value.encode('utf-8'), pk)
    return result

data = encrypt(pub_key_code,'zff')   # “zff”是要被加密的数据
print(data)
print(len(data))

输出结果:

# 加密后的数据
b'GJy\xfc\x82R\xc6N\xebo_\xdad\xbf\x93\xe8\xb9\xd0y\xee\x0b\x9b\xe6\xad\x9f\x07\xcf4\x7f\x8f\x1b\xb4\xc4f\xa0\x01\xc8z\xf9\xd1\x89\xbd\xc0\x1b\xdfhi\x93\x14\x84\x1d\x15\xaa3y"2,\xeb\x1aP\xda2)\'\x98\x90R\xab\xf3\xfcV:\xf7\x12\xcd\xf2d}\xb4NZ\x021\xd6\xce=\x9e\xdf\x07\x0eD\xbc\xf8\xb9\xcdO\xe1\xb0R\x0e\x1cg\xc0X1"\xab\xcd\x18K\xc2\x9bn\xbb\xda{\xd1\xec\x1d\xbai\'\xef\x86\xa85C'
128

不管多大的数据,加密后的长度都是128个字符;

数据解密

# # ######### 3. 解密 #########
def decrypt(priv_key_code,value):
    key_str = base64.standard_b64decode(priv_key_code)
    pk = rsa.PrivateKey.load_pkcs1(key_str)
    val = rsa.decrypt(value, pk)
    return val

origin = decrypt(priv_key_code,data)
print(origin)

输出结果:

# 解密后数据,字节类型
b'zff'

注意:加密长度为1024字节时,可加密的字符长度为117?117是这么计算得来的:8字节=1字符,1024字节=128字符,加密算法本身会占用11个字符,所以128 - 11 = 117;以此类推:当加密长度为2048字节时,可加密的字符长度为 2048 / 8 - 11 = 245
但是当我们要发送的数据达到几KB,甚至几MB时,怎么办呢?看看接下来的,大数据加密!

大字符串数据加密

对于大字符串加密的思路:

  1. 以加密长度为1024字节为例,将大字符串的数据分割成若干个小字符串,每段字符串长度为117,然后再循环对小字符串加密,然后再将加密后的小字符串拼接成大字符串传给服务端;
  2. 服务端将加密的大字符串分割成每段128个字符长度,进行分段解密,然后再将解密后的小字符串拼接成元始数据。
import rsa
import base64
 

# ######### 1. 生成公钥私钥 #########
pub_key_obj, priv_key_obj = rsa.newkeys(1024) # 128 - 11 = 117
# 公钥字符串
pub_key_str = pub_key_obj.save_pkcs1()
pub_key_code = base64.standard_b64encode(pub_key_str)
print(pub_key_code)
# 私钥字符串
priv_key_str = priv_key_obj.save_pkcs1()
priv_key_code = base64.standard_b64encode(priv_key_str)
print(priv_key_code)

#
# # # ######### 2. 加密 #########
def encrypt(pub_key_code,value):
    key_str = base64.standard_b64decode(pub_key_code)
    pk = rsa.PublicKey.load_pkcs1(key_str)
    value_bytes = value.encode('utf-8')
    data_list = []
    for i in range(0,len(value_bytes),117):
        chunk = value_bytes[i:i+117]
        result = rsa.encrypt(chunk, pk)
        data_list.append(result)

    return b''.join(data_list)

data = encrypt(pub_key_code,'zff'*1000)
print(len(data),data)


# # # ######### 3. 解密 #########
def decrypt(priv_key_code,bytes_value):
    key_str = base64.standard_b64decode(priv_key_code)
    pk = rsa.PrivateKey.load_pkcs1(key_str)
    result = []
    for i in range(0,len(bytes_value),128):
        chunk = bytes_value[i:i+128]
        val = rsa.decrypt(chunk, pk)
        result.append(val)
    return b''.join(result)

origin = decrypt(priv_key_code,data)
origin_str = origin.decode('utf-8')
print(origin_str)

相关文章
|
9月前
|
JSON 监控 前端开发
python对接API二次开发高级实战案例解析:Zabbix API封装类实现获取认证密钥、所有主机组、所有主机、所有监控项和历史数据
python对接API二次开发高级实战案例解析:Zabbix API封装类实现获取认证密钥、所有主机组、所有主机、所有监控项和历史数据
298 0
|
1月前
|
监控 安全 API
短信服务的API秘钥认证如何防止滥用
短信服务的API秘钥认证如何防止滥用
|
4月前
|
Kubernetes API 网络架构
k8s学习-CKS真题-启用API Server认证,禁止匿名访问
k8s学习-CKS真题-启用API Server认证,禁止匿名访问
70 0
|
8月前
|
API 虚拟化
虚拟化——使用postman对ovirt做鉴权认证并调用ovirt的api接口
虚拟化——使用postman对ovirt做鉴权认证并调用ovirt的api接口
|
9月前
|
JSON API 数据安全/隐私保护
【Django学习】(十五)API接口文档平台_项目流程分析_日志器_认证_授权
【Django学习】(十五)API接口文档平台_项目流程分析_日志器_认证_授权
|
9月前
|
前端开发 中间件 API
逐步搭建vs2015的API自带认证调用+跨域调用
逐步搭建vs2015的API自带认证调用+跨域调用
|
9月前
|
API PHP Python
漏刻有时API接口实战开发系列(5):百宝云API认证方式及数据获取的解决方案(php)
漏刻有时API接口实战开发系列(5):百宝云API认证方式及数据获取的解决方案(php)
70 0
|
10月前
|
存储 JSON 监控
有关企业四要素认证API的相关知识
近些年,企业开放API的趋势愈发明显。多数企业选择将其产品或服务通过API向外部提供访问接口,以增强用户体验和促进业务转型。但是,仅仅开放一个开放式API接口是不够的。针对企业级API,专门的认证机制显得尤为重要。在本文中,我们将会介绍有关企业四要素认证API的相关知识。
|
缓存 安全 API
网络平台挑选实名制认证API接口的注意事项
网络平台实名制认证是对用户真实性身份进行的一种查验审核,有助于建立完善可靠的互联网信用基础。对于网络平台运营者来说,身份实名认证除了符合工信部监管政策外,也可以帮助平台方筛选掉部分无效客户,规避一定的风险,净化网络环境。
302 0
网络平台挑选实名制认证API接口的注意事项
|
Java API 开发工具
10:Java人脸识别认证-Java API 实战
10:Java人脸识别认证-Java API 实战
606 0
10:Java人脸识别认证-Java API 实战