跳转到主要内容

基于Starlette的ASGI应用程序和框架的认证后端和助手

项目描述

starlette-auth-toolkit

travis pypi python black

Starlette-based ASGI应用程序和框架的认证后端和助手。

注意:文档正在编写中。在此期间,您可以自由地 阅读源代码

功能

  • 数据库无关。
  • 用户模型无关。
  • 支持密码哈希和哈希迁移。
  • 内置对常见认证流程的支持,包括基本和令牌认证。
  • 支持多个认证后端。
  • 轻松集成到 orm

内容

安装

pip install starlette-auth-toolkit

快速入门

import typing

from starlette.applications import Starlette
from starlette.authentication import requires
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.responses import JSONResponse, PlainTextResponse

from starlette_auth_toolkit.base.backends import BaseBasicAuth
from starlette_auth_toolkit.cryptography import PBKDF2Hasher

# Password hasher
hasher = PBKDF2Hasher()


# Example user model
class User(typing.NamedTuple):
    username: str
    password: str


# Fake user storage
USERS = {
    "alice": User(username="alice", password=hasher.make_sync("alicepwd")),
    "bob": User(username="bob", password=hasher.make_sync("bobpwd")),
}

# Authentication backend
class BasicAuth(BaseBasicAuth):
    async def find_user(self, username: str):
        return USERS.get(username)

    async def verify_password(self, user: User, password: str):
        return await hasher.verify(password, user.password)


# Application

app = Starlette()

app.add_middleware(
    AuthenticationMiddleware,
    backend=BasicAuth(),
    on_error=lambda _, exc: PlainTextResponse(str(exc), status_code=401),
)


@app.route("/protected")
@requires("authenticated")
async def protected(request):
    return JSONResponse({"message": f"Hello, {request.user.username}!"})

将此文件保存为 app.py。然后,假设您已安装 uvicorn,请运行 $ uvicorn app:app 并发出请求

  • 匿名请求
curl -i http://localhost:8000/protected
HTTP/1.1 403 Forbidden
date: Tue, 23 Jul 2019 20:44:52 GMT
server: uvicorn
content-length: 9
content-type: text/plain; charset=utf-8

Forbidden
  • 认证请求
curl -i -u alice:alicepwd http://localhost:8000/protected
HTTP/1.1 200 OK
date: Tue, 23 Jul 2019 20:45:24 GMT
server: uvicorn
content-length: 27
content-type: application/json

{"message":"Hello, alice!"}

对于实际示例,请参见 此处

依赖项

与Starlette一样,starlette-auth-toolkit没有任何硬依赖,但您可以选择安装以下内容

  • passlib - 如果您想使用密码哈希器,则必需。

基本后端

基本后端实现了一个 认证流程,但凭据验证的确切实现留给了您。这意味着您可以选择执行数据库查询、使用环境变量或私有文件等。

这些后端在认证成功时授予一组 作用域

尽管基本后端与用户模型无关,但我们建议您实现由 starlette.authentication.BaseUser 指定的接口(也请参阅 Starlette 认证)。

它们位于 starlette_auth_toolkit.base.backends

BaseBasicAuth

基本实现 基本认证方案

请求头格式

Authorization: Basic {credentials}

其中 {credentials} 指的是 {username}:{password} 的 base64 编码。

示例

# myapp/auth.py
from starlette.authentication import SimpleUser  # or a custom user model
from starlette_auth_toolkit.base.backends import BaseBasicAuth

class BasicAuth(BaseBasicAuth):
    async def verify(self, username: str, password: str):
        # In practice, request the database to find the user associated
        # to `username`, and validate that its password hash matches the
        # given password.
        if (username, password) != ("bob", "s3kr3t"):
            return None
        return SimpleUser(username)

抽象方法

  • async .verify(self, username: str, password: str) -> Optional[BaseUser]

    如果 usernamepassword 有效,则返回相应的用户。否则,返回 None

作用域

  • 已认证

BaseTokenAuth

基础实现令牌认证,是 Bearer 认证方案 的简化版。

请求头格式

Authorization: Token {token}

示例

# myapp/auth.py
from starlette.authentication import SimpleUser  # or a custom user model
from starlette_auth_toolkit.base.backends import BaseTokenAuth

class TokenAuth(BaseTokenAuth):
    async def verify(self, token: str):
        # In practice, request the database to find the token object
        # associated to `token`, and return its associated user.
        if token != "abcd":
            return None
        return SimpleUser("bob")

抽象方法

  • async .verify(self, token: str) -> Optional[BaseUser]

    如果 token 指的是有效令牌,则返回相应的用户。否则,返回 None

作用域

  • 已认证

后端

此处列出的认证后端是现成的实现,位于 backends 模块中,除非另有说明。

