Web身份认证 --- OAuth授权机制
Web身份认证 --- OAuth授权机制
- 什么OAuth
- 适用场景
- OAuth工作流
- 两种授权模式
- 授权码模式(authorization code)
- 简化模式 (无授权码)
- 两个模式的区别
什么OAuth
OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版
适用场景
为了理解OAuth的适用场合,让我举一个假设的例子。
有一个"云冲印"的网站,可以将用户储存在Google的照片,冲印出来。用户为了使用该服务,必须让"云冲印"读取自己储存在Google上的照片。
问题是只有得到用户的授权,Google才会同意"云冲印"读取这些照片。那么,"云冲印"怎样获得用户的授权呢?
- 方法: 用户将自己的Google用户名和密码,告诉"云冲印",后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点。
- "云冲印"为了后续的服务,会保存用户的密码,这样很不安全。
- Google不得不部署密码登录,而我们知道,单纯的密码登录并不安全。
- "云冲印"拥有了获取用户储存在Google所有资料的权力,用户没法限制"云冲印"获得授权的范围和有效期。
- 用户只有修改密码,才能收回赋予"云冲印"的权力。但是这样做,会使得其他所有获得用户授权的第三方应用程序全部失效。
- 只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏
OAuth就是为了解决上面这些问题而诞生的
OAuth工作流
- Third-party application:第三方应用程序,本文中又称"客户端"(client),即上一节例子中的"云冲印"。
- HTTP service:HTTP服务提供商,本文中简称"服务提供商",即上一节例子中的Google。
- Resource Owner:资源所有者,本文中又称"用户"(user)。
- User Agent:用户代理,本文中就是指浏览器。
- Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
- Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器

(A)用户打开客户端以后,客户端要求用户给予授权。
(B)用户同意给予客户端授权。
(C)客户端使用上一步获得的授权,向认证服务器申请令牌。
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源
两种授权模式
授权码模式(authorization code)
授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动

第5步
用户被重新定向到授权服务器后, 请求授权码
GET /oauth/authorize?response_type=code
&client_id=s6BhdRkqt3
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
&scope=photos iles
&state=xyz123
HTTP/1.1
Host: auth.server.com
- response_type=code - 固定值,表示授权码模式
- client_id=s6BhdRkqt3 - 客户端注册时获得的ID
- redirect_uri=https://client.example.org/cb - 授权成功后回调地址,授权码随这个redirect发放
- scope=photos files - 请求的权限范围 (optional)
- state=xyz123 - 随机字符串,防止CSRF攻击
第9步和第10步:用户授权
收到获取授权码的请求后,授权服务器在前端显示登录和授权页面

第11步:返回授权码
用户授权后,授权服务器重定向到客户端前端,并携带授权码
HTTP/1.1 302 Found
Location: https://client.example.org/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz123
Cache-Control: no-cache, no-store
- code=SplxlOBeZQQYbYS6WxSbIA - 授权码,有效期通常很短(几分钟)
- state=xyz123 - 原样返回客户端之前发送的state参数
第15步:客户端后端发送get access code请求到认证服务器
POST /oauth/token HTTP/1.1
Host: auth.server.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JWgrant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
参数详解:
- Authorization里包括clientId 和 client secret
- grant_type=authorization_code - 固定值,表示使用授权码模式
- code=SplxlOBeZQQYbYS6WxSbIA - 上一步获得的授权码
- redirect_uri=https://client.example.org/cb - 必须与授权请求中的一致
认证方式:
- 使用HTTP Basic认证,头部包含:Authorization: Basic base64(client_id:client_secret)
或者在请求体中包含:client_id=s6BhdRkqt3&client_secret=gX1fBat3bV
第17步: 返回访问令牌
授权服务器返回访问令牌和刷新令牌(储存在浏览器里)
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache{"access_token": "2YotnFZFEjr1zCsicMWpAA","token_type": "Bearer","expires_in": 3600,"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA","scope": "photos files"
}
- access_token - 访问令牌,用于访问受保护资源
- token_type - 令牌类型,通常是"Bearer"
- expires_in - 令牌有效期(秒)
- refresh_token - 刷新令牌,用于获取新的访问令牌
- scope - 实际授予的权限范围
第21步: 访问受保护资源
客户端使用访问令牌调用API
GET /api/photos HTTP/1.1
Host: resource.server.com
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA (access token)
Accept: application/json
简化模式 (无授权码)
简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证

