microdot.login --- 使用者登入流程¶
圍繞 microdot.session 的較高階包裝,實作慣用的使用者名稱/密碼登入流程:使用者載入器(user loader)回呼函式會將工作階段所儲存的使用者 ID 映射回應用程式的使用者物件,路由裝飾器會將未經驗證的請求重新導向至可設定的登入 URL,而選用的「記住我」cookie 則讓回訪者跨瀏覽器工作階段保持登入狀態。
需要在建構登入物件之前於應用程式上初始化 microdot.session —— 工作階段即是儲存使用者 ID 的位置。
class Login¶
- class microdot.login.Login(login_url: str = '/login')¶
- login_url
裝飾器將未經驗證的請求重新導向至的 URL。應用程式會在此路由提供實際的登入表單/處理常式。預設為
'/login'。
- user_loader(f)¶
註冊使用者解析回呼函式的裝飾器。f 接受儲存於工作階段中的使用者 ID 並回傳使用者物件(若該 ID 已不再有效則回傳
None—— 例如已刪除的帳號、已撤銷的工作階段等)。login = Login() @login.user_loader async def load_user(user_id): return users.get(user_id)
- __call__(f)¶
以
Login實例裝飾路由即可將其置於驗證之後::@app.get('/dashboard') @login async def dashboard(request): user = request.g.current_user # ...
未經驗證的請求會被重新導向至 login_url,並附帶
?next=<original-url>查詢參數,讓登入處理常式可將使用者送回其原本所在之處。
- fresh(f)¶
與
__call__()類似,但會拒絕從「記住我」cookie 還原的工作階段 —— 使用者必須自上次完整登入後曾明確登入。用於保護敏感路由(變更密碼、刪除帳號),使被竊取的記住我 cookie 無法觸及這些路由。
- async login_user(request, user, remember: bool | int = False, redirect_url: str = '/')¶
將 user 標記為已為 request 登入。會將使用者 ID 儲存於工作階段中並回傳重新導向回應。
- user
使用者載入器所回傳的使用者物件。必須具有
id屬性。- remember
若為真值,則同時設定長效的
_remembercookie。傳入True表示預設的 30 天,或傳入整數表示 cookie 應持續的天數。- redirect_url
登入後重新導向的目的地。若原始請求的
?next=<url>指向同站路徑,則由其覆寫。
回傳重新導向回應 —— 請從登入路由處理常式回傳其值。
- async logout_user(request)¶
從工作階段清除使用者 ID 並移除任何
_remembercookie。請從/logout路由使用::@app.post('/logout') async def logout(request): await login.logout_user(request) return redirect('/')
- async get_current_user(request)¶
回傳目前已登入的使用者物件(或
None)。會記憶於request.g.current_user上,因此在單一請求內重複呼叫只會觸及載入器一次。
範例¶
一個最簡的登入流程::
from microdot import Microdot, redirect
from microdot.session import Session
from microdot.login import Login
app = Microdot()
Session(app, secret_key=load_secret())
login = Login()
@login.user_loader
async def load_user(user_id):
return users.get(user_id)
@app.get('/login')
async def login_page(request):
return Response.send_file('static/login.html')
@app.post('/login')
async def do_login(request):
user = authenticate(request.form['user'], request.form['pass'])
if not user:
return redirect('/login?error=1')
return await login.login_user(request, user, remember=True)
@app.get('/dashboard')
@login
async def dashboard(request):
return 'hi ' + request.g.current_user.name
@app.post('/logout')
async def logout(request):
await login.logout_user(request)
return redirect('/')
_remember cookie 本身是一個簽署過的 JWT(使用工作階段的密鑰),因此被竊取的 cookie 無法在不同的應用程式上、或在密鑰輪替後被重複使用。