当前位置:   article > 正文

fastapi(十七)-安全_fastapi scope

fastapi scope

安全介绍
有许多方式去处理安全性,身份认证和授权。
并且它通常是一个复杂而又困难的话题。
fastapi提供一些工具去更容易的处理安全问题。
先来了解一些小的概念
1、OAuth2
OAuth2是一个规范,定义了几种处理身份验证和授权的方式。
它是一个相当广泛的规范,涵盖了几个复杂的用例。
它包括使用“第三方”进行身份验证的方法。
这就是所有带有“使用Facebook,Google,Twitter,GitHub登录”的系统的基础
2、OAuth 1
有一个OAuth 1,它与OAuth2完全不同,并且更为复杂,因为它直接包含有关如何加密通信的规范。
它现在不是很流行或使用。
OAuth2没有指定如何加密通信,它希望您使用HTTPS为您的应用程序提供服务
3、OpenID
OpenID Connect是另一个基于OAuth2的规范。
它只是扩展了OAuth2,以指定一些在OAuth2中相对模糊的内容,以尝试使其更具互操作性。
例如,Google登录使用OpenID Connect(在下面使用OAuth2)。
但是,Facebook登录不支持OpenID Connect。它具有自己的OAuth2风格。
4、OpenAPI
OpenAPI(以前称为Swagger)是用于构建API(现已成为Linux Foundation的一部分)的开放规范。
FastAPI基于OpenAPI。
这就是使多个自动交互式文档界面,代码生成等成为可能的原因。
OpenAPI具有定义多个安全“方案”的方法。
通过使用它们,您可以利用所有这些基于标准的工具,包括这些交互式文档系统。
OpenAPI定义了以下安全方案:

  • apiKey:特定于应用程序的密钥,可以来自:
    • 查询参数。
    • header。
    • cookie。
  • http:标准的HTTP身份验证系统,包括:
    • bearer:标题Authorization的值Bearer加上令牌。这是从OAuth2继承的。
    • HTTP基本身份验证。
    • HTTP摘要等
  • oauth2:所有OAuth2处理安全性的方式(称为“流程”)。
    • 其中一些流程适合构建OAuth 2.0身份验证提供程序(例如Google,Facebook,Twitter,GitHub等):
      • implicit
      • clientCredentials
      • authorizationCode
    • 但是,有一个特定的“流”可以完美地用于直接在同一应用程序中处理身份验证:
      • password:接下来的几章将介绍此示例。
  • openIdConnect:具有定义自动发现OAuth2身份验证数据的方式。 此自动发现是OpenID Connect规范中定义的内容。

fastapi utilities
fastapi提供一些工具给以上的安全模式在fastapi.security模块中。

第一步
想象一下你的后端api在一个域中,而你的前端在另一个域中,或者在相同域中的不同的路径。
并且你想通过用户名和密码来进行前后端授权。
可以使用OAuth2来构建fastapi。
首先来看看它是怎么工作的。

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")


@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

要想让它运行得先安装python-multipart。这是因为OAuth2使用表单数据来发送用户名密码。
运行后可以直接在docs中进行测试。
在这里插入图片描述
目前你无论填入什么都将不起作用。
password flow
密码流是OAuth2中定义得流中得一种方式,去处理安全以及身份验证。
OAuth2被设计来已确保后端或api能够独立于授权用户得服务。
但是这个例子中fastpi程序将会处理api以及授权。
因此,让我们从简化的角度进行回顾:

  • 用户在前端键入他username和和password,然后点击Enter。
  • 前端(在用户的浏览器中运行)发送一个username和password我们的API在一个特定的URL。
  • API会检查username和password,并以“令牌”作为响应
    • 令牌”只是一个包含一些内容的字符串,我们稍后可以使用它来验证该用户。
    • 通常,令牌设置为在一段时间后过期。
      • 因此,用户稍后将不得不再次登录。
      • 而且,如果令牌被盗,则风险会降低。它不像将永远有效的永久密钥(在大多数情况下)。
  • 前端将该令牌临时存储在某个地方。
  • 用户单击前端以转到前端Web应用程序的另一部分。
  • 前端需要从API中获取更多数据。
    • 但是它需要针对该特定端点的身份验证。
    • 因此,为了通过我们的API进行身份验证,它会发送一个标头Authorization,其值Bearer加上令牌。
    • 如果令牌包含foobar,则Authorization标头的内容为:Bearer foobar。
      OAuth2PasswordBearer
      OAuth2PasswordBearer是fastapi提供得使用Bearer token得password流。
      它是客户端使用username 和password来发送并获得token得类。
      oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
      它并不创建端点路径操作,但是声明客户端得到token得地址。
      然后使用实例作为路径操作得依赖,这个依赖返回得参数是一个str类型得token。

