fastapi之oauth2

425 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29,点击查看活动详情

  • 上一篇上我们回顾了登录认证的几种当时,目前主流的方式是jwt
  • fastapi也提供了一些jwt认证的工具,比如我们可以使用fastapi提供的工具在openapi中使用
  • 下面我们就详细介绍下如何使用
 from fastapi import FastAPI
 ​
 app = FastAPI(title="XXX项目文档")
 ​
 ​
 books = [
     {
         "title": "平凡的世界",
         "author": "路遥"
     },
     {
         "title": "西游记",
         "author": "吴承恩"
     },
 ]
 ​
 ​
 @app.get("/books")
 def get_books_list():
     return books
 ​
  • 上面的简单例子中定义了一个/books接口,直接访问该接口,就会返回我们模拟的数据
  • 现在需求来了,我们希望用户登录后,访问该接口才有数据返回;没有登录的情况下不返回数据
  • 第一步,我们先实现一个简单的登录接口,要求用户输入用户名和密码,校验通过后给用户下发jwt_token
 from typing import Dict, Optional
 from datetime import datetime, timedelta
 import time
 from fastapi import FastAPI, Form
 from jose import jwt, JWTError
 from pydantic import BaseModel
 ​
 app = FastAPI(title="XXX项目文档")
 ​
 SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
 ALGORITHM = "HS256"
 ACCESS_TOKEN_EXPIRE_MINUTES = 30
 ​
 ​
 ​
 books = [
     {
         "title": "平凡的世界",
         "author": "路遥"
     },
     {
         "title": "西游记",
         "author": "吴承恩"
     },
 ]
 ​
 users: Dict[str, Dict[str, str]] = {
     "liuxu": {"username": "liuxu", "password": "password"}
 }
 ​
 ​
 @app.post("/login")
 def login(username: str = Form(), password: str = Form()):
     if username not in users:
         return {"msg": "usename not exists"}
     if users[username]["password"] != password:
         return {"msg": "wrong password"}
     # login success & generate jwt and return
     exp = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
     access_token = jwt.encode({"sub": username, "exp": exp}, SECRET_KEY, ALGORITHM)
     return {"access_token": access_token, "token_type": "bearer"}
 ​
 @app.get("/books")
 def get_books_list():
     return books
  • 如此依赖,只要用户登录成功后就会给客户端一个access_token,这个就是jwttoken,以后客户端请求后端接口时就需要在请求头上带着它
  • 我们可以在books接口中从请求头中获取这个access_token, 拿到校验它,如果校验成功则是合法用户
  • 因为可能有其他接口也需要做类似的逻辑限制,我们可以使用依赖注入的方式,让多个接口可以贡献它。
 from typing import Dict, Optional
 from datetime import datetime, timedelta
 import time
 from fastapi import FastAPI, Form, HTTPException, status, Depends, Cookie, Header
 from jose import jwt, JWTError
 from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
 from pydantic import BaseModel
 ​
 app = FastAPI(title="XXX项目文档")
 ​
 SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
 ALGORITHM = "HS256"
 ACCESS_TOKEN_EXPIRE_MINUTES = 30
 ​
 ​
 ​
 books = [
     {
         "title": "平凡的世界",
         "author": "路遥"
     },
     {
         "title": "西游记",
         "author": "吴承恩"
     },
 ]
 ​
 users: Dict[str, Dict[str, str]] = {
     "liuxu": {"username": "liuxu", "password": "password"}
 }
 ​
 ​
 ​
 @app.post("/login")
 def login(username: str = Form()):
     exp = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
     access_token = jwt.encode({"sub": username, "exp": exp}, SECRET_KEY, ALGORITHM)
     return {"access_token": access_token, "token_type": "bearer"}
 ​
 ​
 oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
 ​
 ​
 @app.get("/books")
 def get_books_list(token: str = Depends(oauth2_scheme)):
     # 校验
     try:
         payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
         print("payload:", payload)
         username: str = payload.get("sub")
         if username is None:
             raise credentials_exception
     except JWTError:
         raise credentials_exception
     return books
  • 上面代码中,在books接口中需要获取合法的jwt token 于是我们使用 Depends(oauth2_scheme),它会返回一个token
  • 当我们这样写之后,每次访问books接口时,都会请求一次login接口获取一个合法的token,然后校验token,如何合法就返回数据,否则报错。