OAuth 2.0简介1OAuth2授权流程刨根问底协议ContentsOAuth 2.0简介 ........................................................................................ 1什么是中的角色授权流程 ............................................................................................... 2()其他 .................................................................................................... 7的验证最近有一个问题一直比较困惑,起因是我们有一个的应用使用进行权限验证,而我之前的实践通常是基于的权限验证(即通过用户名和密码获取),那么是否适合这样的场景呢?OAuth 2.0简介什么是OAuth对进行了介绍:从这个描述我们可以得出一些结论:是一个授权(而不是认证)的框架设计是为了授权一个第三方的应用访问受限的服务资源出现取代了那么,的出现时为了解决什么问题呢?对于一个典型的客户端请求服务器(受限)资源的场景,譬如一个网络相册服务,客户端需要将用户的认证信息(通常是用户名和密码)发送给服务端,这样才能保证有且仅有这个相册的能够访问该相册。现在假设有一个第三方的应用,比如一个什么打印机应用来帮用户打印照片,它也需要调用相册的服务来获取照片,那么显而易见的做法就是将用户的认证信息共享给第三方。但是这样做带来了一些问题:第三方应用需要存储用户的认证信息,这样通常是不安全的服务器需要能够支持密码认证 授权流程2第三方获取到的权限可能比需要要大(比如也许打印机只需要访问一张照片,但是有了用户的认证信息实际上也可以访问到其他的照片,而无法进行限制),而且无法限制其使用时长如果期望取消某一个第三方应用的授权,唯一的办法是修改密码,但假设有多个第三方应用那么都会收到影响在中,客户端通过请求一个单独的来访问受限的资源,而中包含了一些关于权限的描述信息(譬如范围、时效等)。这样在上面的打印照片的例子中,打印服务不需要知道用户的用户名和密码就可以获取到用户想打印的照片。OAuth 中的角色中定义了四个角色::资源所有者(通常是用户),可以授权应用访问其所有的受保护的资源:资源服务器,可以根据获取受保护的资源:代表及其授权、访问受保护资源的应用:通过对认证并获得其授权后,颁发的值得注意的是,这些角色并不要求是分开的实体,同一个也可以拥有多个角色,例如和可以是同一个服务。对于,又可以分为两种:可以认证的客户端,能够(安全的)保存自身的认证信息无法存储自身认证信息,例如运行在浏览器或者移动端的应用授权流程中最初定义了种授权方式:后面又发布了一些新的流程和增强,有些已经过时了。Authorization Code Grant客户端需要生成一个授权链接,包含如下参数:必须为code客户端标识重定向链接授权的用来防止跨站请求伪造https://authorization-server.com/authorize? response_type=code 授权流程3 &client_id=egHuu4oJxgOLeBzPAQ9sXg4i &redirect_uri=https://www.oauth.com/playground/authorization-code.html &scope=photo+offline_access &state=hCi3i1u67XgxqbO-授权服务器收到请求后,对请求参数进行检查,如果无误则对用户进行认证,并取得用户授权;授权完成后,授权服务器重定向到请求中的上,并附加一些参数:授权码,必须是在短期内失效(以降低泄漏后带来的风险),建议最长不超过分钟;客户端对其应该只使用一次,否则授权服务器将拒绝请求并应该尽可能之前通过该颁发的。即请求中的值在上面的例子中,当用户授权之后,会跳转到:https://www.oauth.com/playground/authorization-code.html? state=hCi3i1u67XgxqbO- &code=7RfqR_w09Ak75fZRlFCVL1ZtKUM3RR67Wd18I9tNZQwSANx9客户端必须首先验证state是否与用户会话中的值一致(这个值可以保存在、或者通过其他方式保存),从而防止攻击。验证无误后,客户端需要使用这个来换取:POST https://authorization-server.com/tokengrant_type=authorization_code&client_id=egHuu4oJxgOLeBzPAQ9sXg4i&client_secret=p4NlH7i7o2JQJ9xpGdhG95eXWgX1I8teWYZo8pH5-vILSZXv&redirect_uri=https://www.oauth.com/playground/authorization-code.html&code=7RfqR_w09Ak75fZRlFCVL1ZtKUM3RR67Wd18I9tNZQwSANx9其中:必须为authorization_code上一步从认证服务器拿到的如果拿这一步有的话这里也必须一致客户端标识,如果客户端不是的话,需要对进行认证上面的例子中通过进行认证认证无误后,就可以换取到了:{ "token_type": "Bearer", "expires_in": 86400, "access_token": "Y80stMYZlsL6p6YSwwR16UiUueaV_BtuGVVtbmAj-b2Y_5u-yKtGqq2gWL2NY6ftKNMo6hin", "scope": "photo offline_access", "refresh_token": "eA-3mBXx8G9MLDzoKbJZNyV6"} 授权流程4Authorization Code Grant with PKCE上面一种流程通常推荐跟()一起使用来增强安全,区别如下:在跳转到之前,生成一个(位,包含的随机字符串)和(通过生成)。如果不支持的话,则跟一致在第一次获取的时候回发送给服务端,服务端会保存;而后在获取的时候,客户端需要发送,从而服务器可以重新进行一次来对比例如,code verifier = sz3-THfasVfv882QlbHeLsmBOdkEvgQXAYlce7MTeqzHG7Dkcode challenge = base64url(sha256(code_verifier)) = pVx7RqTYem8RYTImvRC1M4EsoaOkeqYB6I4l5tnrPWg客户端需要存储。然后在授权的中带上参数:https://authorization-server.com/authorize? response_type=code &client_id=egHuu4oJxgOLeBzPAQ9sXg4i &redirect_uri=https://www.oauth.com/playground/authorization-code-with-pkce.html &scope=photo+offline_access &state=G_SbnGGJEopEPN9A &code_challenge=pVx7RqTYem8RYTImvRC1M4EsoaOkeqYB6I4l5tnrPWg &code_challenge_method=S256同样,授权服务器会重定向到请求的上并带上和?state=G_SbnGGJEopEPN9A&code=dS6-4QKtIsX6fNBPzxo4DffXTtgufG_MLbZntG6kQwoEKXUP当客户端拿这个换取的时候,需要带上。 授权流程5POST https://authorization-server.com/tokengrant_type=authorization_code&client_id=egHuu4oJxgOLeBzPAQ9sXg4i&client_secret=p4NlH7i7o2JQJ9xpGdhG95eXWgX1I8teWYZo8pH5-vILSZXv&redirect_uri=https://www.oauth.com/playground/authorization-code-with-pkce.html&code=dS6-4QKtIsX6fNBPzxo4DffXTtgufG_MLbZntG6kQwoEKXUP&code_verifier=sz3-THfasVfv882QlbHeLsmBOdkEvgQXAYlce7MTeqzHG7Dk授权服务器会依照这个与之前的进行比较,从而防止有人通过某些途径拿到之后使用它(因为是不可逆的,除非很难通过得到原始的)。Legacy: Implicit Grant首先客户端需要生成一个授权的例如:https://authorization-server.com/authorize? response_type=token &client_id=egHuu4oJxgOLeBzPAQ9sXg4i &redirect_uri=https://www.oauth.com/playground/implicit.html &scope=photo &state=wjtEAa38CxUJbhKE其中,必须为,其他参数与前面的授权流程一样。不同的是,服务端重定向的时候,带的参数为而不是:#access_token=cXoSzbih9UYXAZEQlN7gag4sWhvpP9J941OHOhrbXzGqlA_mzC-os3u3X4_g25I1x5epxRM_ &token_type=Bearer &expires_in=86400 &scope=photos &state=wjtEAa38CxUJbhKE这种方式虽然简单,但是安全性是比较缺乏的,已经不被推荐使用: 授权流程6Legacy: Resource Owner Password Credentials (Password Grant)这种方式即通过用户名和密码来直接获取,应用需要将用户的用户名和密码发送给授权服务器来获取,已经不推荐使用。Client Credentials将客户端的认证信息作为获取的凭证,通常用于访问一些客户端自身的一些资源(而不是用户的资源)。请求的参数为:为client_credentials(:请求授权的例如,一个授权请求:POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=client_credentials这里授权服务器必须要对客户端进行认证(上面的请求中带了客户端的认证信息),如果没有问题则返回信息:{ "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"example", "expires_in":3600, "example_parameter":"example_value"}注意,在这个中是不允许包含refresh token在返回结果中的。Refresh Token在前面的流程中,获取的同时也会拿到一个,客户端可以通过这个来重新拿到一个。请求参数:必须为refresh_token返回中的请求的授权,必须是包含在最初拿时请求的中例子: 其他7POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA服务端必须要对客户端进行认证,并对进行校验(确保这个有效并且是之前签发给这个的)。Device Code对于无法使用浏览器或者说有输入限制的互联网设备,提供了一个的。在这个流程中,客户端首先请求一个:POST https://example.okta.com/deviceclient_id=https://www.oauth.com/playground/然后拿到一个{ "device_code": "NGU5OWFiNjQ5YmQwNGY3YTdmZTEyNzQ3YzQ1YSA", "user_code": "BDWD-HQPK", "verification_uri": "https://example.okta.com/device", "interval": 5, "expires_in": 1800}其中的和是需要展示给用户的。然后客户端需要一直去POST https://example.okta.com/tokengrant_type=urn:ietf:params:oauth:grant-type:device_code&client_id=https://www.oauth.com/playground/&device_code=NGU5OWFiNjQ5YmQwNGY3YTdmZTEyNzQ3YzQ1YSA当用户完成授权之后,可以得到一个{ "token_type": "Bearer", "access_token": "RsT5OjbzRn430zqMLgV3Ia", "expires_in": 3600, "refresh_token": "b7a3fac6b10e13bb3a276c2aab35e97298a060e0ede5b43ed1f720a8"}其他access_token的验证当拿到一个的时候,是需要对其进行验证的,这再中已经标准化,可以通过来进行: 其他8POST /introspect HTTP/1.1Host: server.example.comAccept: application/jsonContent-Type: application/x-www-form-urlencodedAuthorization: Bearer 23410913-abewfq.123483token=2YotnFZFEjr1zCsicMWpAA返回如下:{ "active": true, "client_id": "l238j323ds-23ij4", "username": "jdoe", "scope": "read write dolphin", "sub": "Z5O3upPC88QrAjx00dis", "aud": "https://protected.example.net/resource", "iss": "https://server.example.com/", "exp": 1419356238, "iat": 1419350238, "extension_field": "twenty-seven"}