繁体   English   中英

使用枚举在 FastApi 中保护端点

[英]Secured endpoints in FastApi using enum

在我的应用程序中,我想根据角色(枚举)应用对给定端点的访问。 这一切的工作方式是登录(授权)用户想要访问某些资源或创建新用户等......,然后他的 jwt 令牌被解码,因此我们可以看到他的角色(枚举)。 我将创建 3 个函数(permission_user、permission_admin、permission_manager)来读取用户的角色并基于它来决定是否授予访问权限。 我知道我可以创建 6 个函数(排列),例如 permission_user_and_manager,但我想以更专业的方式解决这个问题。 我想做一些基于:

@app.get("/users") #example endpoint
def fetch_users(is_auth: bool = Depends(permission_admin or permission_manager)
.
.

不幸的是它不起作用,你知道任何解决方案吗?

我会提供该值作为另一个依赖项,如果枚举不是合适的值,它将返回 403。 我希望有一个单独的依赖项来处理实际的身份验证并返回权限的枚举值(例如AuthRole之类的东西)。

def admin_permissions(auth_role: AuthRole = Depends(get_auth_role)):
    if auth_role!= AuthRole.ADMIN:
        raise HTTPException(
            status_code=403, 
            detail="User must be an admin to perform this action"
        )

在端点路由的定义中,您可以将此方法指定为必须在调用发生之前执行的依赖项。 您还可以将其应用于ApiRouter class 以避免重复。

@app.get("/users", dependencies=[Depends(admin_permissions)])
def fetch_users():
    ...

现在,如果admin_permissions依赖项没有引发 403 响应代码,您将只输入fetch_users的主体。

如果你想进一步参数化它,你可以使用高级依赖,它使用 class 实例__call__方法来执行工作。 然后,您可以提供多个可接受的角色,而不仅仅是一个。 这看起来像这样:

class AuthChecker:
    def __init__(self, *roles: AuthRole) -> None:
        self.roles = roles

    def __call__(self, auth_role: AuthRole = Depends(get_auth_role)):
        if auth_role not in self.roles:
            roles_values = " or ".join([role.value for role in self.roles])
            raise HTTPException(
                status_code=403,
                detail=f"User must be in role {roles_values} to perform this action"
            )


@app.get("/users", dependencies=[Depends(AuthChecker(AuthRole.ADMIN, AuthRole.MANAGER))])
def fetch_users():
    return "users"

完整示例:

from enum import Enum
import uvicorn as uvicorn
from fastapi import FastAPI, Header, Depends, HTTPException

app = FastAPI()

class AuthRole(Enum):
    ADMIN = "admin"
    MANAGER = "manager"
    NORMAL = "normal"

def get_auth_role(auth_role: AuthRole = Header()) -> AuthRole:
    return auth_role

class AuthChecker:
    def __init__(self, *roles: AuthRole) -> None:
        self.roles = roles

    def __call__(self, auth_role: AuthRole = Depends(get_auth_role)):
        if auth_role not in self.roles:
            roles_values = " or ".join([role.value for role in self.roles])
            raise HTTPException(
                status_code=403,
                detail=f"User must be in role {roles_values} to perform this action"
            )

@app.get("/admin", dependencies=[Depends(AuthChecker(AuthRole.ADMIN, AuthRole.MANAGER))])
def admin(auth_role: AuthRole = Depends(get_auth_role)):
    return auth_role


if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM