JWT Bearer Flow 是一种 OAuth 流程,其中外部应用程序 (也称为客户端) 向 Salesforce 发送一个称为 JWT
的签名 JSON
字符串,以获得一个访问令牌(access token)
. 然后外部应用程序可以使用该访问令牌来读取和写入 Salesforce 中的数据。
与其他一些 OAuth 流程不同的是,JWT 流程不需要最终用户操作。外部应用程序发送 JWT 并进行自我认证,无需人工干预。
在本文开始之前,先来简单介绍一下 JWT 的构成,以此希望大家对整个 JWT Bearer Flow 的理解更加的清楚。
JWT 的数据结构
真实的 JWT 大概像下面这样:
JWT 是一个很长的字符串,中间用点(.)
分隔成三个部分。需要注意的是:JWT 内部是没有换行的。
这三个部分组成如下:
- Header(头部)
- Payload(载荷)
- Signature(签名)
Header
JWT 的头部有两部分信息:
- 声明类型,JWT 令牌统一写为 JWT
- 声明加密的算法,默认为 HMAC SHA256, 简写后 HS256
示例:
1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}
然后将头部通过 Base64URL 加密算法转成字符串,构成了第一部分。
1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据.JWT 规定了 7 个官方字段,但是这些字段不是必须的但是是推荐使用的。
- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
示例:
1
2
3
4
5
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
这部分的数据也是经过 Base64Url 进行加密的,这部分加密的内容组成了 JWT 的第二部分。
请注意:针对令牌这部分的签名已经被防范篡改。但是这部分还是可以被解密的,因此请不要将任何密钥放到这部分的数据中,除非你的密钥是已经加密过的密钥。
Signature
Signature 部分是对前两部分的签名,防止数据篡改。首先,需要指定一个密钥 (secret).这个密钥只有服务器才知道,不能泄露给用户。
例如,如果你希望使用 HMAC SHA256 算法来进行签名,那么这个算法中使用的数据为:
1
2
3
4
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
签名的作用主要用于校验传输的令牌 (Token) 数据没有在过程中被篡改。
算出签名以后,将上面的三个部分用.
连接成一个完整的字符串,就构成了最终的 jwt.
Salesforce OAuth JWT Flow
接下来让我们开始今天的内容。在这个例子中,我们将介绍如何使用 Salesforce 中的 JWT 流程获取 Access Token.
生成 private key 和 cert
作用:外部应用程序使用 private key 来签名 JWT, 而数字证书 (cert) 则由 Salesforce 用来验证签名并签发访问令牌。
使用 JWT Bearer Token flow 的第一步是创建证书。为了能够创建证书,你需要安装 OpenSSL
步骤 1: 创建 RSA private key
运行以下 OpenSSL 命令:
1
openssl genrsa -des3 -passout pass:xxxx -out server.pass.key 2048
结果:
步骤 2: 从 server.pass.key 文件中创建 key file
运行以下 OpenSSL 命令:
1
2
openssl rsa -passin pass:xxxx -in server.pass.key -out server.key
rm server.pass.key (可选的, 可以删除这个文件)
结果:
步骤 3: 生成证书
运行以下 OpenSSL 命令:
1
openssl req -new -key server.key -out server.csr
结果:
步骤 4: 生成 SSL 证书
运行以下 OpenSSL 命令:
1
openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
结果:
在 Salesforce 中创建一个 Connected App
为了连接到我们的 Salesforce org,我们需要创建一个 Connected App.如果你是在 Lightning 或者 Classic 中,创建方式会略有不同。
创建 connected app
- Lightning:
Setup
⇨App Manager
, 点击New Connected App
- Classic:
Setup
⇨ Apps, 在Connected Apps
列表选项中,点击New
.
- 填入
Name
和Email
- 开启
Enable OAuth Settings
- Callback URL:
https://login.salesforce.com/services/oauth2/callback
- 开启
Use digital signatures
, 上传在上一步创建的数字证书 (server.crt) - 选择 OAuth 范围:
- Manage user data via APIs (api)
- Manage user data via Web browsers (web)
- Perform requests at any time (refresh_token, offline_access)
- 点击
Save
预先批准 connected app
为什么需要这一步骤呢?先说结论,如果没有这一步,直接生成 JWT Token, 然后调用 Salesforce API 获取 Access Token 是会报错的,如下:
1
{"error":"invalid_grant","error_description":"user hasn't approved this consumer"}
从技术上讲,我们的应用程序发送的 JWT Token 进行认证,但是 salesforce 此时并不知道哪些资源是可用的,并得到了用户的许可。通常情况下,我们可以发送 scope
参数作为 JWT 的一部分去实现,但 Salesforce 不支持这样做。参考文档.
简单地说,这意味着在使用 jwt flow 之前至少获得一次刷新令牌 (下面的第二个选项)[选项 2], 或执行后端审批[下面的第一个选项].
这里提供了几个不同的选项可以做到这一点:
- 选项 1: 管理员从 Salesforce 的 connected app 中进行审批
- 选项 2: 用
User-Agent OAuth Flow
预先批准 connected app
选项 1
执行如下步骤:
Setup
->App Manager
->YOUR APP
->Manage
->Edit Policies
- 在
Permitted Users
选择Admin approved users are pre-authorized
- 点击
Save
. - 点击
Manage Profiles
- 选择
System Administrator
- 点击
Save
选项 2
将下面链接中的参数替换为你自己的,然后复制链接到浏览器中:
1
https://<your instance>.salesforce.com/services/oauth2/authorize?response_type=token&client_id=<consumer key>&redirect_uri=https://login.salesforce.com/services/oauth2/callback
创建 JWT Token
你可以使用你喜欢的开发语言的库来创建 JWT Token,但我这里只使用一个在线工具来演示。
JWT Header
1
2
3
4
{
"alg": "RS256",
"typ": "JWT"
}
JWT Payload
1
2
3
4
5
6
{
"iss": "Consumer key",
"sub": "Username",
"aud": "https://<login or test>.salesforce.com",
"exp": "now + 2 minutes in Unix timestamp"
}
其中 exp
的时间,可以通过将下面 JS 复制到 chrome console 里:
1
2
3
d = new Date();
d.setSeconds(d.getSeconds() + 180); // 有效期为 3 分钟
d.valueOf();
Signature
1
2
3
4
5
RSASHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
Private Key
)
从之前步骤中生成的 Server.Key
中复制 Private Key
通过 POSTMAN 使用 JWT 获取 Access Token
复制上一个步骤生成的 JWT Token:
总结
JWT flow 在 Salesforce 生态系统中被广泛使用,以使外部应用程序能够访问 Salesforce 数据,而无需用户手动干预。通过向 Salesforce 发送签名的 jwt, 我们获得一个访问令牌 (Access Token). 上面的步骤应该适用于您的外部应用程序所使用的任何编程语言 (java,node,python 等).