现在我们已经拥有了所有的安全流程,现在让我们使用JWT令牌和安全密码哈希来使应用程序真正安全。
该代码是您可以实际在应用程序中使用的代码,可以在数据库中保存密码哈希,等等。
我们将从上一章开始的地方开始并对其进行递增。

关于JWT

JWT的意思是JSON Web Tokens
这是将JSON对象编码为长而密集的字符串且没有空格的标准。看起来像这样:

  1. eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

它没有加密,因此任何人都可以从内容中恢复信息。但这是签名的。
因此,当您收到发出的令牌时,可以验证您是否实际发出了令牌。
这样,您可以创建一个有效期为1周的令牌。然后,当用户第二天带着令牌返回时,您知道她/他仍在登录到您的系统中。
一周后,令牌将过期,用户将不会获得授权,必须再次登录才能获得新令牌。而且,如果用户(或第三方)试图修改令牌以更改到期时间,则您将能够发现它,因为签名不匹配。
如果要使用JWT令牌并查看其工作原理,请查看https://jwt.io

安装 PyJWT

我们需要安装PyJWT来生成和验证Python中的JWT令牌:

  1. pip install pyjwt

密码hashing

“散列”是指将某些内容(在这种情况下为密码)转换为看起来像乱七八糟的字节序列(只是一个字符串)。
每当您传递完全相同的内容(完全相同的密码)时,您都会得到完全相同的胡言乱语。但是您不能从乱码转换回密码。

为什么使用hasing

如果您的数据库被盗,小偷将没有用户的明文密码,只有哈希值。
因此,小偷将无法尝试在另一个系统中使用该密码(由于许多用户在各处都使用相同的密码,因此很危险)。

安装passlib

PassLib是一个很棒的Python程序包,用于处理密码哈希。
它支持许多安全的哈希算法和实用程序来使用它们。
推荐的算法是“ Bcrypt”。
因此,使用Bcrypt安装PassLib:

  1. pip install passlib[bcrypt]


:::tips 提示
使用passlib,您甚至可以将其配置为能够读取由Django,Flask安全性插件或许多其他工具创建的密码。
因此,您将能够例如与FastAPI应用程序共享数据库中来自Django应用程序的相同数据。或者使用相同的数据库逐渐迁移Django应用程序。
您的用户将可以同时从Django应用程序或FastAPI应用程序登录。 :::

hash和验证密码

passlib导入我们需要的工具。
创建一个PassLib“上下文”。这将用于哈希和验证密码。

:::tips 提示
PassLib上下文还具有使用不同哈希算法的功能,包括仅淘汰旧算法以允许对其进行验证等。
例如,您可以使用它来读取和验证由另一个系统(如Django)生成的密码,但对任何新密码进行哈希处理使用Bcrypt等不同的算法。
并同时与所有它们兼容。 :::

