接口安全二:OAuth2(open authorization,开放授权)

授权流程如下:

接口安全二:OAuth2(open authorization,开放授权)

 

简单以业务场景串起来就是:张三(客户端)想取货,先去找管理货物的领导,领导给他一个授权码,并指导他去门卫那里拿通行证,门卫核实授权码后给张三一个通行证(token 短时间有效),张三拿到通行证去仓库取货。

领导给出授权码一共有4种方式:

1 授权码模式(最常用)

2 简化模式

3 密码模式

4 客户端模式

 

授权码模式是最常用的一个模式:

 

接口安全二:OAuth2(open authorization,开放授权)

 

接口安全二:OAuth2(open authorization,开放授权)

第三方引导用户跳转至认证服务器的授权页面

在引导跳转的时候需要携带如下的几个参数

response_type:授权类型。授权码模式下,就固定为code

app_id:第三方应用的标识id。

redirect_uri:重定向uri,也就是在授权成功后认证服务器让用户重定向的地址。一般而言也就是当前用户在第三方应用中最初的请求地址

scope:授权范围。可选内容,可以根据第三方应用和实现方的要求自行制定合适的值。

state:透明的验证参数。RFC6749文档推荐认证服务器在重定向的时候应该原封不同的返还这个参数。注意,该参数严格来说应该是一个必须参数。用来防止CSRF攻击。也就是说用于让第三方服务器验证重定向回来的uri的确是认证服务器的行为而不是其他的攻击者伪造的。一般来说跳转到认证服务器的授权页面是走的https,但是认证服务器重定向到回调地址的时候可能走的就是http。此时code存在泄漏以及url存在被伪造的风险。那么第三方应用必须要有办法验证该回调是否的确由认证服务器发起,并且的确是之前自己的授权请求导致的回调。做法其实也不复杂,就是在session中保存一个随机值,作为state参数。认证服务器回调的时候带上该state参数,第三方应用验证该参数是否与自己session中的state参数值一致即可。如果认证的授权页面不是https加密的,那么在发出请求的时候,认证state参数可能会被窃取。那么这个时候还有另外一种做法。也就是第三方应用发送的是加密后的state参数,而认证服务器重定向的时候携带的是解密后的state参数。第三方应用只要在session中判断解密后的值是否与session的一致,也可以达到防止攻击的目的。这样,授权页面也就是可以走在普通的http之中了。

用户选择是否给予授权

这一步是一个用户行为。目前基本的做法都是让用户在授权页面上输入用户名和密码。为了保证安全性,这个页面需要由https来进行保护。当然,如果有其他的方式来保证用户名密码,以及认证的state参数不会泄露也是可以的。如果用户输入正确的用户名和密码,一般就确认为用户给予授权。

认证服务器生成code并且让用户重定向至指定的url

如果用户给予授权,则认证服务器需要生成一个唯一的授权码code。该code的时效性应该比较短,在5分钟以内比较合适。并且该code只能使用一次,下次就会失效。同时,该code与客户端的id,redirect-uri参数是一一对应的关系。认证服务器此时应该让用户重定向至一开始指定的redirect_uri。携带上state和code参数

第三方应用使用code到认证服务器处兑换令牌access token

第三方应用在验证过state参数的正确性后,接着就可以使用code到认证服务器处换取token。这一步,第三方应用需要携带上的参数有

code:就是认证服务器给予的code参数

appid:客户端的唯一标识

redirect_uri:也就是第一步请求中的重定向参数。因为code实际上是与appid和redirect_uri一一对应的。所以用code换取令牌的时候也要携带上这两个参数

grant_type: 授权模式,这里固定为"authorization_code"

appkey:用于验证应用的身份。appid和appkey可以理解为应用自己的用户名和密码。

oauth2的服务器本身都是走https。所以都可以直接明文传输不需要考虑安全性问题。不过如果不是http的,也可以直接参数用户名密码登录的方式,就是给appkey进行md5运算。 关于为何不直接传递accesstoken的问题,是基于安全考虑。因为认证服务器是基于Https,而第三方应用可以是http的。如果在回调的时候直接带上accesstoken,就存在着泄露的问题。

认证服务器返回accesstoken

认证服务器在验证过参数的合法性后,生成一个全局唯一的token,并且返回给第三方应用。返回的内容采用json表示,返回的参数主要有

access_token: 用于获取对应资源的令牌

expires_time: 该令牌的有效期

reflesh_token: 用户获取新的accesstoekn的token。由于accesstoken的有效期比较短,一旦失效,用户需要再走上面的流程是比较繁琐的。为了提升用户体验,可以使用reflesh_token来获取新的accesstoken。不过这个做法,已经有不同的实现方将这个返回参数去掉了。因为实际上reflesh_token也就意味着accesstoekn是永久有效的了。那和直接延长accesstoken的有效期也没有直接区别了。