Skip to main content

JWT 身份验证函数和装饰器。专为 In10t 的项目 Apogee 打造

项目描述

ApoJWT

创建该apojwt包的目的是为 In10t 的 Apogee 微服务提供 JWT 支持。这些服务需要在所有端点之间变化的权限层次结构。因此,这个包旨在提供可以附加路由声明的装饰器,以确保在请求标头中发送具有适当权限的有效 JWT。该软件包旨在与 Flask 或 FastAPI 等 API 框架一起使用。


ApoJWT 类

ApoJWT 类具有以下构造函数:

(self, secret: str, exp_period: int=900, iss: str="", asynch: bool=False, server_audience: str="default", algorithm: str="HS256", token_finder=None, permission_formatter=None, exception_handler=None)
"""
Keyword Arguments (those with asterisks are functions):

JWT Validation
    secret: Secret string used to encode and decode the access JWT
    exp_period: Length of time in seconds access tokens should be valid for. Default 900 (15 minutes) 
    iss: Issuer string used for additional security. Default ""
    server_audience: Audience name of the server hosting the HTTP framework. 
        Audience names are typically base address URLs. Ex: https://example.com
    algorithm: The algorithm to use when encoding/decoding. Default HS256
    admin_permission: Optional permission allowing full access to JWTs carrying this permission. USE CAREFULLY
    * token_finder: Function used to retrieve access JWT from http Authorization header. Default None
        Expected Function Structure: (*args, **kwargs) -> str
        NOTE: args and kwargs are the same arguments given to the http request handler 

Framework Configuration
    asynch: Tells ApoJWT to use async decorators instead of the normal. Default False
        (FastAPI needs this True)
    * exception_handler: HTTP error handling function given an HTTP code and message as arguments
        Expected Function Structure: (code: int, exception_type: str, msg: str) -> None

Request Data Utility
    * permission_formatter: String formatting function that is given permission_name as an argument
        Can be used to format request data in the permission name
        Expected Function Structure: (permission: str, *args, **kwargs) -> str
"""


ApoJWT 中的高阶函数


令牌查找器

token_finder 函数必须传递给更高阶的构造函数,以使修饰的令牌验证成功。该函数必须返回 JWT 字符串,该字符串通常可以在带有“授权”键的 HTTP 请求标头中找到。JWT 的标准做法是使用“Bearer”作为前缀。由这个函数来删除这个子字符串。

预期功能结构:(*args, **kwargs) -> str

注意:argskwargs是给 HTTP 请求处理程序的相同参数

例子

request.headers["Authorization"]
>>> 'Bearer <token>'
request.headers["Authorization"].replace("Bearer ", "")
>>> '<token>'
"""Token Finder: used to locate and return the JWT"""
# FastAPI
token_finder = lambda **kwargs: str(kwargs["Authorization"]).replace("Bearer ", "")
ajwt = ("secret", iss="issuer", asynch=True, token_finder=token_finder)
## NOTE: asynch is True for FastAPI

# Flask
token_finder = lambda: request.headers["Authorization"].replace("Bearer ", "")
ajwt = ("secret", iss="issuer", token_finder=token_finder)
## NOTE: asynch defaults to False for Flask

异常处理程序

异常处理程序是可选的,但允许使用正在使用的 HTTP 框架提供的 HTTP 错误响应正确处理修饰验证。

预期功能结构:(code: int, exception_type: str, msg: str, *args, **kwargs) -> None

例子

"""Exception Handler"""
# FastAPI
def exception_handler(code: int, msg: str):
    raise HTTPException(status_code=code, detail=msg)
ajwt = ("secret", iss="issuer", asynch=True, token_finder=..., exception_handler=exception_handler)

# Flask
def exception_handler(code: int, msg: str):
    abort(code, msg)
ajwt = ("secret", iss="issuer", token_finder=..., exception_handler=exception_handler)

权限格式化程序

permission_formatter 是完全可选的,但可用于向只能在请求正文中找到的权限添加附加信息。

预期功能结构:(permission: str, *args, **kwargs) -> str

示例:附加请求中找到的 resource_id

"""Permission Formatter: used to apply additional formatting to permission"""
# FastAPI
def fastapi_permission_formatter(permission_name, *args, **kwargs):
    if "resource_id" in kwargs.keys():
        return f"{permission_name}:{kwargs['resource_id']}"
ajwt = ("secret", iss="issuer", asynch=True, token_finder=..., permission_formatter=fastapi_permission_formatter)

# Flask
def flask_permission_formatter(permission_name):
    if "resource_id" in request.args.keys():
        return f"{permission_name}:{request.args['resource_id']}"
    elif "resource_id" in request.json.keys():
        return f"{permission_name}:{request.json['resource_id']}"
ajwt = ("secret", iss="issuer", asynch=False, token_finder=..., permission_formatter=flask_permission_formatter)


装饰器

装饰器是初始化后 ApoJWT 包的主要用例。它们允许使用一行简单的代码来保护任何端点。

ajwt = ApoJWT(secret, iss, token_finder=lambda: ..., ...)


@ajwt.token_required
"""Validates JWT

Returns 'token_data' and 'token_sub' as kwargs to HTTP handler
"""


@ajwt.permission_required(permission_name: str)
"""Validates JWT and ensures permission_name is among the token permissions

permission_name: a permission string

Returns 'token_data' and 'token_sub' as kwargs to HTTP handler
"""

两个装饰器都返回token_datatoken_sub作为被装饰的 HTTP 处理程序的关键字参数。使用这些参数,存储在 JWT 和 JWT 主题中的附加数据都可以访问。

