--- title: 使用 FastAPI 及 MongoDB 建立登入 API tags: 筆記 description: --- ## 開啟 MongoDB **輸入連結後點擊 `Connect` ,進行連接。** ![](https://i.imgur.com/gLB68uL.png) **新增 `Database` 以及 `Collection`。** ![](https://i.imgur.com/53sZd4y.png) **資料表中的資料。** ![](https://i.imgur.com/ZgZD2gD.png) ## 建立 FastAPI ```python import uvicorn from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from api.authentication import router as auth_router def create_app (): app = FastAPI() # 允許指定源頭發起跨來源請求 origins = [ 'http://localhost', 'http://localhost:3000' ] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=['*'], allow_headers=['*'] ) return app app = create_app() app.include_router(auth_router, prefix='/api') if __name__ == '__main__': uvicorn.run(app, host='127.0.0.1', port=8000) ``` ## 驗證身分 ### JWT token 加密 ```python import jwt SECRET_KEY = 'kirito' def generate_token (result: dict): payload = { 'name': result['name'], 'role': result['role'], 'username': result['username'], 'password': result['password'], 'avatar': result['avatar'] } token = encode(payload, SECRET_KEY, algorithm='HS256') return token ``` ### 連接 MongoDB 並驗證身分 ```python from fastapi import APIRouter, Depends, HTTPException from fastapi.security import HTTPBasic, HTTPBasicCredentials import pymongo router = APIRouter() security = HTTPBasic() @router.post('/authenticate') async def authenticate (credentials: HTTPBasicCredentials = Depends(security)): client = pymongo.MongoClient(['localhost:27017']) DATABASE = client['Fall_Detection'] result = DATABASE['user'].find_one({'username' : credentials.username}) if not result or result['password'] != credentials.password: raise HTTPException(status_code=401, detail='Incorrect username or password') token = generate_token(result) return {'access_token' : token} ``` ## 獲取用戶相關資訊 ### JWT token 解密 ```python def get_token (auth_header: str = Depends(oauth2_scheme)): token = auth_header try: payload = decode(token, SECRET_KEY, algorithms=['HS256']) return payload except PyJWTError as error: raise HTTPException(status_code=401, detail=str(error)) ``` ### 回傳用戶資訊 ```python @router.get('/protected') async def read_protected (token_payload: dict = Depends(get_token)): return token_payload ``` ## API 測試 測試網址:[https://reqbin.com](https://reqbin.com) **驗證成功** ![](https://i.imgur.com/isg3p9D.png) **驗證失敗** ![](https://i.imgur.com/YPguHfW.png) **獲取成功** ![](https://i.imgur.com/Uigheqq.png) **獲取失敗** ![](https://i.imgur.com/GKoYrcn.png) ## 應用 ### 使用 fetch 進行 POST 請求 發送帳號密碼驗證身分。 ```typescript const navigate = useNavigate(); const handleSubmit = async (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); const userData = new FormData(event.currentTarget); const username = userData.get('username'); const password = userData.get('password'); const url = 'http://localhost:8000/api/authenticate'; const requestOptions = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Basic ${window.btoa(`${username}:${password}`)}` }, body: JSON.stringify({ }) }; const handleResponse = async (response: Response) => { const json = await response.json(); return (response.ok) ? json : Promise.reject(json); } await fetch(url, requestOptions) .then(handleResponse) .then((data) => { toast.success('登入成功'); localStorage.setItem('access_token', data.access_token); navigate('/'); }) .catch(() => toast.error('帳號或密碼錯誤')); }; ``` ### 使用 fetch 進行 GET 請求 發送 token 取得用戶資料。 ```typescript const handleResponse = async (response: Response) => { const json = await response.json(); return (response.ok) ? json : Promise.reject(json); } const token = localStorage.getItem('access_token'); const url = 'http://localhost:8000/api/protected'; const requestOptions = { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }; fetch(url, requestOptions) .then(handleResponse) .then((data) => { handleSetUser({ id: data.id, role: data.role, name: data.name, username: data.username, avatar: data.avatar, }); }); ``` ![](https://i.imgur.com/C1UNhFQ.gif)