contrib.orm.ModelBasicAuth

使用 orm 用户模型实现 BaseBasicAuth 的现成实现。

注意:要使用此后端,必须安装 orm

示例

from starlette.applications import Starlette
from starlette_auth_toolkit.contrib.orm import ModelBasicAuth
from starlette_auth_toolkit.cryptography import PBKDF2Hasher

from myproject.models import User  # DIY

hasher = PBKDF2Hasher()

app = Starlette()
app.add_middleware(
    AuthenticationMiddleware,
    backend=ModelBasicAuth(User, hasher=hasher)
)

参数

  • model (orm.Model() -> orm.Model):用户模型(或用于延迟加载的可调用对象)。
  • hasher (BaseHasher):密码哈希器 — 与用于哈希用户密码的相同。
  • password_field (str,可选):存储在用户对象上的密码哈希字段。默认为 "password"

作用域

  • 已认证

多认证

此后端允许您在应用程序中支持多种认证方法。 MultiAuth 将按顺序尝试使用提供的 backends 进行认证,直到成功(或全部失败)。

注意:如果任何后端因 AuthenticationError 失败(例如,因为提供了某些凭据但它们无效),MultiAuth 将传播异常,并且不会进行进一步的尝试 — 即使稍后的后端可能会成功。

示例

from starlette_auth_toolkit.backends import MultiAuth

from myproject.auth import TokenAuth, BasicAuth  # TODO

# Allow to authenticate using either a token or username/password credentials.
backend = MultiAuth([TokenAuth(), BasicAuth()])

参数

  • backends (List[AuthBackend]):认证后端列表,确定客户端可以使用哪些认证方法进行认证。

作用域

  • 已认证

密码哈希器

此软件包提供基于 PassLib 构建的密码哈希实用程序。

用法

  • 异步:await .make() / await .verify()(哈希和验证在线程池中执行)
import asyncio
from starlette_auth_toolkit.cryptography import PBKDF2Hasher

async def main():
    # Instanciate a hasher:
    hasher = PBKDF2Hasher()

    # Hash a password:
    pwd = await hasher.make("hello")

    # Verify a password against a known hash:
    assert await hasher.verify("hello", pwd)

# Python 3.7+
asyncio.run(main())
  • 阻塞:.make_sync() / .verify_sync()
from starlette_auth_toolkit.cryptography import PBKDF2Hasher

# Instanciate a hasher:
hasher = PBKDF2Hasher()

# Hash a password
pwd = hasher.make_sync("hello")

# Verify a password against a known hash:
assert hasher.verify_sync("hello", pwd)

哈希迁移(高级)

如果您需要更改哈希算法(例如,从 PBKDF2 更改为 Argon2),您通常希望保留对现有哈希的支持,但尽快使用新算法重新哈希。

MultiHasher 就是为此而设计的

from starlette_auth_toolkit.cryptography import Argon2Hasher, PBKDF2Hasher, MultiHasher

hasher = MultiHasher([Argon2Hasher(), PBKDF2Hasher()])

上述 hasher 在哈希新密码时将使用 Argon2,但将能够验证使用 Argon2 或 PBKDF2 创建的哈希。

要检测是否需要重新哈希,请使用 .needs_update()

valid = await hasher.verify(pwd, pwd_hash)

if hasher.needs_update(pwd_hash):
    new_hash = await hasher.make(pwd)
    # TODO: store new hash

# ...

注意:在调用 .verify() 之外的时间调用 .needs_update() 将引发 RuntimeError

可用的哈希器

名称 需要 PassLib 算法
PBKDF2Hasher pbkdf2_sha256
CryptHasher sha256_crypt
BCryptHasher bcrypt bcrypt
Argon2Hasher argon2-cffi argon2
MultiHasher N/A

对于高级用例,请使用 Hasher 并传递 passlib.hash 中列出的其中一个算法。

from starlette_auth_toolkit.cryptography import Hasher

hasher = Hasher(algorithm="pbkdf2_sha512")

在视图中认证

如果您需要在视图中对用户进行认证,即用一对 usernamepassword 交换实际的 user,请使用您的 BasicAuth 后端。

auth = MyBasicAuth()

@app.route("/guard")
async def logs_user_in(request):
    data = await request.json()
    username = data["username"]
    password = data["password"]
    user = await auth.verify(username, password)
    # ...

贡献

想要贡献?太棒了!请务必阅读我们的贡献指南

变更日志

查看CHANGELOG.md

许可

MIT

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源代码分发

starlette-auth-toolkit-0.5.0.tar.gz (13.3 kB 查看哈希值)

上传时间 源代码

构建分发

starlette_auth_toolkit-0.5.0-py3-none-any.whl (11.0 kB 查看哈希值)

上传时间 Python 3

由支持