创建一个程序以hash来实现用户的密码。
一个函数用于验证收到的密码是否与存储的哈希匹配。
另一个用于认证并返回用户。

  1. from datetime import datetime, timedelta
  2. import jwt
  3. from fastapi import Depends, FastAPI, HTTPException, status
  4. from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
  5. from jwt import PyJWTError
  6. from passlib.context import CryptContext
  7. from pydantic import BaseModel
  8. # to get a string like this run:
  9. # openssl rand -hex 32
  10. SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
  11. ALGORITHM = "HS256"
  12. ACCESS_TOKEN_EXPIRE_MINUTES = 30
  13. fake_users_db = {
  14. "johndoe": {
  15. "username": "johndoe",
  16. "full_name": "John Doe",
  17. "email": "johndoe@example.com",
  18. "hashed_password": "$2b$12$1OdnpBdoEvcPlA95J40YUeLru4rLN9ViMve8QNnZm5fDYLR9bu3T.",
  19. "disabled": False,
  20. }
  21. }
  22. class Token(BaseModel):
  23. access_token: str
  24. token_type: str
  25. class TokenData(BaseModel):
  26. username: str = None
  27. class User(BaseModel):
  28. username: str
  29. email: str = None
  30. full_name: str = None
  31. disabled: bool = None
  32. class UserInDB(User):
  33. hashed_password: str
  34. pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
  35. oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
  36. app = FastAPI()
  37. def verify_password(plain_password, hashed_password):
  38. return pwd_context.verify(plain_password, hashed_password)
  39. def get_password_hash(password):
  40. return pwd_context.hash(password)
  41. def get_user(db, username: str):
  42. if username in db:
  43. user_dict = db[username]
  44. return UserInDB(**user_dict)
  45. def authenticate_user(fake_db, username: str, password: str):
  46. user = get_user(fake_db, username)
  47. if not user:
  48. return False
  49. if not verify_password(password, user.hashed_password):
  50. return False
  51. return user
  52. def create_access_token(data: dict, expires_delta: timedelta = None):
  53. to_encode = data.copy()
  54. if expires_delta:
  55. expire = datetime.utcnow() + expires_delta
  56. else:
  57. expire = datetime.utcnow() + timedelta(minutes=15)
  58. to_encode.update({"exp": expire})
  59. encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
  60. return encoded_jwt
  61. async def get_current_user(token: str = Depends(oauth2_scheme)):
  62. credentials_exception = HTTPException(
  63. status_code=status.HTTP_401_UNAUTHORIZED,
  64. detail="Could not validate credentials",
  65. headers={"WWW-Authenticate": "Bearer"},
  66. )
  67. try:
  68. payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
  69. username: str = payload.get("sub")
  70. if username is None:
  71. raise credentials_exception
  72. token_data = TokenData(username=username)
  73. except PyJWTError:
  74. raise credentials_exception
  75. user = get_user(fake_users_db, username=token_data.username)
  76. if user is None:
  77. raise credentials_exception
  78. return user
  79. async def get_current_active_user(current_user: User = Depends(get_current_user)):
  80. if current_user.disabled:
  81. raise HTTPException(status_code=400, detail="Inactive user")
  82. return current_user
  83. @app.post("/token", response_model=Token)
  84. async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
  85. user = authenticate_user(fake_users_db, form_data.username, form_data.password)
  86. if not user:
  87. raise HTTPException(
  88. status_code=status.HTTP_401_UNAUTHORIZED,
  89. detail="Incorrect username or password",
  90. headers={"WWW-Authenticate": "Bearer"},
  91. )
  92. access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
  93. access_token = create_access_token(
  94. data={"sub": user.username}, expires_delta=access_token_expires
  95. )
  96. return {"access_token": access_token, "token_type": "bearer"}
  97. @app.get("/users/me/", response_model=User)
  98. async def read_users_me(current_user: User = Depends(get_current_active_user)):
  99. return current_user
  100. @app.get("/users/me/items/")
  101. async def read_own_items(current_user: User = Depends(get_current_active_user)):
  102. return [{"item_id": "Foo", "owner": current_user.username}]

:::tips 注意
如果您检查新的(伪)数据库fakeusersdb,您将看到哈希密码现在的样子:“$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW”。 :::

处理JWT tokens

导入已安装的模块。
创建一个随机密钥,该密钥将用于对JWT令牌进行签名。
要生成安全的随机密钥,请使用以下命令:

  1. openssl rand -hex 32