隐式授权模式的核心特点:
- 无客户端后端参与:整个流程在浏览器中完成,不需要客户端的后端服务器。
- 直接返回令牌:授权服务器直接返回access_token,跳过了授权码步骤。
- 通过URL片段传递:令牌通过URL的#片段传递
- 令牌对用户可见:访问令牌在浏览器的地址栏和JavaScript代码中是可见的
安全注意事项:
- 由于令牌在浏览器中暴露,存在被截获暴露的风险 (第八步:access token直接放在redirect url里)
在现代OAuth 2.0实践中,更推荐使用授权码模式+PKCE来替代隐式模式
两个模式的区别
场景一:授权码模式 - 正规安全流程
- 你 = 用户
- 公寓前台 = 授权服务器
- 保安室 = 客户端后端服务器
- 住户小明 = 资源所有者
- 小明家 = 资源服务器
第1步:申请访问
- 你来到豪华公寓大堂,对前台说:
“你好,我想去拜访18楼的小明”
第2步:身份登记
- 前台给你一张访客登记表:
“请先填写个人信息,我们需要验证你的身份”
你填写了姓名、电话、身份证号(用户名密码登录)
第3步:主人确认
- 前台打电话给小明确认:
“小明先生,有一位XXX先生要拜访您,是否允许?”
小明在电话中回答:“是的,我认识他,请允许他上来”
(用户授权确认)
第4步:获得取卡凭证
- 前台给你一张【取卡单】,上面写着:
“凭证号:AUTH-123456”
(这就是授权码,本身不能开门)
第5步:兑换门禁卡
- 你拿着取卡单走到保安室:
“你好,我用这个凭证兑换临时门禁卡”
保安验证凭证真伪后,给你一张【临时门禁卡】
(后端用授权码交换访问令牌)
第6步:进入访问
- 你用临时门禁卡刷开电梯和楼层门禁
顺利来到小明家做客
🔒 安全特点
🛡️ 门禁卡从不暴露在公开场合
- 即使有人看到取卡单,也无法直接使用
- 保安室(后端)是受信任的中介
场景二:隐式模式 - 快捷自助流程
- 你 = 用户
- 公寓前台 = 授权服务器
- 住户小明 = 资源所有者
- 小明家 = 资源服务器
- 第1步:申请访问
你来到公寓大堂,对前台说:
“我想去小明家做客”
- 第2步:身份验证
前台说:“请先登录系统验证身份”
你在前台的平板电脑上:
-输入用户名密码(登录授权服务器)
-看到提示:“应用XXX想访问你的基本信息,是否允许?”
-点击"允许"(用户授权)
- 第3步:直接获得门禁密码 ⚠️ 这里开始出现问题
前台没有给你临时门禁卡,而是:
-在大堂的公共电子屏幕上直接显示:
“小明家门禁密码:123456”
-同时还显示:“小明家地址:18楼1801室”
任何人都能看到这个密码!
- 第4步:使用密码进入
你记下密码,坐电梯到18楼
在小明家门禁输入密码"123456"
门开了,你进入小明家
安全问题分析
- 问题1:密码完全暴露
隐式模式的实际表现:令牌在URL中公开
https://client.com/callback#access_token=eyJ123456…&token_type=Bearer
就像密码显示在公共屏幕上,任何能看到你浏览器地址栏的人都能偷走令牌。
- 问题2:令牌可能泄露给恶意应用
// 如果重定向URI被篡改,令牌可能发送到攻击者服务器
恶意重定向:https://hacker.com#access_token=你的令牌
就像有人冒充你领走了密码。
