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等).