OAuth2
Problem
核心问题: “我怎样才能允许应用程序访问我的数据而又不必向其提供密码?”
OAuth 是 REST/API 的委托授权框架。它允许应用获得对用户数据的有限访问权限(范围),而无需泄露用户的密码。它将身份验证与授权分离开来,并支持针对不同设备功能的多种用例。它支持服务器到服务器的应用、基于浏览器的应用、移动/本机应用以及控制台/电视。
您可以将其视为酒店钥匙卡,但适用于应用程序。如果您有酒店钥匙卡,则可以进入您的房间。如何获取酒店钥匙卡?您必须在前台进行身份验证才能获得它。通过身份验证并获取钥匙卡后,您可以访问整个酒店的资源。
简单来说,OAuth 是 (以照片打印程序 PhotoApp 为例)
应用程序向用户请求授权:
PhotoApp 向 Alice 请求要访问 Alice 的照片
用户授权App并提供证明:
PhotoApp 向 Alice 申请我授权访问她的 QQ 相册, 而 Alice 授权 PhotoApp 这一请求
应用程序向服务器提供授权证明以获取令牌:
PhotoApp 向 QQ 相册提供 Alice 的授权证明
Token 仅限于访问用户为特定应用授权的内容
QQ 相册发给 PhotoApp 访问 Alice 相册图片的访问令牌 access token
OAuth 流程中的参与者
资源所有者 Resource Owner(RO): 拥有资源服务器中的数据。例如,Alice 是她的 QQ 相册的资源所有者。
资源服务器 Resource Server (RS): 存储应用程序想要访问的数据的 API, 例如 QQ 相册的 API
客户端 Client: 想要访问你的数据的应用程序, 例如相片打印程序 PhotoApp
授权服务器 Authorization Server (AS): OAuth 的主要引擎, 例如 QQ 相册的授权模块

OAuth 流程
OAuth 定义了一个基本框架, 也定义了一些典型流程, 可是到底用哪一种流程呢?
OAuth 2.0 提供了不同的授权流程(OAuth Flows),根据不同的应用场景,选择合适的流程至关重要。
OAuth 2.0 的授权流程好比不同的“过门禁”方式,不同的场景用不同的方式刷脸或刷卡。
2. Implicit Flow(隐式流程)
场景: 适用于单页面应用(SPA)和纯前端应用。
角色分配: 用户直接获取令牌,不需要后端参与。
流程图解:
用户通过前端登录,直接从授权服务器获取访问令牌(Access Token)。
前端用访问令牌访问资源。
优点:
无需后端服务器,适合轻量应用。
缺点:
令牌直接暴露在前端,安全性较低。
实现方法: 使用 JavaScript 库(如 oidc-client)直接与授权服务器交互。
3. Client Credentials Flow(客户端凭据流程)
场景: 适用于后端服务器之间的通信,或者需要访问自己资源的服务。
角色分配: 服务器自己给自己授权,用户完全不参与。
流程图解:
客户端直接向授权服务器申请访问令牌(Access Token),提供客户端 ID 和密钥。
使用令牌访问资源。
优点:
简单高效。
安全性较高,因为只在服务器上使用。
实现方法(FastAPI 示例):
import requests
# 模拟客户端向授权服务器申请令牌
response = requests.post(
'https://auth.server.com/token',
data={
'grant_type': 'client_credentials',
'client_id': 'your_client_id',
'client_secret': 'your_client_secret',
}
)
token = response.json()["access_token"]
print("Access Token:", token)
Resource Owner Password Credentials Flow(资源所有者密码凭据流程)
场景: 适用于你完全信任的客户端(内部应用,老旧系统)。
角色分配: 用户直接把用户名和密码交给客户端。
流程图解:
用户直接输入用户名和密码到客户端。
客户端用这些信息向授权服务器申请访问令牌(Access Token)。
使用令牌访问资源。
优点:
简单直接。
缺点:
用户的密码需要交给客户端,安全性很低。
实现方法(FastAPI 示例):
利用 OAuth2PasswordBearer,如下:
@app.post('/token')
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# 检查用户登录信息
if form_data.username == "user1" and form_data.password == "password1":
token = create_access_token({"sub": form_data.username})
return {"access_token": token, "token_type": "bearer"}
raise HTTPException(status_code=400, detail="Invalid credentials")
5. Device Code Flow(设备代码流程)
场景: 适用于输入受限的设备(如智能电视、IoT 设备)。
角色分配: 设备和用户配合完成授权。
流程图解:
设备向授权服务器申请一个设备代码,并提供用户操作的 URL。
用户用其他设备(比如手机或电脑)访问 URL,完成登录和授权。
设备通过轮询获取访问令牌(Access Token)。
优点:
适合没有键盘的设备。
实现方法: 使用 OAuth 设备授权库(如 requests-oauthlib)。
选哪个流程?
Web 应用:用 Authorization Code Flow,最安全。
单页面应用(SPA):用 Implicit Flow,灵活方便。
服务器间通信:用 Client Credentials Flow,效率高。
老旧应用:用 Password Flow,仅限你信得过的。
智能设备:用 Device Code Flow,用户体验好。
通过这些 Flows,OAuth2 提供了灵活的工具集,能满足各种场景需求。记住,安全是第一位,选对了流程,才能让你的应用既方便又稳如磐石!🔒
Reference