并将输出复制到变量SECRET_KEY(在示例中不要使用该变量)。
使用用于签名JWT令牌的算法创建变量ALGORITHM,并将其设置为“HS256”。
创建一个令牌到期的变量。
定义将在令牌端点中用于响应的Pydantic模型。
创建一个实用程序函数以生成一个新的访问令牌。

  1. from datetime import datetime, timedelta
  2. import jwt
  3. from fastapi import Depends, FastAPI, HTTPException, status
  4. from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
  5. from jwt import PyJWTError
  6. from passlib.context import CryptContext
  7. from pydantic import BaseModel
  8. # to get a string like this run:
  9. # openssl rand -hex 32
  10. SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
  11. ALGORITHM = "HS256"
  12. ACCESS_TOKEN_EXPIRE_MINUTES = 30
  13. fake_users_db = {
  14. "johndoe": {
  15. "username": "johndoe",
  16. "full_name": "John Doe",
  17. "email": "johndoe@example.com",
  18. "hashed_password": "$2b$12$1OdnpBdoEvcPlA95J40YUeLru4rLN9ViMve8QNnZm5fDYLR9bu3T.",
  19. "disabled": False,
  20. }
  21. }
  22. class Token(BaseModel):
  23. access_token: str
  24. token_type: str
  25. class TokenData(BaseModel):
  26. username: str = None
  27. class User(BaseModel):
  28. username: str
  29. email: str = None
  30. full_name: str = None
  31. disabled: bool = None
  32. class UserInDB(User):
  33. hashed_password: str
  34. pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
  35. oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
  36. app = FastAPI()
  37. def verify_password(plain_password, hashed_password):
  38. return pwd_context.verify(plain_password, hashed_password)
  39. def get_password_hash(password):
  40. return pwd_context.hash(password)
  41. def get_user(db, username: str):
  42. if username in db:
  43. user_dict = db[username]
  44. return UserInDB(**user_dict)
  45. def authenticate_user(fake_db, username: str, password: str):
  46. user = get_user(fake_db, username)
  47. if not user:
  48. return False
  49. if not verify_password(password, user.hashed_password):
  50. return False
  51. return user
  52. def create_access_token(data: dict, expires_delta: timedelta = None):
  53. to_encode = data.copy()
  54. if expires_delta:
  55. expire = datetime.utcnow() + expires_delta
  56. else:
  57. expire = datetime.utcnow() + timedelta(minutes=15)
  58. to_encode.update({"exp": expire})
  59. encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
  60. return encoded_jwt
  61. async def get_current_user(token: str = Depends(oauth2_scheme)):
  62. credentials_exception = HTTPException(
  63. status_code=status.HTTP_401_UNAUTHORIZED,
  64. detail="Could not validate credentials",
  65. headers={"WWW-Authenticate": "Bearer"},
  66. )
  67. try:
  68. payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
  69. username: str = payload.get("sub")
  70. if username is None:
  71. raise credentials_exception
  72. token_data = TokenData(username=username)
  73. except PyJWTError:
  74. raise credentials_exception
  75. user = get_user(fake_users_db, username=token_data.username)
  76. if user is None:
  77. raise credentials_exception
  78. return user
  79. async def get_current_active_user(current_user: User = Depends(get_current_user)):
  80. if current_user.disabled:
  81. raise HTTPException(status_code=400, detail="Inactive user")
  82. return current_user
  83. @app.post("/token", response_model=Token)
  84. async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
  85. user = authenticate_user(fake_users_db, form_data.username, form_data.password)
  86. if not user:
  87. raise HTTPException(
  88. status_code=status.HTTP_401_UNAUTHORIZED,
  89. detail="Incorrect username or password",
  90. headers={"WWW-Authenticate": "Bearer"},
  91. )
  92. access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
  93. access_token = create_access_token(
  94. data={"sub": user.username}, expires_delta=access_token_expires
  95. )
  96. return {"access_token": access_token, "token_type": "bearer"}
  97. @app.get("/users/me/", response_model=User)
  98. async def read_users_me(current_user: User = Depends(get_current_active_user)):
  99. return current_user
  100. @app.get("/users/me/items/")
  101. async def read_own_items(current_user: User = Depends(get_current_active_user)):
  102. return [{"item_id": "Foo", "owner": current_user.username}]

更新依赖