它将会去请求得头部中查找Authorization,验证值是否是Bearer+token并且返回token。
如果头部中没有Authorization,或者值不包括Bearer token,将会返回401.
甚至不用你去检查token是否存在,你可以很确定只要函数被执行了,就能够得到token。
得到当前用户
首先创建用户模型,

class User(BaseModel):
    username: str
    email: Optional[str] = None
    full_name: Optional[str] = None
    disabled: Optional[bool] = None
  • 1
  • 2
  • 3
  • 4
  • 5

创建get_current_user 依赖
get_current_user将会依赖oauth2_scheme。并从中得到token。

async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    return user
  • 1
  • 2
  • 3

get_current_user将会使用一个假的功能函数,以token作为参数并返回user模型。

def fake_decode_token(token):
    return User(
        username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
    )
  • 1
  • 2
  • 3
  • 4

完整代码:

from typing import Optional

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")


class User(BaseModel):
    username: str
    email: Optional[str] = None
    full_name: Optional[str] = None
    disabled: Optional[bool] = None


def fake_decode_token(token):
    return User(
        username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
    )


async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    return user


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

在登陆的路径操作中需要以form格式传入username以及password
scope
客户端还可以传入scope参数,这个参数实际是以空格分隔的多个scope。每一个scope都是string。
他们经常被用于声明特殊的安全权限,eg:
users:read or users:write

OAuth2PasswordRequestForm

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        "hashed_password": "fakehashedsecret",
        "disabled": False,
    },
    "alice": {
        "username": "alice",
        "full_name": "Alice Wonderson",
        "email": "alice@example.com",
        "hashed_password": "fakehashedsecret2",
        "disabled": True,
    },
}

app = FastAPI()


def fake_hash_password(password: str):
    return "fakehashed" + password


oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")


class User(BaseModel):
    username: str
    email: str = None
    full_name: str = None
    disabled: bool = None


class UserInDB(User):
    hashed_password: str


def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)


def fake_decode_token(token):
    # This doesn't provide any security at all
    # Check the next version
    user = get_user(fake_users_db, token)
    return user


async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials",
            headers={"WWW-Authenticate": "Bearer"},
        )
    return user


async def get_current_active_user(current_user: User = Depends(get_current_user)):
    if current_user.disabled:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user


@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user_dict = fake_users_db.get(form_data.username)
    if not user_dict:
        raise HTTPException(status_code=400, detail="Incorrect username or password")
    user = UserInDB(**user_dict)
    hashed_password = fake_hash_password(form_data.password)
    if not hashed_password == user.hashed_password:
        raise HTTPException(status_code=400, detail="Incorrect username or password")

    return {"access_token": user.username, "token_type": "bearer"}


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_active_user)):
    return current_user
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

OAuth2PasswordRequestForm是一个声明以下body内容的类依赖:

  • username
  • password
  • 可选scope
  • 可选grant_type
  • 可选client_id
  • 可选client_secret

OAuth2PasswordBearer 使fastapi知道他是安全模式,所以它会被直接加到openapi中,而OAuth2PasswordRequestForm仅仅是一个类依赖,你也可以直接自己声明表单参数。fastapi提供它仅仅是使用起来更简单。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/849215
推荐阅读
相关标签
  

闽ICP备14008679号