功能

ajwt = ApoJWT(secret, iss, asynch=..., token_finder=...)

ajwt.create_token(self, sub: str="", permissions: list[str]=[], aud: list[str]=[], data: dict=dict(), refresh_data: dict=dict()):
        """Encodes and returns an access JWT and optionally a refresh JWT

        sub: Subject of the JWT (typically some reference to the user of JWT)
        permissions: List of permissions to assign to token
        aud: List of audiences token should be accepted by
        data: Any additional information that is needed
        refresh_data: If refresh is configured, this additional data is stored with the refresh token

        JWT will contain the following claims:
            - exp: Expiration Time
            - nbf: Not Before Time
            - iss: Issuer
            - aud: Audience
            - iat: Issued At
        """

ajwt.token_data():
"""Retrieves the additional data stored in the JWT payload"""


刷新令牌


ApoJWT 1.5.0 引入了刷新令牌功能。强烈建议使用此功能为应用程序提供额外的安全层。要了解刷新令牌及其好处,请查看这篇 Auth0 文章了解更多信息。ApoJWT 中的刷新功能通过以下函数激活:

ajwt.config_refresh(refresh_secret: str, refresh_exp_period: int=86400, refresh_finder=None):
        """Configures ApoJWT for use with refresh tokens

        refresh_secret: Secret string used to encode and decode the refresh JWT

        refresh_exp_period: Number of seconds for the refresh token to be valid. Default 86400 (1 day)

        refresh_finder: Function used to retrieve the refresh JWT from an http-only cookie. Default None
        """

该函数与必须返回刷新令牌的函数refresh_finder类似。token_finder主要区别在于refresh_finder,在大多数情况下,应该在仅 http 的安全 cookie 而不是 HTTP 授权标头中找到刷新令牌。refresh_finder如果只需要创建刷新令牌而不由 ApoJWT 使用,则也是可选的。

预期refresh_finder功能结构:(*args, **kwargs) -> str

一旦调用并初始化此函数,ApoJWT 就可以处理刷新令牌。

刷新功能

create_token函数现在将返回一个包含访问令牌和刷新令牌的元组

access, refresh = ajwt.create_token(...)

通常,此刷新令牌可以存储在仅 HTTP 的 cookie 中。

从那里,@ajwt.refresh装饰器可以放置在应该发生刷新的任何端点上。装饰器将新的访问令牌作为关键字参数access_token传递给它正在装饰的函数。

# Fast Api
@app.get("/some/endpoint")
@ajwt.refresh
def refresh(access_token):
    return access_token


使用示例


构建 ApoJWT

# FastAPI
def fastapi_permission_formatter(permission_name, *args, **kwargs):
    if "resource_id" in kwargs.keys():
        return f"{permission_name}:{kwargs['resource_id']}"
        
def fastapi_exception_handler(code, msg):
    raise HTTPException(status_code=code, detail=msg)

fastapi_token_finder = lambda **kwargs: str(kwargs["authorization"]).replace("Bearer ", "")
ajwt = (
    "secret", 
    iss="issuer", 
    asynch=True
    token_finder=fastapi_token_finder
    permission_formatter=fastapi_permission_formatter
    exception_handler=fastapi_exception_handler
)
## NOTE: asynch must be True for FastAPI
# Flask
def flask_permission_formatter(permission_name):
    if "resource_id" in request.args.keys():
        return f"{permission_name}:{request.args['resource_id']}"
    elif "resource_id" in request.json.keys():
        return f"{permission_name}:{request.json['resource_id']}"

def flask_exception_handler(code, msg):
    abort(code, msg)
    
flask_token_finder = lambda: request.headers["authorization"].replace("Bearer ", "")
ajwt = (
    "secret", 
    iss="issuer",
    token_finder=flask_token_finder
    permission_formatter=flask_permission_formatter
    exception_handler=flask_exception_handler
)
## NOTE: asynch defaults to False

使用装饰器验证 JWT

# fast api
@app.get("/some/endpoint")
@ajwt.permission_required("some:permission:name"):
...

# flask
@app.route("/some/endpoint", methods=["GET"])
@ajwt.permission_required("some:permission:name"):
...

刷新配置

# fast api
def refresh_finder(refresh_token: Union[str, None] = Cookie(default=None)):
    return refresh_token
ajwt.config_refresh("refresh_secret", refresh_finder=refresh_finder)

# flask
ajwt.config_refresh("refresh_secret", refresh_finder=lambda: request.cookies.get('refresh_token'))
(refresh_secret: str, refresh_exp_period: int=86400, refresh_finder=None)

创建新的 JWT

"""Permissions will be assigned to the new token"""

sub = "user_id_1"
permissions = ["some:permission:name", ...]
data = dict(...=...)
aud = [...]

# NOTE: all arguments are optional
# If refresh is not configured
token = ajwt.create_token(sub=sub, permissions=permissions, aud=aud, data=data)

# If refresh is configured
refresh_data = dict(...=...)
access, refresh = ajwt.create_token(
    sub=sub, 
    permissions=permissions, 
    aud=aud, 
    data=data, 
    refresh_data=refresh_data
)

从 JWT 获取令牌数据和主题

@app....
@ajwt.token_required
def route(token_data: dict, token_subject: str):
    print(token_subject)
    return token_data

项目详情


下载文件

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

源分布

apojwt-1.6.1.tar.gz (11.1 kB 查看哈希

已上传 source

内置分布

apojwt-1.6.1-py3-none-any.whl (9.5 kB 查看哈希

已上传 py3