更新get_current_user以接收与以前相同的令牌,但是这次使用JWT令牌。
解码收到的令牌,对其进行验证,然后返回当前用户。
如果令牌无效,请立即返回HTTP错误。

  1. from datetime import datetime, timedelta
  2. from typing import Optional
  3. from fastapi import Depends, FastAPI, HTTPException, status
  4. from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
  5. from jose import JWTError, jwt
  6. from passlib.context import CryptContext
  7. from pydantic import BaseModel
  8. # to get a string like this run:
  9. # openssl rand -hex 32
  10. SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
  11. ALGORITHM = "HS256"
  12. ACCESS_TOKEN_EXPIRE_MINUTES = 30
  13. fake_users_db = {
  14. "johndoe": {
  15. "username": "johndoe",
  16. "full_name": "John Doe",
  17. "email": "johndoe@example.com",
  18. "hashed_password": "$2b$12$1OdnpBdoEvcPlA95J40YUeLru4rLN9ViMve8QNnZm5fDYLR9bu3T.",
  19. "disabled": False,
  20. }
  21. }
  22. class Token(BaseModel):
  23. access_token: str
  24. token_type: str
  25. class TokenData(BaseModel):
  26. username: Optional[str] = None
  27. class User(BaseModel):
  28. username: str
  29. email: Optional[str] = None
  30. full_name: Optional[str] = None
  31. disabled: Optional[bool] = None
  32. class UserInDB(User):
  33. hashed_password: str
  34. pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
  35. oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
  36. app = FastAPI()
  37. def verify_password(plain_password, hashed_password):
  38. return pwd_context.verify(plain_password, hashed_password)
  39. def get_password_hash(password):
  40. return pwd_context.hash(password)
  41. def get_user(db, username: str):
  42. if username in db:
  43. user_dict = db[username]
  44. return UserInDB(**user_dict)
  45. def authenticate_user(fake_db, username: str, password: str):
  46. user = get_user(fake_db, username)
  47. if not user:
  48. return False
  49. if not verify_password(password, user.hashed_password):
  50. return False
  51. return user
  52. def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
  53. to_encode = data.copy()
  54. if expires_delta:
  55. expire = datetime.utcnow() + expires_delta
  56. else:
  57. expire = datetime.utcnow() + timedelta(minutes=15)
  58. to_encode.update({"exp": expire})
  59. encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
  60. return encoded_jwt
  61. async def get_current_user(token: str = Depends(oauth2_scheme)):
  62. credentials_exception = HTTPException(
  63. status_code=status.HTTP_401_UNAUTHORIZED,
  64. detail="Could not validate credentials",
  65. headers={"WWW-Authenticate": "Bearer"},
  66. )
  67. try:
  68. payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
  69. username: str = payload.get("sub")
  70. if username is None:
  71. raise credentials_exception
  72. token_data = TokenData(username=username)
  73. except JWTError:
  74. raise credentials_exception
  75. user = get_user(fake_users_db, username=token_data.username)
  76. if user is None:
  77. raise credentials_exception
  78. return user
  79. async def get_current_active_user(current_user: User = Depends(get_current_user)):
  80. if current_user.disabled:
  81. raise HTTPException(status_code=400, detail="Inactive user")
  82. return current_user
  83. @app.post("/token", response_model=Token)
  84. async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
  85. user = authenticate_user(fake_users_db, form_data.username, form_data.password)
  86. if not user:
  87. raise HTTPException(
  88. status_code=status.HTTP_401_UNAUTHORIZED,
  89. detail="Incorrect username or password",
  90. headers={"WWW-Authenticate": "Bearer"},
  91. )
  92. access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
  93. access_token = create_access_token(
  94. data={"sub": user.username}, expires_delta=access_token_expires
  95. )
  96. return {"access_token": access_token, "token_type": "bearer"}
  97. @app.get("/users/me/", response_model=User)
  98. async def read_users_me(current_user: User = Depends(get_current_active_user)):
  99. return current_user
  100. @app.get("/users/me/items/")
  101. async def read_own_items(current_user: User = Depends(get_current_active_user)):
  102. return [{"item_id": "Foo", "owner": current_user.username}]

