一、OAuth 2 介绍
1,什么是 OAuth 2?
- OAuth 是一个开放标准,该标准允许用户让第三方应用访问该用户在某一网站上存储的私密资源(如头像、照片、视频等),而在这个过程中无须将用户名和密码提供给第三方应用。实现这一功能是通过提供一个令牌(token),而不是用户名和密码来访问他们存放在特定服务提供者的数据。
- 每一个令牌授权一个特定的网站在特定的时段内访问特定的资源。这样,OAuth 让用户可以授权第三方网站灵活地访问存储在另外一些资源服务器的特定信息,而非所有内容。目前主流的 qq,微信等第三方授权登录方式都是基于 OAuth2 实现的。
- OAuth 2 是 OAuth 协议的下一版本,但不向下兼容 OAuth 1.0。OAuth 2 关注客户端开发者的简易性,同时为 Web 应用、桌面应用、移动设备、起居室设备提供专门的认证流程。
- 传统的 Web 开发登录认证一般都是基于 Session 的,但是在前后端分离的架构中继续使用 Session 会有许多不便,因为移动端(Android、iOS、微信小程序等)要么不支持Cookie(微信小程序),要么使用非常不便,对于这些问题,使用 OAuth 2 认证都能解决。
2,OAuth 2 角色
OAuth 2 标准中定义了以下几种角色:
- 资源所有者(Resource Owner):即代表授权客户端访问本身资源信息的用户,客户端访问用户帐户的权限仅限于用户授权的“范围”。
- 客户端(Client):即代表意图访问受限资源的第三方应用。在访问实现之前,它必须先经过用户者授权,并且获得的授权凭证将进一步由授权服务器进行验证。
- 授权服务器(Authorization Server):授权服务器用来验证用户提供的信息是否正确,并返回一个令牌给第三方应用。
- 资源服务器(Resource Server):资源服务器是提供给用户资源的服务器,例如头像、照片、视频等。
3,OAuth 2 授权流程
下面是 OAuth 2 一个大致的授权流程图:
- 步骤1:客户端(第三方应用)向用户请求授权。
- 步骤2:用户单击客户端所呈现的服务授权页面上的同意授权按钮后,服务端返回一个授权许可凭证给客户端。
- 步骤3:客户端拿着授权许可凭证去授权服务器申请令牌。
- 步骤4:授权服务器验证信息无误后,发放令牌给客户端。
- 步骤5:客户端拿着令牌去资源服务器访问资源。
- 步骤6:资源服务器验证令牌无误后开放资源。
4,OAuth 2 授权模式
OAuth 协议的授权模式共分为 4 种,分别说明如下:
- 授权码模式:授权码模式(authorization code)是功能最完整、流程最严谨的授权模式。它的特点就是通过客户端的服务器与授权服务器进行交互,国内常见的第三方平台登录功能基本 都是使用这种模式。
- 简化模式:简化模式不需要客户端服务器参与,直接在浏览器中向授权服务器中请令牌,一般若网站是纯静态页面,则可以采用这种方式。
- 密码模式:密码模式是用户把用户名密码直接告诉客户端,客户端使用这些信息向授权服务器中请令牌。这需要用户对客户端高度信任,例如客户端应用和服务提供商是同一家公司。
- 客户端模式:客户端模式是指客户端使用自己的名义而不是用户的名义向服务提供者申请授权。严格来说,客户端模式并不能算作 OAuth 协议要解决的问题的一种解决方案,但是,对于开发者而言,在一些前后端分离应用或者为移动端提供的认证授权服务器上使用这种模式还是非常方便的。
Oauth基于客户端与认证服务器验证的能力定义了两种客户端类型(以及,维护客户端认证信息的能力): 客户端模式、密码模式。
基础参数定义:
- grant_type (发放令牌类型)、
- client_id (客户端标识id)
- username(用户帐号)
- password (用户密码)
- client_secret(客户端标识密钥)
- refresh_token (刷新令牌)
- scope(表示权限范围,可选项)
4.1 客户端模式
认证服务器给客户端下发客户端标识--一个代表了注册信息的唯一字符串。客户端标识不是秘密;它被暴露给资源拥有者,并且不能单独用来客户端验证。客户端标识对认证服务器来说是唯一的。
- 客户端直接向认证服务器获取令牌介。
- 认证服务器确认无误后,向客户端提供访问令牌。
- 客户端携带令牌访问资源端
常用于访问公共资源(无需登录):网站首页
该模式没有refresh_token,过期可以直接认证获取匿名令牌。
- 请求接口 oauth2-server 认证授权服务器的url配置,在获取AccessToken以及检测AccessToken中
http://localhost:8080/oauth/token
- 请求参数
grant_type:client_credentials client_id:46582ae7217343a8b252e3977e7cc421 client_secret:cgGvf5Rotv7D76m9JaArfY3YG6fDec47
- 结果
{ "access_token": "9fae1382-8d9c-4c64-a01c-d67817137fd4", "token_type": "bearer", "expires_in": 27689, "scope": "read write" }
4.2 密码端模式
密码模式在客户端基础上,从用户方获取帐号密码,再访问授权服务器认证授权。
- 客户端从用户方获取帐号密码。
- 客户端携带用户信息向认证服务器获取令牌介。
- 认证服务器确认无误后,向客户端提供访问令牌。
- 客户端携带令牌访问资源端
常用于访问个人资源(必须登录):个人资料
- 请求接口 oauth2-server 认证授权服务器的url配置,在获取AccessToken以及检测AccessToken中
http://localhost:8080/oauth/token
- 请求参数
grant_type:password client_id:46582ae7217343a8b252e3977e7cc421 username:18565783136 password:AC1DAdo9ZcY4dKAdtyPRzoICWZlkR7WDgtO06S5fVCUS6A/67rMxeW+2mKKbo2N1FQ== client_secret:cgGvf5Rotv7D76m9JaArfY3YG6fDec47
- 结果
{ "access_token": "41d74d86-bd30-4935-a6f1-c61614a1b72b", "token_type": "bearer", "refresh_token": "1ba402f7-394b-420b-9805-39578d6176f8", "expires_in": 30063, "scope": "read write" }
4.3 授权码模式
授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。
- 用户访问客户端,后者将前者导向认证服务器(一个获取授权的页面)。
- 用户选择是否给予客户端授权。
- 假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
- 客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
- 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
常用于 第三方登录,例如:QQ登录网易音乐。
- 请求接口
GET http://localhost:8080/authorize?response_type=code&client_id=cgGvf5Rotv7D76m9JaArfY3YG6fDec47&state=userId&redirect_uri=www.baidu.com
- 请求参数
response_type code 代表授权模式为授权码模式 client_id 客户端ID redirect_uri 表示重定向URL编码后的URI(第三方服务器) state 表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。(常用:用户标识)
- 结果
{ "access_token": "41d74d86-bd30-4935-a6f1-c61614a1b72b", "token_type": "bearer", "refresh_token": "1ba402f7-394b-420b-9805-39578d6176f8", "expires_in": 30063, "scope": "read write" }
-
简化模式
不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,即没有code,直接返回令牌。
- 客户端将用户导向认证服务器。
- 用户决定是否给于客户端授权。
- 假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
- 客户端(第三方服务器)获取到令牌。
用于单页面应用,没有后台
- 请求接口
GET http://localhost:8080/authorize?response_type=token&client_id=cgGvf5Rotv7D76m9JaArfY3YG6fDec47&state=userId&redirect_uri=www.baidu.com
- 请求参数
response_type token 代表授权模式为简化模式 client_id 客户端ID redirect_uri 表示重定向URL编码后的URI(第三方服务器) state 表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。(常用:用户标识)
- 结果
重定向 www.baidu.com?access_token=41d74d86-bd30-4935-a6f1-c61614a1b72b&state=userId
5,OAuth 2 刷新令牌
当访问令牌过期时候,刷新重新获取令牌。
Access Token 是客户端访问资源服务器的令牌。拥有这个令牌代表着得到用户的授权。然而,这个授权应该是 临时 的。这是因为,Access Token 在使用的过程中 可能会泄漏。给 Access Token 限定一个 较短的有效期 可以降低因 Access Token 泄漏带来的风险。
然而引入了有效期之后,客户端使用起来就不那么方便了。每当 Access Token 过期,客户端就必须重新向用户索要授权。这样用户可能每个几天,甚至每天都需要进行授权操作。这是一件非常影响用户体验的事情。希望有一种方法,可以避免这种情况。
传入 refresh_token
和 client_id
,认证服务器验证通之后,返回一个新的 Access Token。为了安全, OAuth2.0 引入了两个措施:
- OAuth2.0 要求,Refresh Token 一定要保持在客户端的服务器上,而绝不能放在狭义的客户端(如App 、PC端软件)上。调用
refresh
接口的时候,一定是从服务器到服务器的访问。 - OAuth2.0 引入了
client_secret
机制。即每一个client_id
都对应一个cleint_secret
。这个client_secret
会在客户端申请client_id
时,随着client_id
一起分配给客户端。客户端必须把这个client_secret
妥善保管在服务器上,绝不能泄漏。刷新 Access Token 时,需要验证这个client_secret
合法性。
- 请求接口
http://localhost:8080/oauth/token
- 请求参数
grant_type:refresh_token refresh_token: 1ba402f7-394b-420b-9805-39578d6176f8 client_id:46582ae7217343a8b252e3977e7cc421 client_secret:cgGvf5Rotv7D76m9JaArfY3YG6fDec47
- 结果
{ "access_token": "6d8cffd1-a90e-4846-838f-176050ed49b4", "token_type": "bearer", "refresh_token": "1ba402f7-394b-420b-9805-39578d6176f8", "expires_in": 43199, "scope": "read write" }