CMDB那些事儿 ------> API认证及数据加密解密

API验证

1、为什么要做API验证?

  - 保证仅是有权限的人才能访问
  - 访问过程中,保证数据安全,库不会被篡改

2、你是如何设计的?

  - 与Tornado 中加密Cookie的原理类似
  - 创建动态KEY:md5(key+time)|time
  - 为防止被黑客截获KEY,利用某人API,操作我后台数据,优化代码加以限制!
  - 此处要:维护一个访问记录表(字典类型),仅当前10秒内访问的所有用户,超时相应用户会删除!
  (解释:为什么要10s,各位看官,如果是每次访问生成的动态KEY我都记录的话,那到后期我这个表得大到什么地步!!!创建他的目的是优化访问权限,而不是增加累赘)
  

  - 实现优化的鬼门三关(仅为娱乐)
    第一关:时间 ------------> 超时无权限
    第二关:算法规则 --------> 数据截获篡改,MD5算法值不一致,滚粗!
    第三关:已访问记录 ------> 俗称人头关,首次访问会记录,同样的KEY再来一次,你当我算法是菜啊!

  - 但是: 如果黑客截获KEY,他的网速比你快无比,这样是不是数据又被截获泄漏了?!怎么解决?--->数据加密!

    别担心加密后,这样他来访问,就是来拉高你的网速的!!!

3、数据加密方法:

  - RSA(公钥私钥) ----> 由于内部有字节限制操作,生成公钥时需要指定bytes位数,同时也不清楚每次发送数据的大小,拘谨很大!
    摒弃吧!我的哥,我们需要的是随心所遇的加密方法!

  - AES(仿微信高级加密) ---> 通过一个16位字节格式(及倍数)的key,对数据进行加密(数据必须是要bytes)一定要注意,此种加密的数据长度必须是16个字节的倍数!
不够则需要在字节数据之后加值凑够倍数,解码的时候,再把凑数的值去掉,获取真正的数据!

4、数据API认证及数据加密代码:

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
"""
    用于数据加密及API加密认证的插件
"""
from Crypto.Cipher import AES
from lib.conf.config import settings

def encrypt(message):
    """
    数据加密
    :param message:
    :return:
    """
    key = settings.DATA_KEY     #获取配置文件中的KEY 注意是16个字节的倍数
    cipher = AES.new(key, AES.MODE_CBC, key)    #根据KEY生成AES对象
    ba_data = bytearray(message,encoding='utf-8') #把要字符串转码成bytearray(bytes格式的数组)
    v1 = len(ba_data)  #计算bytes数据的长度
    v2 = v1 % 16    #对长度取余看是否是16的倍数
    v3 = 16-v2 if v2 != 0 else 16   #v3是追加的长度
    # if v2 == 0:
    #     v3 = 16
    # else:
    #     v3 = 16 - v2
    for i in range(v3):
        ba_data.append(v3) #bytearray只能追加数字,默认把数字转成字节  补齐差位,一般是余数为几就补多少个这个数字(一个数字占一个字节)
    final_data = ba_data.decode("utf-8") #先把数组类型的字节数据转成字符串
    data = final_data.encode("utf-8")  #再把字符串转成字节
    msg = cipher.encrypt(data) # 要加密的字符串,加密后是bytes格式,必须是16个字节或16个字节的倍数;可以对bytes格式的直接加密!
    return msg

def decrypt(msg):
    """
    数据解密
    :param message:
    :return:
    """
    key = settings.DATA_KEY
    cipher = AES.new(key, AES.MODE_CBC, key)  #根据KEY生成AES对象
    result = cipher.decrypt(msg)   #对接收到的数据进行解密  ---->数据还是字节格式
    data = result[0:-result[-1]]   #利用切片的方法获取真实的数据,不取后边凑位拼接的数据
                                # (小技巧:由于补充的是数字,先获取这个数字,然后切片到倒数这个数)
    return str(data,encoding='utf-8') #bytes转成字符串格式


def auth():
    """
    API验证算法
    :return:
    """
    import time
    import hashlib

    ctime = time.time()   #客户端获取当前机器的时间戳
    key = "asdfasdfasdfasdf098712sdfs"  #API认证的KEY(自定义)
    new_key = "%s|%s" %(key,ctime,)   #组成新的字符串
    m = hashlib.md5()   #利用md5算法生成一个不可逆的随机码
    m.update(bytes(new_key,encoding='utf-8'))  #里面是字节数据
    md5_key = m.hexdigest()                    #返回值是字符串类型
    md5_time_key = "%s|%s" %(md5_key,ctime)  #把计算出的随机码与参与计算的时间重新拼接成一个新的字符串,用以API验证
    return md5_time_key
