跳转至

认证

介绍

Django Ninja 提供了若干工具来帮助你轻松、快速、以标准方式处理认证和授权,而无需研究和学习 所有安全规范.

核心概念是,当你描述一个 API 操作时,你可以定义一个认证对象。

from ninja import NinjaAPI
from ninja.security import django_auth

api = NinjaAPI(csrf=True)


@api.get("/pets", auth=django_auth)
def pets(request):
    return f"Authenticated user {request.auth}"

在这个例子中,如果客户端使用 Django 会话认证(默认是基于 cookie 的),它将只能调用 pets 方法,否则将返回一个 HTTP-401 错误。 如果你只需要授权超级用户,你可以使用 from ninja.security import django_auth_superuser 来代替。

自动 OpenAPI 模式

这里有一个例子,其中客户端为了进行认证需要传递一个头信息:

Authorization: Bearer supersecret

from ninja.security import HttpBearer


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


@api.get("/bearer", auth=AuthBearer())
def bearer(request):
    return {"token": request.auth}

现在访问 http://localhost:8000/api/docs 文档。

Swagger UI Auth

现在,当你点击 Authorize 按钮,你将得到一个输入你的认证令牌的提示。

Swagger UI Auth

当你进行测试调用时,每个请求都会传递授权头信息。

全局认证

如果你需要保护你的 API 的 所有 方法,你可以将 auth 参数传递给 NinjaAPI 构造函数:

from ninja import NinjaAPI, Form
from ninja.security import HttpBearer


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


api = NinjaAPI(auth=GlobalAuth())

# @api.get(...)
# def ...

# @api.post(...)
# def ...

并且,如果您需要否决其中一些方法,可以再次在操作级别通过传递 auth 参数来实现。在这个例子中,对于 /token 操作将禁用身份验证:

from ninja import NinjaAPI, Form
from ninja.security import HttpBearer


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


api = NinjaAPI(auth=GlobalAuth())

# @api.get(...)
# def ...
# @api.post(...)
# def ...


@api.post("/token", auth=None)  # < overriding global auth
def get_token(request, username: str = Form(...), password: str = Form(...)):
    if username == "admin" and password == "giraffethinnknslong":
        return {"token": "supersecret"}

可用的身份验证选项

自定义函数

"auth=" 参数参数接受任何可调用对象。只有当可调用对象返回一个可以 转换为布尔值True的值 时, NinjaAPI 才会传递身份验证。这个返回值将被分配给 request.auth 属性。

def ip_whitelist(request):
    if request.META["REMOTE_ADDR"] == "8.8.8.8":
        return "8.8.8.8"


@api.get("/ipwhitelist", auth=ip_whitelist)
def ipwhitelist(request):
    return f"Authenticated client, IP = {request.auth}"

API 密钥

一些 API 使用 API 密钥进行授权。API 密钥是客户端在进行 API 调用时提供的用于识别自身的令牌。该密钥可以在查询字符串中发送:

GET /something?api_key=abcdef12345

或者作为请求头:

GET /something HTTP/1.1
X-API-Key: abcdef12345

或者作为一个 cookie:

GET /something HTTP/1.1
Cookie: X-API-KEY=abcdef12345

Django Ninja 带有内置类来帮助您处理这些情况。

在 Query 中

from ninja.security import APIKeyQuery
from someapp.models import Client


class ApiKey(APIKeyQuery):
    param_name = "api_key"

    def authenticate(self, request, key):
        try:
            return Client.objects.get(key=key)
        except Client.DoesNotExist:
            pass


api_key = ApiKey()


@api.get("/apikey", auth=api_key)
def apikey(request):
    assert isinstance(request.auth, Client)
    return f"Hello {request.auth}"

在这个例子中,我们从 GET['api_key'] 获取一个令牌,并在数据库中找到与之对应的 Client。 Client 实例将被设置为 request.auth 属性。

注意: param_name 是将被检查的 GET 参数的名称。如果未设置,将使用默认的 "key" 。

from ninja.security import APIKeyHeader


class ApiKey(APIKeyHeader):
    param_name = "X-API-Key"

    def authenticate(self, request, key):
        if key == "supersecret":
            return key


header_key = ApiKey()


@api.get("/headerkey", auth=header_key)
def apikey(request):
    return f"Token = {request.auth}"
from ninja.security import APIKeyCookie


class CookieKey(APIKeyCookie):
    def authenticate(self, request, key):
        if key == "supersecret":
            return key


cookie_key = CookieKey()


@api.get("/cookiekey", auth=cookie_key)
def apikey(request):
    return f"Token = {request.auth}"

HTTP Bearer

from ninja.security import HttpBearer


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


@api.get("/bearer", auth=AuthBearer())
def bearer(request):
    return {"token": request.auth}

HTTP 基本身份验证

from ninja.security import HttpBasicAuth


class BasicAuth(HttpBasicAuth):
    def authenticate(self, request, username, password):
        if username == "admin" and password == "secret":
            return username


@api.get("/basic", auth=BasicAuth())
def basic(request):
    return {"httpuser": request.auth}

多个身份验证器

The auth 参数也允许您传递多个身份验证器:

from ninja.security import APIKeyQuery, APIKeyHeader


class AuthCheck:
    def authenticate(self, request, key):
        if key == "supersecret":
            return key


class QueryKey(AuthCheck, APIKeyQuery):
    pass


class HeaderKey(AuthCheck, APIKeyHeader):
    pass


@api.get("/multiple", auth=[QueryKey(), HeaderKey()])
def multiple(request):
    return f"Token = {request.auth}"

在这种情况下, Django Ninja 将首先检查 API 密钥 GET,如果未设置或无效,将检查 header密钥。 如果两者都无效,它将向响应引发身份验证错误。

路由器身份验证

在路由器上使用 auth 参数, 将身份验证器应用于其中声明的所有操作:

api.add_router("/events/", events_router, auth=BasicAuth())

或者使用路由器构造函数

router = Router(auth=BasicAuth())

自定义异常

引发具有异常处理程序的异常将以与操作相同的方式返回该处理程序的响应:

from ninja import NinjaAPI
from ninja.security import HttpBearer

api = NinjaAPI()

class InvalidToken(Exception):
    pass

@api.exception_handler(InvalidToken)
def on_invalid_token(request, exc):
    return api.create_response(request, {"detail": "Invalid token supplied"}, status=401)

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


@api.get("/bearer", auth=AuthBearer())
def bearer(request):
    return {"token": request.auth}

异步身份验证

Django Ninja 对异步身份验证有基本支持。虽然默认的身份验证类不是异步兼容的,但您仍然可以定义您的自定义异步身份验证可调用对象,并使用 auth 传递它们。

async def async_auth(request):
    ...


@api.get("/pets", auth=async_auth)
def pets(request):
    ...

有关更多信息,请参阅 处理错误

大功告成

继续下一小节 测试

评论


本文总阅读量