OAuth2简介
是一个业界标准的授权协议(authorization protocol),这里的授权是以委派代理(delegation)的方式。可以这样理解,OAuth 2.0提供一种协议交互框架,让某个应用能够以安全地方式获取到用户的委派书,这个委派书在OAuth 2.0中就是访问令牌(access token),随后应用便可以使用该委派书,代表用户来访问用户的相关资源。
在OAuth 2.0的协议交互中,有四个角色的定义,
- 资源所有者(Resource Owner):顾名思义,资源的所有者,很多时候其就是我们普通的自然人(但不限于自然人,如某些应用程序也会创建资源),拥有资源的所有权。
- 资源服务器(Resource Server):保存着受保护的用户资源。
- 应用程序(Client):准备访问用户资源的应用程序,其可能是一个web应用,或是一个后端web服务应用,或是一个移动端应用,也或是一个桌面可执行程序。
- 授权服务器(Authorization Server):授权服务器,在获取用户的同意授权后,颁发访问令牌给应用程序,以便其获取用户资源。
从一个简单的应用场景谈起
为了方便讨论,我们假设有一个用户Michael,他在一个资源服务器上保存着他自己的账号信息,例如微信账号的姓名、头像等。某个应用程序在用户登录时,需要获取Michael的这些账号信息。
Michael在资源服务器上保存的账号信息是受保护的,为了让应用程序能够获取Michael的账号信息,需要提供用户的访问密码。有一个简单的方法是,在应用服务器和资源服务器之间共享同一访问密码,当Michael登录输入密码后,应用程序复制Michael的登录密码并向资源服务器请求访问,获取Michael的账号信息。这是早些时候比较常见的跨应用授权访问方法。
这样子做有很大的安全隐患,主要有如下三个方面的问题,
- 用户在应用程序和资源服务器需要保持一致的密码
- 无法控制应用程序的权限,应用程序需要的是读权限,但是拿到用户密码后,获取到的却是用户的所有访问权限
- 用户的密码会被应用程序获取到,有用户密码泄露的风险,一旦应用程序多了,安全风险不可控
在简单的应用场景里,在应用程序和资源服务器之间保持一致的密码是可行的,这也确实能够带来一定的便利,至少用户不用记多套用户名和密码,但账号和密码的独立性无法得到保证,应用程序可以直接接触到用户密码等敏感信息,账号的安全性也无法控制。
为了解决第1个问题,用户Michael在应用程序和资源服务器可以使用不同的密码登录,有一个可行的方法是,让用户输入两次密码,第一次输入密码为了登录应用程序,第二次让Michael输入其在资源服务器的登录密码,以便应用程序获取资源服务器的账号信息。
这样就需要用户输入两次密码,给用户的使用带来很大的不便,而且这个方案依然存在第2和第3的问题。
为了解决第2个问题,限制应用程序访问资源服务器的权限,我们可以让用户在资源服务器申请一个只读的受限密码,该受限密码只用来读取用户信息,无法用来进行编辑和删除操作,用户输入这个只读密码给应用程序,让应用程序读取用户在资源服务器上的信息。
这个方法解决了上述提到的第3个问题,但是这个key是一个通用的读权限,权限范围很大,其和用户没有任何关联。在很多时候,我们还是需要用户级别上的受限权限控制。
能否有一个方案,在不影响用户的使用便利性,并且颁发一个在用户级别上的可控权限key?可以考虑的是,用户动态按需地向资源服务器申请读权限key,然后颁发给应用程序,用于应用程序去申请访问用户的信息,该受限密码在颁发后有一定的时效性,甚至可以指定其只能被使用一次。这样子的话,解决问题1、2和3的条件都得到满足。
这个方案已经很接近于OAuth 2.0在设计之初所提供的授权方案,不一样之处的是,受限密码的颁发交给了独立的安全组件:授权服务器。
这里马上就要介绍OAuth 2.0的基本授权方式:授权码模式。
OAuth 2.0基本授权流程:授权码模式
让我们看看在增加授权服务器之后,OAuth 2.0的一个基本授权流程,
如图所示,授权流程场景可以描述为如下几个步骤,
- 用户在应用程序中,应用程序尝试获取用户保存在资源服务器上的信息,比如用户的身份信息和头像,应用程序首先让重定向用户到授权服务器,告知申请资源的读权限,并提供自己的client id。
- 到授权服务器,用户输入用户名和密码,服务器对其认证成功后,提示用户即将要颁发一个读权限给应用程序,在用户确认后,授权服务器颁发一个授权码(authorization code)并重定向用户回到应用程序。
- 应用程序获取到授权码之后,使用这个授权码和自己的client id/secret向认证服务器申请访问令牌/刷新令牌(access token/refresh token)。授权服务器对这些信息进行校验,如果一切OK,则颁发给应用程序访问令牌/刷新令牌。
- 应用程序在拿到访问令牌之后,向资源服务器申请用户的资源信息
- 资源服务器在获取到访问令牌后,对令牌进行解析(如果令牌已加密,则需要进行使用相应算法进行解密)并校验,并向授权服务器校验其合法性,如果一起OK,则返回应用程序所需要的资源信息。
这个授权流程在OAuth 2中被称为授权码模式(authorization code grant),其命名的原因是,应用程序使用授权码来向授权服务器申请访问令牌/刷新令牌。
可以看到,在整个过程中应用程序没有接触到用户的密码。
授权码和令牌都是一个唯一标识的值,其各个意义为,
- 授权码:即用户的委派书,代表着用户的受限权限,有时效性
- 访问令牌:用于应用程序每次向资源服务器访问时提供,有时效性,如果安全性比较高的话,则每个访问令牌可以被设置为只用一次,或者对令牌设置一个有效期,在有效期可以反复使用。
- 刷新令牌:用于应用程序向授权服务器申请新的访问令牌,在访问令牌失效或过期的时候,重新获取新的访问令牌。
注意的是,访问令牌对于应用程序来说是透明的,应用程序无需关注访问令牌所带的任何信息,只需在访问资源服务器时带上它。但是资源服务器需要知道访问令牌的组成和加密方式,资源服务器需要解析或解密这个访问令牌,查看并校验里面的信息。
授权服务器和访问令牌,前者为授权的颁发,后者为授权的载体,两者实现了动态按需地代理权限分发,这也是OAuth 2.0解决方案在授权上所带来的创新变化。