简介
授权码流程是OAuth 2中最常被使用以及最经典的一种流程。这种flow中涉及到的大部分是基于服务器端渲染的客户端。比如大部分浏览器显示的页面就是基于服务器端生成的页面渲染,这种场景下页面的内容并不取决于客户端(也就是浏览器)而是由服务器端控制。
当我们打开一个页面应用,这个页面想要访问一个我们在其他服务器上的资源。但是我们并不希望将我们访问这个资源的用户名和密码透露给这个页面应用,这种情况下我们就可以通过OAuth 2的授权码流程来生成一个token,页面应用可以通过使用这个token里访问我们的资源。
授权请求
a. 重定向
当应用服务器需要访问其他服务器的资源的时候,服务器会将请求重定向到资源服务器上。
这个重定向请求需要使用一些特定的请求参数:
- client_id:应用服务器在授权服务器上注册的id,作为这个应用在这台授权服务器上的唯一标识。这个注册是使用OAuth 2的一个前提条件。也就是说并不是随便一个应用都能通过访问授权服务器来获取token,必须提前注册
- scope:定义了需要访问的资源
- redirect_uri:授权完成后的回调地址
- response_type:授权完成后返回的参数类型,在授权码流程中使用的是code授权码这种类型,接下来会讲解为何使用code以及作用是什么
- state:这是一个可选项,但是官方建议每次请求都附带上。state是由应用生成的一个随机数,主要用于关联授权完成之后的回调。
这个请求内容按照直白的语言大概是说:我是webapp,我这次的请求序号是123456,我想要请求访问resource,如果访问的权限被允许了的话请调用我的接口https://webapp/xx,我期待你在调用我接口的时候同时返回一段类型为code的授权码。
b. 认证登录
授权服务器获得请求之后,会先查看用户是否已经认证登录了(这里的认证登录authentication是对于授权服务器的认证登录,在后续的文章中我们会讲解到不应该使用OAuth 2来实现认证登录,那个登录登录指的是应用的登录,希望大家了解这一点)。如果没有登录,那么会弹出类似下图的登录界面让用户登录。这是因为没有登录的情况下,授权服务器并不能知道谁是资源的所有者,进而就没办法判断接下来的操作。
下面的这个例子使用的是通过微博账号来登录携程旅游,通过地址栏我们可以看到其实用的链接格式就是我们在前面一小节中提到的一些参数。
授权服务器登录界面
c. 权限确认
登录了授权服务器之后(上面的例子就是登录了微博之后),如果这是第一次通过授权服务器给应用进行授权,那么授权服务器就是弹出所谓的’权限确认页面‘。在这个页面中会显示是什么应用(携程),需要访问哪些数据(个人信息好友关系等等)。如果用户选择了授权(例子中的’连接‘),那么授权服务器就会负责后面的token生成等一些操作,这样携程旅游就能够访问在微博中的用户信息了。
权限确认页面(Consent Page)
d.授权响应
如果在上一步,用户允许了授权,那么授权服务器就会生成一个授权码,并且回调刚刚定义在redirect_uri中的地址。
这个授权码并不是最后会被使用的token,之所以需要一个额外的授权码是因为在这整个授权请求的过程中不是应用服务器和授权服务器直接交互的,其中还通过了所谓的客户端也就是浏览器。如果授权服务器直接返回token令牌,那么就意味着任何一个能够使用浏览器的人都有可能获取到这段token,那么他们就可以通过这个token获取到用户的信息。
同时,在这段回调中也包含了state信息,state信息是和之前重定向中的state信息一致的,这样应用服务器就能通过state来区分这个回调是对应之前哪个请求了。
令牌申请
a. 令牌申请请求
在获取了授权码之后,应用服务器就可以直接和授权服务器进行交互来获取token了。
在这个过程中,应用服务器需要在授权服务器进行认证,而认证的信息在最开始应用注册到授权服务器的时候进行定义配置的。然后应用服务器需要将之前获取的授权码发送给授权服务器。这个请求中需要两个参数一个就是authorization_code授权码,还有一个是grant_type是authorization_code代表是通过授权码模式进行授权的。
b. 令牌申请响应
授权服务器在接收到应用服务器的请求之后,如果一切顺利,那么就会返回令牌请求的响应。
在这个响应中,包含了两种类型的token,一种是access_token,还有一种是refresh_token。access_token代表了应用服务器可以直接使用的token,这种token具有比较短的时效性。refresh_token则是相对长效的一个token,会在之后的章节中再提及。在下面的例子中,access_token在一小时后就会过期。expires_in定义了access_token的有效时间,使用的时间单位是秒。而token_type则标识了token的类型。
访问资源
应用服务器在获取了令牌access_token之后就可以直接访问资源服务器了。
关于Access Token和Refresh Token
资源服务器会根据access_token中包含的信息来对应用服务器的访问进行控制。 access_token中包含了 一些重要信息,比如资源所有者的信息,应用服务器的信息,允许被访问的资源等等。资源服务器一般需要验证token的issuer,signature以及有效期。
由于access_token是有时效性的,那么如果token过期之后应该怎么处理呢。
一种简单的方式就是将上述的申请步骤再循环一次就能获得新的token了,但是这涉及到太多的http请求并且还有可能需要用户的干涉才能完成,体验并不是很友好。
另一种方式就是通过refresh_token来获取一个新的access_token. 这个请求跟前面的token请求类似,唯一的区别就是grant_type的不同,这里使用的是refresh_token。
但是,这里其实引入了一个问题,一旦有了refresh_token,就意味着应用永远都可以访问资源服务器上的资源。我想大部分用户都不希望这样的事情发生。
所以,大部分的授权服务器都会有移除授权的功能。这就意味着一旦用户移除了授权,应用无法再通过refresh_token来更新access_token,只能通过重新走一次上述的流程来获得授权。
总结
- 授权码流程是为基于服务器端应用而设计的。服务器端能够安全的保存其于授权服务器之间通信所使用的认证信息。授权码能够防止access_token通过浏览器泄露
- 可以实现长时访问access_token有过期时间refresh_token可以用来获取新的access_token授权服务器需要能够管理授权以防止应用服务器可以无限期的访问资源
参考
RFC 6749 – The OAuth 2 2.0 Authorization Framework