FastAPI 通过中间件实现JWT鉴权

1、实现login路由白名单获取Token

2、对非白名单的路由进行鉴权

 2.1 检查headers里是否包含鉴权信息

 2.2 检查是否Token过期或非法Token

安装pyjwt包 pip install pyjwt 'passlib[bcrypt]'

实现JWT 生成和解码类

class AuthHandle():

    security=HTTPBearer()
    pwd_context=CryptContext(schemes=['bcrypt'],deprecated='auto')
    secret=settings.JWT_SECRET_KEY

    # 生成Token
    async def encode_token(self,user_id:int)->str:
      payload= {
         'exp': datetime.now()+timedelta(days=0,minutes=120),
         'sub': str(user_id)
      }

      return jwt.encode(payload,self.secret,algorithm='HS256')

    # 验证Token
    async def decode_token(self,token):
        try:
            payload=jwt.decode(token,self.secret,algorithms=['HS256'])
            return payload['sub']
        except jwt.ExpiredSignatureError:
            raise AuthException(message='令牌已过期')
        except jwt.InvalidTokenError:
            raise AuthException(message='无效令牌')


    # 异常类
 class AuthException(Exception):
     def __init__(self,message):
         super().__init__(message)

中间件过滤白名单  WHITE_LIST=['/user/login']

from settings import WHITE_LIST,TOKEN_INVALID_ERROR

class TokenMiddleware(BaseHTTPMiddleware):
    def __init__(self,app):
        super().__init__(app)

    async def dispatch(self,req:Request,call_next):

        # 不在白名单内 提示未授权
        if not req.url.path in WHITE_LIST: 
            # 需要通过JWT鉴权
            if 'Authorization' not in req.headers or not req.headers['Authorization']:
                return Response(content=json.dumps(TOKEN_INVALID_ERROR),status_code=status.HTTP_401_UNAUTHORIZED)
            # 去掉Bearer 
            token=req.headers['Authorization'][7:]
            auth=AuthHandle()
            try:
                await auth.decode_token(token)
            except AuthException as e:
                TOKEN_INVALID_ERROR['msg']=f'{e}'
                return Response(content=json.dumps(TOKEN_INVALID_ERROR),
                                status_code=status.HTTP_401_UNAUTHORIZED
                                )

最终在app.add_middleware(TokenMiddleware)绑定实现

<think>嗯,用户想了解FastAPI如何实现接口。那我得先理清楚的常见方法,然后结合FastAPI的特性来讲解。首先,我应该考虑用户可能的背景,可能是开发者,对Web开发有一定了解,但需要具体的实现步骤。 首先,通常有几种方式:Bearer Token(比如JWT)、OAuth2、API Key等。FastAPI支持这些,尤其是OAuth2和JWT,因为官方文档有详细说明。用户可能需要知道不同的方案以及如何选择。 接下来,我应该分步骤介绍。比如,先讲安装依赖,然后设置安全模块,创建用户模型,实现登录接口,保护路由,处理JWT令牌等。每个步骤需要给出代码示例,这样用户能更直观地理解。 要注意的是,代码中的依赖项是否正确导入,比如OAuth2PasswordBearer和OAuth2PasswordRequestForm。同时,需要提到密码哈希处理,不能明文存储,推荐使用passlib库。这部分可能用户会忽略,所以强调安全性很重要。 另外,JWT的使用需要生成和验证令牌,secret_key和算法是必须的。需要提醒用户保护好secret_key,不要硬编码在代码中,而是使用环境变量。可能用户对JWT的结构不太清楚,可以简要说明一下payload的内容。 在路由保护部分,通过Depends依赖注入get_current_user函数,这样每个需要的路由都可以直接使用。这里要说明依赖项的工作原理,以及如何抛出HTTP异常来处理未认证的情况。 可能用户还会关心错误处理,比如令牌无效或过期时的响应。这时候需要提到HTTPException的使用,返回401状态码和错误信息。 最后,总结一下步骤,并提到扩展点,比如角色限管理,可以使用Scopes或者其他库,比如fastapi-permissions。同时,提醒用户实际部署时的注意事项,比如HTTPS、密钥管理等。 检查一下是否有遗漏的部分,比如是否提到了安装必要的库,代码示例是否有误,语法是否正确。比如,OAuth2PasswordBearer的tokenUrl参数是否正确指向登录路由,JWT的生成和解析是否正确使用HS256算法等。 可能用户还会问及其他方式,比如API Key,但用户的问题主要集中在接口,所以以JWT和OAuth2为主。如果有时间的话,可以简要提及其他方法,但重点放在JWT实现上。 确保回答结构清晰,分步骤说明,代码示例正确,并且用中文口语化表达,避免技术术语过多,但保持准确性。同时,按照系统级指令,数学公式用$...$,但这里可能用不上,但需要检查是否有意外的情况。 总之,整理出一个逻辑清晰的步骤,结合代码示例和关键点说明,帮助用户理解FastAPI如何实现接口。</think>以下分步骤介绍FastAPI实现接口的常见方案及核心实现逻辑: 1. **选择方案** - 常用方案:Bearer Token(JWT)、OAuth2、API Key - 推荐组合:OAuth2 + JWTFastAPI原生支持) 2. **安装依赖** ```bash pip install fastapi[all] python-jose[cryptography] passlib ``` 3. **基础实现JWT示例)** ```python from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from jose import JWTError, jwt from passlib.context import CryptContext # 配置参数 SECRET_KEY = "your-secret-key" # 生产环境应使用环境变量 ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") ``` 4. **用户模型与密码验证** ```python class User: def __init__(self, username: str, hashed_password: str): self.username = username self.hashed_password = hashed_password def verify_password(plain_password, hashed_password): return pwd_context.verify(plain_password, hashed_password) def get_user(username: str): # 此处应查询数据库 fake_user = User(username="test", hashed_password=pwd_context.hash("test")) return fake_user ``` 5. **登录接口实现** ```python from datetime import datetime, timedelta 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) @app.post("/token") async def login(form_data: OAuth2PasswordRequestForm = Depends()): user = get_user(form_data.username) if not user or not verify_password(form_data.password, user.hashed_password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect credentials" ) access_token = create_access_token(data={"sub": user.username}) return {"access_token": access_token, "token_type": "bearer"} ``` 6. **路由保护中间件** ```python async def get_current_user(token: str = Depends(oauth2_scheme)): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials" ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception except JWTError: raise credentials_exception user = get_user(username) if user is None: raise credentials_exception return user @app.get("/protected-route") async def protected_route(current_user: User = Depends(get_current_user)): return {"message": f"Welcome {current_user.username}"} ``` **关键要点说明:** 1. 安全凭证存储 - 密码必须哈希存储(使用bcrypt) - JWT密钥应通过环境变量配置 2. 令牌验证流程 - 客户端在请求头携带`Authorization: Bearer <token>` - 服务端验证签名、过期时间和有效负载 3. 错误处理 - 统一返回标准HTTP 401状态码 - 错误消息避免泄露敏感信息 **扩展方案:** - 角色限控制:在JWT payload中添加roles字段,验证时检查限 - 使用第三方库:`fastapi-jwt-auth`、`authlib`等 - OAuth2完整流程实现:支持第三方登录 **注意事项:** 1. 生产环境必须启用HTTPS 2. JTT令牌有效期不宜过长 3. 敏感操作建议结合二次验证 4. 定期轮换加密密钥 实际部署时建议结合以下安全措施: - 速率限制(Rate Limiting) - CORS严格配置 - 请求签名验证 - 完整的日志审计
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恒云客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值