更新 /token路径操作

用令牌的到期时间创建一个timedelta。
创建一个真实的JWT访问令牌并返回它。

  1. from datetime import datetime, timedelta
  2. from typing import Optional
  3. from fastapi import Depends, FastAPI, HTTPException, status
  4. from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
  5. from jose import JWTError, jwt
  6. from passlib.context import CryptContext
  7. from pydantic import BaseModel
  8. # to get a string like this run:
  9. # openssl rand -hex 32
  10. SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
  11. ALGORITHM = "HS256"
  12. ACCESS_TOKEN_EXPIRE_MINUTES = 30
  13. fake_users_db = {
  14. "johndoe": {
  15. "username": "johndoe",
  16. "full_name": "John Doe",
  17. "email": "johndoe@example.com",
  18. "hashed_password": "$2b$12$1OdnpBdoEvcPlA95J40YUeLru4rLN9ViMve8QNnZm5fDYLR9bu3T.",
  19. "disabled": False,
  20. }
  21. }
  22. class Token(BaseModel):
  23. access_token: str
  24. token_type: str
  25. class TokenData(BaseModel):
  26. username: Optional[str] = None
  27. class User(BaseModel):
  28. username: str
  29. email: Optional[str] = None
  30. full_name: Optional[str] = None
  31. disabled: Optional[bool] = None
  32. class UserInDB(User):
  33. hashed_password: str
  34. pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
  35. oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
  36. app = FastAPI()
  37. def verify_password(plain_password, hashed_password):
  38. return pwd_context.verify(plain_password, hashed_password)
  39. def get_password_hash(password):
  40. return pwd_context.hash(password)
  41. def get_user(db, username: str):
  42. if username in db:
  43. user_dict = db[username]
  44. return UserInDB(**user_dict)
  45. def authenticate_user(fake_db, username: str, password: str):
  46. user = get_user(fake_db, username)
  47. if not user:
  48. return False
  49. if not verify_password(password, user.hashed_password):
  50. return False
  51. return user
  52. def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
  53. to_encode = data.copy()
  54. if expires_delta:
  55. expire = datetime.utcnow() + expires_delta
  56. else:
  57. expire = datetime.utcnow() + timedelta(minutes=15)
  58. to_encode.update({"exp": expire})
  59. encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
  60. return encoded_jwt
  61. async def get_current_user(token: str = Depends(oauth2_scheme)):
  62. credentials_exception = HTTPException(
  63. status_code=status.HTTP_401_UNAUTHORIZED,
  64. detail="Could not validate credentials",
  65. headers={"WWW-Authenticate": "Bearer"},
  66. )
  67. try:
  68. payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
  69. username: str = payload.get("sub")
  70. if username is None:
  71. raise credentials_exception
  72. token_data = TokenData(username=username)
  73. except JWTError:
  74. raise credentials_exception
  75. user = get_user(fake_users_db, username=token_data.username)
  76. if user is None:
  77. raise credentials_exception
  78. return user
  79. async def get_current_active_user(current_user: User = Depends(get_current_user)):
  80. if current_user.disabled:
  81. raise HTTPException(status_code=400, detail="Inactive user")
  82. return current_user
  83. @app.post("/token", response_model=Token)
  84. async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
  85. user = authenticate_user(fake_users_db, form_data.username, form_data.password)
  86. if not user:
  87. raise HTTPException(
  88. status_code=status.HTTP_401_UNAUTHORIZED,
  89. detail="Incorrect username or password",
  90. headers={"WWW-Authenticate": "Bearer"},
  91. )
  92. access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
  93. access_token = create_access_token(
  94. data={"sub": user.username}, expires_delta=access_token_expires
  95. )
  96. return {"access_token": access_token, "token_type": "bearer"}
  97. @app.get("/users/me/", response_model=User)
  98. async def read_users_me(current_user: User = Depends(get_current_active_user)):
  99. return current_user
  100. @app.get("/users/me/items/")
  101. async def read_own_items(current_user: User = Depends(get_current_active_user)):
  102. return [{"item_id": "Foo", "owner": current_user.username}]

