ninja-authenticate的文章

发布时间 2023-07-02 15:00:22作者: 花生与酒

从drf切换到django-ninja。

drf是一个传统以model驱动的rest框架,可以对一个模型进行CURD。但fastapi给了我一个提示,现代后端系统不完全是一个rest标准,不是把model当成资源这么简单,会涉及到缓存、nosql,分布式等等。传统rest api应用起来挺麻烦。

django-ninja与fastapi“长得”几乎一样,就是受fastapi启发,但可以与django原生生成整合到一起,即可以获得django的orm, admin的优势,同时还可以获得fastapi的异步,swagger,pydantic的数据校验能力。

 

 

api本身就是一个json_response,对一个dict字典结构进行序列化后返回的过程。ninja类似fastapi做了入参与出参自动解析与校验的工作,可以省下我们一些工作量,另外就是swagger的界面整合,还是router路由等。

当然api工作本身有一个权限与认证的过程,这个相对更加重要。现在前后端分离不可能使用session-auth,这个基本不用了,主要是jwt的token认证机制。

今天我们主要搞搞基本django-ninja的jwt机制。

from ninja.security import HttpBearer


class AuthBearer(HttpBearer):
    def authenticate(self, request, token):
        if token == "supersecret":
            return token

header里需要带 Authorization字段, Bearer TOKEN。

curl -X 'GET' \
  'http://127.0.0.1:8000/api/common/test_auth' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer aaa'

这个HttpBearer是会从header里取出Authorization后面的TOKEN。

这里的auth可以是任意可执行的函数,比如校验ip的黑白名单等。还有api key的场景,无论是在query key,header里或者cookie里,ninja都有一些支持,其实自己实现也不复杂。

simplejwt封装了encode, decode功能。

from datetime import datetime

from simplejwt.jwt import Jwt, decode

SECRET_KEY = '123456'
# token 有效时间 时 分 秒
TOKEN_LIFETIME = 12 * 60 * 60


class JwtUtils:
    @staticmethod
    def get_token(payload):
        time_now = int(datetime.now().timestamp())
        valid_to = time_now + TOKEN_LIFETIME
        jwt = Jwt(secret=SECRET_KEY, payload=payload, valid_to=valid_to)
        return jwt.encode()

    @staticmethod
    def get_payload(token):
        return decode(SECRET_KEY, token)[1]


    @staticmethod
    def is_timeout(payload):
        time_now = int(datetime.now().timestamp())
        if payload['exp'] < time_now:
            return True
        else:
            return False


if __name__ == '__main__':
    token = JwtUtils.get_token({'username': 'admin'})
    print(token)
    payload = JwtUtils.get_payload(token)
    print(payload)
    print(JwtUtils.is_timeout(payload))

本质上,就是把一个字典进行加密,使用同一个SECRET_KEY可以对数据进行解密,取出数据。

使用如下AuthBearer可以给任意一个api请求做jwt校验,token是自动获取的,其实就是从header里的meta取出authorization里的token。然后写自己的检验逻辑即可。若返回False就是一个401的错误。

class AuthBearer(HttpBearer):
    def authenticate(self, request, token):
        payload = JwtUtils.get_payload(token)
        username = payload.get('username')
        print(username)
        # if username == 'admin':
        #    return False
        return True

校验成功之后,类似session机制,需要拿到current_user对象,session验证是把对象保存在session里,而jwt模式下,可以从token里解出user_info。

@router.get("/test_auth", auth=AuthBearer())
def bearer(request):
    user_info = get_user_info_from_token(request)
    return {'message': 'test_auth正常使用', 'user_info': user_info}

get_user_info_form_token的代码如下:

def get_user_info_from_token(request):
    token = request.META.get("HTTP_AUTHORIZATION")
    token = token.split(" ")[1]
    user_info = JwtUtils.get_payload(token)
    return user_info

其实框架只是把一些例行性的功能做了封装。

在熟悉机制之后用比较好,省不少模板代码。但从学习的角度,还是自己从更基础的代码开始实现会更好掌握。

小结:

前后端分享django+ ninja是个不错的选择。