前言
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
OAuth 2.0是OAuth协议的下一版本,但不向下兼容OAuth 1.0。OAuth 2.0关注客户端开发者的简易性,同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。
Oauth2.0定义的 5种角色
- 客户端(Client)
客户端是OAuth服务的接入方,其目的是请求用户存储在资源服务器上的受保护资源,客户端可以是移动应用,网页应用等等 - 用户代理(User Agent)
用户代理是用户参与互联网的工具,一般可以理解为浏览器 - 资源所有者(Resource Owner)
受保护资源所属的实体,比如资源的持有人,用户既资源所有者 - 授权服务器(Authorization Server)
授权服务器的主要职责是验证所有者的身份,并依据资源所有者的许可对第三方应用下发令牌 - 资源服务器(Resource Server)
托管资源的服务器,能够接受和相应持有令牌的资源访问请求,可以与授权服务器是同一台服务器。
基本概念
访问令牌(access token)
访问令牌是在用户授权许可下,授权服务器下发给客户端的一个授权凭证,该令牌所要表达的意思是“用户授予该APP在多少时间范围内允许访问哪些与自己相关的服务”,所以访问令牌主要在 时间范围 和 权限范围 两个维度进行控制,此外访问令牌对于客户端来说是非透明的,外在表现就是一个字符串,客户端无法知晓字符串背后所隐藏的用户信息,因此不用担心用户的登录凭证会因此而泄露。
刷新令牌(refresh token)
刷新令牌的作用在于更新访问令牌,访问令牌的有效期一般较短,这样可以保证在发生访问令牌泄露时,不至于造成太坏的影响,但是访问令牌有效期设置太短存在的副作用就是用户需要频繁授权,虽然可以通过一定的机制进行静默授权,但是频繁的调用授权接口,之于授权服务器也是一种压力,这种情况下就可以在下发访问令牌的同时下发一个刷新令牌,刷新令牌的有效期明显长于访问令牌,这样在访问令牌失效时,可以利用刷新令牌去授权服务器换取新的访问令牌,不过协议对于刷新令牌没有强制规定,是否需要该令牌是客户端可以自行选择。
回调地址(redirect uri)
OAuth2.0 是一类基于回调的授权协议,在授权码模式中,整个授权需要分为两步进行,第一步下发授权码,第二步根据第一步拿到的授权码请求授权服务器下发访问令牌。OAuth 在第一步下发授权码时,是将授权码以参数的形式添加到回调地址后面,并以 302 跳转的形式进行下发,这样简化了客户端的操作,不需要再主动去触发一次请求,即可进入下一步流程。
回调请求的设计却存在一个很大的安全隐患,坏人如果在客户端请求过程中修改了对应的回调地址,并指向自己的服务器,那么坏人可以利用这种机制去拿到客户端的授权码,继而走后面的流程,最终拿到访问令牌,另外坏人可以利用该机制引导用户到一个恶意站点,继而对用户发起攻击。以上两点都是该机制对于用户所造成的安全威胁,对于授权服务器而言,也存在一定的危害,坏人可以利用该机制让授权服务器变成“请求发送器”,以授权服务器为代理请求目标地址,这样在消耗授权服务器性能的同时,也对目标地址服务器产生 DDOS 攻击。
为了避免上述安全隐患,OAuth 协议强制要求客户端在注册时填写自己的回调地址,这个回调地址的目的是为了让回调请求能够到达客户端自己的服务器,从而可以走获取访问令牌的流程。客户端可以同时配置多个回调地址,并在请求授权时携带一个地址,服务器会验证客户端传递上来的回调地址是否与之前注册的回调地址相同,或者前者是后者集合的一个元素,只有在满足这一条件下才允许下发授权码,同时协议还要求两步请求客户端携带的回调地址必须一致,通过这些措施来保证回调过程能够正常达到客户端自己的服务器,并继续后面拿授权码换取访问令牌的流程
权限范围 (scope)
访问令牌自带过期时间,可以在时间维度上对授权进行控制,而在范围维度上,OAuth 引入了一个 scope 的概念。scope 可以看做是一个对象,包含一个权限的 ID,名称,以及描述信息等,比如“获取您的基本资料(头像、昵称)”。应该在接入账号服务时必须向第三方登录服务提供方申请响应的 scope,并在请求授权时指明该参数(否则表明获取该应用所允许的所有权限),这些权限在用户确认授权时,必须毫无保留的展示给用户,以让用户知道该APP需要获取用户的哪些数据或服务。
基本授权流程
- Client请求Resource Owner的授权,请求一般包含:要访问的资源路径,操作类型,Client的身份等信息。
- Resource Owner批准授权,并将“授权证据”发送给Client。
- Client向Authorization Server请求“访问令牌”。此时,Client需向AS提供RO的授权证据以及Client自己身份的凭证。
- AS通过验证后,向Client返回“访问令牌”,
- Client携带“访问令牌”访问RS上的资源。
- RS验证令牌的有效性,比如是否伪造,越权等。
授权码模式(Authorization Code Grant)
大部分 APP 的 OAuth 授权都可以采取授权码模式,下图为授权码各个角色之间的交互时序(这里让用户直接参与其中,省略了用户代理)
- 客户端携带 client_id, scope, redirect_uri, state 等信息引导用户请求授权服务器的授权端点下发 code
- 授权服务器验证客户端身份,验证通过则询问用户是否同意授权(此时会跳转到用户能够直观看到的授权页面,等待用户点击确认授权)
- 假设用户同意授权,此时授权服务器会将 code 和 state(如果客户端传递了该参数)拼接在 redirect_uri 后面,以302形式下发 code
- 客户端携带 code, redirect_uri, 以及 client_secret 请求授权服务器的令牌端点下发 access_token (这一步实际上中间经过了客户端的服务器,除了 code,其它参数都是在应用服务器端添加,下文会细讲)
- 授权服务器验证客户端身份,同时验证 code,以及 redirect_uri 是否与请求 code 时相同,验证通过后下发 access_token,并选择性下发 refresh_token
内部应用可以拿着第三方应用的 client_id 等信息代替第三方应用去请求获取 code,因为自己持有用户的登录态,所以过程中无需用户再次输入用户名和密码,拿到 code 之后将其交给第三方应用,第三方应用利用 code 和自己的 client_secret 信息去请求授权服务器下发 token,整个流程内部应用不需要交出自己持有的用户登录态,第三方应用也无需交出自己的 client_secret 信息,最终却能够实现在保护用户登录凭证的前提下无需再次登录即可完成整个授权流程