Python API简单验证

前言

因为CMDB内部的需求,需要一个API进行数据传输,用来传递需要抓取的服务端信息信息给抓取的autoclient,autoclient抓取好之后再通过API传输到服务器,保存到数据库。但是为了防止恶意的API访问,需要做一个验证。

设想一

可以在客户端跟服务端都规定好一串随机字符串做验证,只有当带着这串验证的请求发送过来的时候,才让其进行访问。

如果学过了爬虫,大家很容易就发现,这串随机字符串在浏览器里面是可以监听的,多观察几次总是会发现的。而且无论通过如何的方式,只要暴露在外面,都是会被察觉的。此时,就需要对其进行加密。

 

设想二

既然随机字符串在web传输中是明文状态,那我们试着将其转换成密文的,转换下思路,如果每次的请求都是随机字符串配合一串一直变动的值进行md5加密,此时,产生的验证字符串也应该变成动态密文。用什么做动态字符串来配合约定好的字符串进行md5加密呢?既然是动态的,时间戳,是最好的选择。   

此时一切看着都很完美,但是,忽略了一点,无论是不是加密的字符串,http请求的时候都是可以监听到的,所以即使加密,即使不知道怎么加密的,依旧可以直接拿着这串字符串直接进行验证访问。尴尬。。。

设想三

其实上面的设想二已经做到了动态,思路上只要改一点就立刻变成可行的了。在http请求的时候,即使是在相当糟糕的网络环境里,也不会需要发送非常长的时间,因此,卡住时间便可以实现。

在拿到client的时间戳是,服务器段只需要跟当前时间戳进行比对,如果时间间隔小于10秒就当作正常访问。就可以了。

上面的设计思路完美的解决了动态的问题,此时不免还有疑问,如果真的会在10s内盗取到字符串直接访问呢?

完善思路

基于上面的问题,可以再多加异步验证,写一个列表,访问过的字符串都放在列表里,后面的访问都跟此列表比对下,如果在此列表内,就拒绝访问。

如上的设计思路就可以解决问题。

优化

最后的设计思路肯定能解决问题,但是也存在一个问题,就是,随着时间进度的推移,访问列表一定会越来越大,始终是不友好的一点。肯定需要给字符串设计一个超时时间。

visited_list = ['28g12b12128912e2kj|127381237812391', '829312g12be120e102ej12je91|12312984123123',....]

如果以上述的方式取跟系统时间比较当然是一件很费事的工作,占用很多的IO,可以使用redis来轻松实现这个功能。

 CBV通过此类装饰方式实现验证

 验证代码

def api_auth_method(request):
    auth_key = request.META.get('HTTP_AUTH_KEY')
    if not auth_key:
        return False
    sp = auth_key.split('|')
    if len(sp) != 2:
        return False
    encrypt, timestamp = sp
    timestamp = float(timestamp)
    limit_timestamp = time.time() - ASSET_AUTH_TIME
    print(limit_timestamp, timestamp)
    if limit_timestamp > timestamp:
        return False
    ha = hashlib.md5(ASSET_AUTH_KEY.encode('utf-8'))
    ha.update(bytes("%s|%f" % (ASSET_AUTH_KEY, timestamp), encoding='utf-8'))
    result = ha.hexdigest()
    print(result, encrypt)
    if encrypt != result:
        return False

    exist = False
    del_keys = []
    for k, v in enumerate(ENCRYPT_LIST):
        print(k, v)
        m = v['time']
        n = v['encrypt']
        if m < limit_timestamp:
            del_keys.append(k)
            continue
        if n == encrypt:
            exist = True
    for k in del_keys:
        del ENCRYPT_LIST[k]

    if exist:
        return False
    ENCRYPT_LIST.append({'encrypt': encrypt, 'time': timestamp})
    return True


def api_auth(func):
    def inner(request, *args, **kwargs):
        if not api_auth_method(request):
            return JsonResponse({'code': 1001, 'message': 'API授权失败'}, json_dumps_params={'ensure_ascii': False})
        return func(request, *args, **kwargs)

    return inner
auth

猜你喜欢

转载自www.cnblogs.com/wuzdandz/p/9429784.html