有关JWT“主题”的技术细节 sub

JWT规范说,有一个密钥sub,带有令牌的主题。
您可以选择使用它,但这是您放置用户标识的地方,因此我们在这里使用它。
JWT除了可以识别用户并允许他们直接在您的API上执行操作之外,还可以用于其他用途。
例如,您可以标识“汽车”或“博客文章”。
然后,您可以添加有关该实体的权限,例如“驱动器”(对于汽车)或“编辑”(对于博客)。
然后,您可以将该JWT令牌提供给用户(或机器人),他们甚至可以无需使用JWT令牌就可以拥有帐户来执行这些操作(驾驶汽车或编辑博客文章)。您为此生成的API。
使用这些思想,JWT可以用于更复杂的场景。
在这种情况下,假设其中一些实体具有相同的ID foo(用户foo,汽车foo和博客文章foo)。
因此,为避免ID冲突,在为用户创建JWT令牌时,您可以在sub键值前面加上例如username:。因此,在此示例中,的值sub可能是:username:johndoe
要记住的重要一点是,sub密钥在整个应用程序中应该具有唯一的标识符,并且应该是字符串。

检查它

运行服务器,然后转到文档:http://127.0.0.1:8000/docs
您将看到如下用户界面:
使用密码和JWT凭证进行OAuth2 - 图1
与以前一样对应用程序进行授权。
使用凭据:
用户名:johndoe 密码:secret

:::tips 检查
请注意,代码中没有任何地方是纯文本密码“ secret”,我们只有散列版本。 :::

使用密码和JWT凭证进行OAuth2 - 图2
调用端点/users/me/,您将获得以下响应:

  1. {
  2. "username": "johndoe",
  3. "email": "johndoe@example.com",
  4. "full_name": "John Doe",
  5. "disabled": false
  6. }

使用密码和JWT凭证进行OAuth2 - 图3
如果打开开发人员工具,您将看到发送数据的方式以及仅包含令牌的情况,仅在第一个请求中发送密码以验证用户身份并获取该访问令牌,但随后将不发送密码:
使用密码和JWT凭证进行OAuth2 - 图4

:::tips 注意
请注意标头Authorization,其值以Bearer开头。 :::

带有scope的高级用法

OAuth2具有“作用域”的概念。
您可以使用它们向JWT令牌添加一组特定的权限。
然后,您可以将此令牌直接提供给用户或第三方,以便在一系列限制下与您的API进行交互。
您可以在稍后的《高级用户指南》中了解如何使用它们以及如何将它们集成到FastAPI中。

概括

到目前为止,您可以使用OAuth2和JWT等标准来设置安全的FastAPI应用程序。
在几乎所有处理安全性的框架中,安全性都很快成为一个相当复杂的主题。
许多大大简化了它的软件包必须在数据模型,数据库和可用功能上做出许多妥协。其中一些简化太多的软件包实际上在下面具有安全漏洞。


FastAPI不会对任何数据库,数据模型或工具造成任何损害。
它使您可以灵活地选择最适合您的项目的项目。
而且,您可以直接使用许多维护良好且使用广泛的软件包,例如passlibpyjwt,因为FastAPI不需要任何复杂的机制即可集成外部软件包。
但是它为您提供了在不影响灵活性,健壮性或安全性的前提下,尽可能简化流程的工具。
您可以以相对简单的方式使用和实现安全的标准协议,例如OAuth2。
您可以在高级用户指南中了解更多有关如何使用OAuth2“范围”的信息,以遵循这些相同的标准来获得更细粒度的权限系统。具有范围的OAuth2是许多大型身份验证提供程序使用的机制,例如Facebook,Google,GitHub,Microsoft,Twitter等,用于授权第三方应用程序代表其用户与其API进行交互。