客户端用于加密解密和生成API认证密钥的插件
 def post_asset(self,server_info):
        """
        向API发送数据,POST方式提交,注意数据提交的样式,
        注意API接收方式,发送数据的时候没有key,POST没有key获取不到,而是用request.body
        API加密认证,写到请求头中
        利用AES算法,加密采集到的数据,data方式传值(加密的bytes数据),非json(自动json序列化)
        :param server_info:
        :return:
        """
        # requests.post(settings.API,json=server_info)
        data = encrypt(json.dumps(server_info))
        response = requests.post(
            url=settings.API,
            data=data,
            headers={'OpenKey':auth(),'Content-Type':'application/json'}
        )
        print(response.text) #用于显示服务端返回的信息
    # 发送的数据由字符串变成字节原因:
    # unicode是万国码,至少是用16位表示一个字符串,py3中内存中默认使用4个字节表示一个字符串,真正传输或保存过程中,
    # 会通过字符编码成别的格式,这样就会对占位进行压缩;在网络传输过程中,bytes的占位还会更小,所以传输的数据都是bytes格式。
    # 我们手动的把字符串数据转成bytes格式,就省却了post内部转码的过程,让他直接发送bytes数据。

    # data 方式发送数据,请求头header中加入:'Content-Type':'application/json' 是为了让后台以json的形式发送数据!
客户端用于发送数据的函数
import json
import hashlib
import time

from django.shortcuts import render,redirect,HttpResponse
from Crypto.Cipher import AES
from django.conf import settings

from repository import models
from api.services import Services

def decrypt(msg):
    """
    数据解密
    :param message:
    :return:
    """
    key = settings.DATA_KEY #获取settings中的KEY
    cipher = AES.new(key, AES.MODE_CBC, key)  #根据KEY生成AES对象
    result = cipher.decrypt(msg)   #对接收到的数据进行解密  ---->数据还是字节格式
    data = result[0:-result[-1]]   #利用切片的方法获取真实的数据,不取后边凑位拼接的数据
                                # (小技巧:由于补充的是数字,先获取这个数字,然后切片到倒数这个数)
    return str(data,encoding='utf-8') #bytes转成字符串格式

api_key_record ={}
def asset(request):

    client_md5_time_key = request.META.get("HTTP_OPENKEY")   #从MESA中获取发送的API认证信息
    client_md5_key,client_ctime = client_md5_time_key.split("|")
    client_ctime = float(client_ctime)
    server_time = time.time()

    # ######################### 鬼门三关 ###############################

    #第一关,时间关
    if server_time - client_ctime > 10:
        return HttpResponse("【第一关】小伙咂,别唬我,你都超时了!!!")

    #第二关,规则关
    temp = "%s|%s"%(settings.AUTH_KEY,client_ctime,)
    m = hashlib.md5()
    m.update(bytes(temp,encoding="utf-8"))
    server_md5_key = m.hexdigest()
    if server_md5_key != client_md5_key:
        return HttpResponse("【第二关】小子,你是不是改值啦?改规则可不是好孩子哦!")
    for k in list(api_key_record.keys()):
        v = api_key_record[k]
        if server_time > v:
            del api_key_record[k]

    #第三关,人头关
    if client_md5_time_key in api_key_record:
        return HttpResponse("【第三关】老哥,你慢了!人头已拿走....")
    else:
        api_key_record[client_md5_time_key] = client_ctime + 10

    if server_md5_key != client_md5_key:
        return HttpResponse("认证失败...")


    if request.method == "GET":
        ys = "重要的不能被现在人等看的数据"
        return HttpResponse(ys) #验证不通过!
    else:
        #新资产信息
        server_info = json.loads(decrypt(request.body))
        for k,v in server_info.items(): #用以判断接收到的数据信息
            print(k,v)
        return HttpResponse("收到了!!") #用于验证是否接收到数据
服务端API认证及解密函数

加密解密方法----->参考武sir!

posted @ 2017-08-06 22:34  细雨蓝枫  阅读(122)  评论(0)    收藏  举报