在上篇[认证受权] 1.OAuth2受权 中介绍到了OAuth2能够帮咱们解决第三方Client访问受保护资源的问题,可是只提供了如何得到access_token,并未说明怎么来撤销一个access_token。关于这部分OAuth2单独定义了一个RFC7009 - OAuth 2.0 Token Revocation来解决撤销Token问题。html
OAuth2提供的“access_token"是一个对Client不透明的字符串,尽管有"scope","expires_in"和"refresh_token"来辅助,但也是不完善的且分散的信息。还拿上一篇的小明来举例,“小明受权在线打印而且包邮的网站访问本身的QQ空间的相册”。双引号里面的这句话其中有4个重要的概念:web
那么如何获得得到上面提到的这些附加的信息呢?OAuth2又单独提供了一个RFC7662 -OAuth 2.0 Token Introspection来解决Token的描述信息不完整的问题。算法
这些信息不但对Client不透明,对于资源服务器来讲也是不透明的,好比受权服务器和资源服务器是独立部署的,而OAuth2又要求资源服务器要对access token作校验,没有这些信息如何校验呢?除非在access token的db存储层面作共享,可是做为一个运行在互联网规模上的网络环境下的协议,这种假设是没法支撑互联网规模的环境的。数据库
简单来讲,这个协议规定了一个Authorization server提供一个怎样的API来供Client撤销access_token或者refresh_token。json
好比Client发起一个以下的请求:安全
POST /revoke HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token服务器
其中各项含义以下:cookie
若是撤销成功,则返回一个HTTP status code为200的响应就能够了。网络
简单的总结来讲,这个规范是为OAuth2扩展了一个API接口(Introspection Endpoint),让第三方Client能够查询上面提到的那些信息(好比,access_token是否还有效,谁颁发的,颁发给谁的,scope又哪些等等的元数据信息)。app
好比Client发起一个以下的请求:
POST /introspect HTTP/1.1
Host: server.example.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer 23410913-abewfq.123483
token=2YotnFZFEjr1zCsicMWpAA&token_type_hint=access_token
看起来和上面的撤销Token的请求差很少,其中各项含义以下:
若是请求成功,则会返回以下的信息:
1 { 2 "active": true, 3 "client_id": "l238j323ds-23ij4", 4 "token_type":"access_token", 5 "username": "jdoe", 6 "scope": "read write dolphin", 7 "sub": "Z5O3upPC88QrAjx00dis", 8 "aud": "https://protected.example.net/resource", 9 "iss": "https://server.example.com/", 10 "exp": 1419356238, 11 "iat": 1419350238, 12 "nbf": 1419350238, 13 "jti": "abcdefg" 14 "extension_field": "twenty-seven" 15 }
JSON各项属性含义以下(其中有些信息是在JSON Web Token中定义的,参考连接有详细的介绍):
其中大量的信息都是可选的信息,并且能够本身扩展须要的属性信息,从这些属性中就能够解决咱们上面提到的access_token对于Client不透明的问题。
咱们注意到其中有不少属于JWT定义的属性,那么这个JWT是什么东西?它解决了什么问题?
简单总结来讲,JWT是一个定义一种紧凑的,自包含的而且提供防篡改机制的传递数据的方式的标准协议。
咱们先来看一个简单的示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imxpbmlhbmh1aSJ9.hnOfZb95jFwQsYj3qlgFbUu1rKpfTE6AzgXZidEGGTk
就是这么一堆看起来像是乱码同样的字符串。JWT由3部分构成:header.payload.signature,每一个部分由“.”来分割开来。
header是一个有效的JSON,其中一般包含了两部分:token类型和签名算法。
{ "alg": "HS256", "typ": "JWT" }
对这个JSON采用base64编码后就是第1部分eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9。
这一部分表明真正想要传递的数据,包含一组Claims,其中JWT预约义了一些Claim(2. Token 元数据 这一节就用到一些JWT预约义的一些Cliam)后面会介绍。关于什么是Claim,能够参考文章末尾给的参考连接。
{ "sub": "1234567890", "name": "linianhui" }
对这个JSON采用base64编码后就是第2部分eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imxpbmlhbmh1aSJ9。
这一部分是可选的,因为前面Header和Payload部分是明文的信息,因此这一部分的意义在于保障信息不被篡改用的,生成这部分的方式以下:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
token生成方使用header中指定的签名算法对“header.payload”部分进行签名,获得的第3部分hnOfZb95jFwQsYj3qlgFbUu1rKpfTE6AzgXZidEGGTk,而后组合成一个完整的JWT字符串 . 而token消费方在拿到token后, 使用一样的签名算法来生成签名,用来判断header和payload部分有没有被篡改过,由于签名的密钥是只有通讯双方知道的,因此能够保证这部分信息不被第三方所篡改。
JWT规范中预先定义了一些Cliam,但并非必选的,经常使用的有:
iss(Issuer签发者)
。sub(subject签发给的受众,在Issuer范围内是惟一的)
。aud(Audience接收方)
。exp(Expiration Time过时时间)
。iat(Issued At签发时间)等等。
更完整的一些Claim列表参见:https://www.iana.org/assignments/jwt/jwt.xhtml
若是上面这些仍没法知足本身的须要,则能够自定义一些来使用。
因为其采用base64来进行编码,使得它能够安全的用在一些仅限ASCII的地方传递信息,好比URL的querystring中。
好比用户登录后,能够把用户的一些属性信息(用户标识,是不是管理员,权限有哪些等等能够公开的信息)用JWT编码存储在cookie中,因为其自包含的性质,每次服务器读取到Cookie的时候就能够解析到当前用户对应的属性信息,而没必要再次去查询数据库。若是Cookie中每次都发送浪费带宽,也能够用 Authorization: Bearer <jwttoken> 的方式附加到Request上去。
注意到咱们在2. Token 元数据 这一小节中,OAuth2返回Token的元数据的JSON,以及OAuth2中的access_token对Client是不透明的字符串这件事,咱们能够把access_token的元数据信息用JWT来编码如下,做为access_token的字符串内容,这样是否是就可使得它对Client是透明的了。
好比我以前遇到的问题,在我使用access_token的时候有没有过时我并不知道,其实须要借助辅助的“expires_in”来检查,还有其scope是哪些,也须要额外的去查询,再好比这个access_token管理的用户是谁,也须要额外的查询,有了JWT呢,能够把这些都打包进去,好比:
{ "sub":"linianhui", "scope":"1419356238", "exp":123456789, }
而后生成一个这样的jwt字符串 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsaW5pYW5odWkiLCJzY29wZSI6IjE0MTkzNTYyMzgiLCJleHAiOjEyMzQ1Njc4OX0.ASu85ohHMSOhnxbJSJI4OKLsPlbjPs7th0Xw5-b4l1A 做为access_token的值,感受一会儿就方便了好多吧。
OAuth2在RFC6749中并未完整的提供一些问题的解决方案,而是附加了一些相关的RFC来解决这些问题,其实除了本文中提到的2个问题点以外,还有一些其余能够优化的地方存在(好比服务发现:https://tools.ietf.org/html/draft-ietf-oauth-discovery-06),From Post Response Mode :http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html),这些点在后续的OIDC的文章中再作介绍吧,感兴趣的能够看一看http://openid.net/connect/中关于OAuth2的另一些相关扩展标准草案,这些标准也是OIDC所须要的一些可选支持;以及OAuth相关扩展草案:https://datatracker.ietf.org/wg/oauth/charter/。另外在一些场景下,使用JWT来使得OAuth2的提供自包含的Token仍是一件很方便的事情的。
以上内容均是我的的一些理解,若是错误之处,欢迎指正!
JSON协议:RFC7159 - The JavaScript Object Notation (JSON) Data Interchange Format
OAuth2 扩展协议:
RFC7009 - OAuth 2.0 Token Revocation
RFC7662 - OAuth 2.0 Token Introspection
OAuth相关扩展草案:
https://datatracker.ietf.org/wg/oauth/charter/
https://tools.ietf.org/wg/oauth/
JWT相关协议族:
RFC7515 - JSON Web Signature (JWS)
RFC7516 - JSON Web Encryption (JWE)
RFC7518 - JSON Web Algorithms (JWA)
RFC7519 - JSON Web Token (JWT)
JWT官方站点:https://jwt.io
Claims:https://en.wikipedia.org/wiki/Claims-based_identity
JWT注册的的一组Claims : https://www.iana.org/assignments/jwt/jwt.xhtml