之前的一篇文章OAuth2的原理和应用对OAuth2进行了简单的介绍。OAuth2的关键技术就是令牌
(access token),本文着重讨论有关令牌
的一些知识。
Token的数据对客户端是不透明的
OAuth2提供的“access_token”仅仅是一个字符串,尽管有"scope","expires_in"和"refresh_token"来辅助,但也是不完善的且分散的信息。拿OAuth2的原理和应用中的实例来说,有四个重要的概念:
-
用户
(Resource Owner)。 - 被授权的应用"云冲印"(
客户端
)。 -
资源服务器
,Google -
认证服务器
,也是Google
这些信息应该通过某种方式同令牌
关联起来,但是由于实际的实现中access_token没有包含这些信息,也就是说这些信息对于客户端
来说是不透明的。
为此,OAuth2又单独提供了一个RFC7662 -OAuth 2.0 Token Introspection来解决Token的描述信息不完整的问题。
OAuth2 Token 元数据(RFC7662 - OAuth2 Token Introspection)
简单的总结来说,这个规范是为OAuth2扩展了一个API接口(Introspection Endpoint),让第三方客户端
可以查询上面提到的那些信息(比如,access_token是否还有效,谁颁发的,颁发给谁的,scope又哪些等等的元数据信息)。
比如客户端
发起一个如下的请求:
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
其中各项含义如下:
- /introspect:是Authorization Server需要提供的API地址,Client使用Post方式请求这个地址。
- Accept:application/json:表示Authorization Server需要返回一个JSON格式的数据。
- Content-Type: application/x-www-form-urlencoded:固定此格式。
- Authorization: Bearer 23410913-abewfq.123483:其中"Bearer "是固定的在access_token前面的头部信息。为了防止token扫描攻击,这个API仍然需要某种形式的授权保护,例如bearer token【1】。
To prevent token scanning attacks, the endpoint MUST also require
some form of authorization to access this endpoint, such as client
authentication as described in OAuth 2.0 [[RFC6749](https://tools.ietf.org/html/rfc6749 ""The OAuth 2.0 Authorization Framework"")] or a separate
OAuth 2.0 access token such as the bearer token described in OAuth
2.0 Bearer Token Usage [RFC6750].
- token:必选,可以是access_token或者refresh_token的内容。
- token_type_hint:可选,表示token的类型,值为”access_token“或者"refresh_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 Web Token (JWT)
OAuth2返回Token的元数据的JSON,但是OAuth2中的access_token对客户端
仍然是不透明的字符串,如果可以把access_token的元数据信息作为access_token的字符串内容,这样是不是就可以使得它对Client是透明的了?
这样做有两个问题:
- 令牌的信息可能会被篡改。
- 在仅限ASCII的地方,传递
token
信息可能会有问题。
为了解决这些问题,JWT出现了。
定义
简单总结来说,JWT是一个定义一种紧凑的,自包含的并且提供防篡改机制的传递数据的方式的标准协议。
我们先来看一个简单的示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imxpbmlhbmh1aSJ9.hnOfZb95jFwQsYj3qlgFbUu1rKpfTE6AzgXZidEGGTk
就是这么一堆看起来像是乱码一样的字符串。JWT由3部分构成:header.payload.signature,每个部分由“.”来分割开来。
header
header是一个有效的JSON,其中通常包含了两部分:token类型和签名算法。
{
"alg": "HS256",
"typ": "JWT"
}
对这个JSON采用base64编码后就是第1部分
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9。
Payload
这一部分代表真正想要传递的数据,包含一组Claims【2】,其中JWT预定义了一些Claim:
{
"sub": "1234567890",
"name": "linianhui"
}
对这个JSON采用base64编码后就是第2部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imxpbmlhbmh1aSJ9。
Signature
这一部分是可选的,由于前面Header和Payload部分是明文的信息,所以这一部分的意义在于保障信息不被篡改用的,生成这部分的方式如下:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
token生成方使用header中指定的签名算法对“header.payload”部分进行签名,得到的第3部分hnOfZb95jFwQsYj3qlgFbUu1rKpfTE6AzgXZidEGGTk,然后组合成一个完整的JWT字符串 . 而token消费方在拿到token后, 使用同样的签名算法来生成签名,用来判断header和payload部分有没有被篡改过,因为签名的密钥是只有通信双方知道的,所以可以保证这部分信息不被第三方所篡改。
JWT 应用场景
由于其采用base64来进行编码,使得它可以安全的用在一些仅限ASCII的地方传递信息,比如URL的querystring中。
比如用户登陆后,可以把用户的一些属性信息(用户标识,是否是管理员,权限有哪些等等可以公开的信息)用JWT编码存储在cookie
中,由于其自包含的性质,每次服务器读取到Cookie的时候就可以解析到当前用户对应的属性信息,而不必再次去查询数据库。如果Cookie中每次都发送浪费带宽,也可以用 Authorization: Bearer <jwttoken
> 的方式附加到Request上去。
OAuth2 & JWT
我们可以把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
的值,感觉一下子就方便了好多。
本文的内容主要来自:
[认证 & 授权] 2. OAuth2授权(续) & JWT(JSON Web Token)
【1】https://tools.ietf.org/html/rfc7662
【2】https://www.iana.org/assignments/jwt/jwt.xhtml