API安全大揭秘:认证与授权的双面舞会
title: API安全大揭秘:认证与授权的双面舞会


+--------+ +---------------+
| |--(A) 授权请求 ->---------------| 资源所有者 |
| | | (用户) |
| |<-(B) 授权许可 ----------------| |
| | +---------------+
| |
| | +---------------+
| |--(C) 授权许可 ->---------------| 授权服务器 |
| 客户端 | | (签发令牌) |
| |<-(D) 访问令牌 ----------------| |
| | +---------------+
| |
| | +---------------+
| |--(E) 访问令牌 ->---------------| 资源服务器 |
| | | (存储数据) |
| |<-(F) 受保护资源 --------------| |
+--------+ +---------------+
# 安装依赖库(指定版本保证兼容性)
pip install fastapi==0.68.0 uvicorn==0.15.0
pip install python-jose[cryptography]==3.3.0
pip install passlib[bcrypt]==1.7.4
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel
from datetime import datetime, timedelta
# 安全配置常量
SECRET_KEY = "your-secret-key-here"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# OAuth2方案声明
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# 用户数据模型
class User(BaseModel):
username: str
disabled: bool = False
class UserInDB(User):
hashed_password: str
# 令牌生成函数
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
# 认证依赖项
async def get_current_user(token: str = Depends(oauth2_scheme)):
credential_exception = HTTPException(
status_code=401,
detail="无法验证凭证",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credential_exception
except JWTError:
raise credential_exception
# 实际项目应查询数据库
user = UserInDB(
username=username,
hashed_password="fakehash",
disabled=False
)
if user.disabled:
raise HTTPException(status_code=400, detail="用户已被禁用")
return user
app = FastAPI()
@app.post("/token")
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
# 验证用户名密码(示例硬编码)
if form_data.username != "testuser" or form_data.password != "testpass":
raise HTTPException(
status_code=401,
detail="用户名或密码错误",
headers={"WWW-Authenticate": "Bearer"},
)
access_token = create_access_token(
data={"sub": form_data.username}
)
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/protected/")
async def read_protected_route(current_user: User = Depends(get_current_user)):
return {"message": "已授权访问", "user": current_user.username}
uvicorn main:app --reload
点击查看答案 正确答案:A 解析:认证是验证用户身份的过程(如登录),授权是验证该身份是否有权限执行特定操作(如访问管理员接口)。
点击查看答案 正确答案:B 解析:完整的授权码流程需要通过授权服务器中转授权码,避免客户端直接接触用户凭证。
{
"detail": [
{
"loc": [
"body",
"password"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
{
"username": "testuser",
"password": "testpass"
}
class LoginRequest(BaseModel):
username: str
password: str # 必填字段
from pydantic import StrictStr
class LoginRequest(BaseModel):
username: StrictStr
password: StrictStr
评论
发表评论