为 Salesforce 设置 JWT 持有人 OAuth 流程 - AWS Glue

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

为 Salesforce 设置 JWT 持有人 OAuth 流程

有关启用与 OAuth 2.0 JSON 网络令牌 server-to-server 集成的信息,请参阅 Salesforce 公共文档。

创建 PEM 文件的证书/密钥对

创建 PEM 文件的证书/密钥对

openssl req -newkey rsa:4096 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem

使用 JWT 创建与 Salesforce 关联的应用程序

  1. 登录 Salesforce,点击右上角的设置齿轮,然后选择设置

  2. 在左侧,导航到应用程序管理器。(平台工具 > 应用程序 > 应用程序管理器)

  3. 选择 “新建连接应用程序”。

  4. 提供应用名称,让其余名称自动填充。

  5. 选中 “启用 OAuth 设置” 复选框。

  6. 设置回调网址。它不会用于 JWT,所以你可以使用 https://localhost。

  7. 选中 “使用数字签名” 复选框。

  8. 上传之前创建的 cert.pem 文件。

  9. 添加所需的权限:

    1. 通过 API (api) 管理用户数据。

    2. 访问自定义权限(自定义权限)。

    3. 访问身份 URL 服务(ID、个人资料、电子邮件、地址、电话)。

    4. 访问唯一的用户标识符 (openid)。

    5. 随时执行请求(refresh_token、offline_access)。

  10. 选中为指定用户发放基于 JSON 网络令牌 (JWT) 的访问令牌复选框。

  11. 选择保存

  12. 选择继续

  13. 选择管理使用者详细信息

  14. 复制使用者密钥(客户端 ID)。

  15. 复制消费者密钥(客户端密钥)。

  16. 单击 Cancel (取消)

生成 JSON 网络令牌 (JWT)

  1. 将 key pair 转换为 pkcs12(出现提示时设置导出密码)。

    openssl pkcs12 -export -in cert.pem -inkey key.pem -name jwtcert > jwtcert.p12
  2. 从 pkcs12 创建 Java 密钥库(出现提示时设置目标密钥库密码,并为源密钥库密码提供以前的导出密码)。

    keytool -importkeystore -srckeystore jwtcert.p12 -destkeystore keystore.jks -srcstoretype pkcs12 -alias jwtcert
  3. 确认 keystore.jks 包含 jwtcert 别名(出现提示时输入之前的目标密钥库密码)。

    keytool -keystore keystore.jks -list
  4. 使用 Salesforce 文档中提供的 Java 类 jwteXample 生成签名令牌。

    1. 根据需要编辑 ClaimArray 中的值:

      • ClaimArray [0] = 客户端 ID

      • ClaimArray [1] = salesforce 用户 ID

      • ClaimArray [2] = 销售人员登录网址

      • ClaimArray [4] = 自纪元以毫秒为单位的到期日期。3660624000000 是 2085-12-31。

    2. 将 path/to/keystore 替换为密钥库的正确路径.jks。

    3. 将密钥库密码替换为您输入的目标密钥库密码

    4. 用您输入的源密钥库密码替换私钥密码

    5. 编译该代码。该代码依赖于 Apache 共享资源编解码器进行 base6 4 编码。

      javac -classpath ".:./commons-codec-1.16.1.jar" JWTExample.java
    6. 运行该代码。

      java -classpath ".:commons-codec-1.16.1.jar" JWTExample
  5. 创建关联的应用程序和 JWT 后,用户仍需要获得应用程序的授权。有关两种方法,请参阅 https://mannharleen.github.io/2020-03-03-salesforce-jwt/ 中的步骤 3。

完成上述步骤后,这将输出一个 JSON 网络令牌 (JWT),该令牌可用于从 Salesforce 获取访问令牌。

示例输入:

export password for pkcs12: awsglue destination keystore password for jks: awsglue source keystore password for jks: awsglue claimArray[0] = “client-id”; claimArray[1] = “my@email.com”; claimArray[2] = "https://login.salesforce.com“; claimArray[3] = "3660624000000"; path to keystore: ./keystore.jks keystore password: awsglue privatekey password: awsglue

示例输出:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.signature

有用的链接

  • https://www.base64encode.org/

  • https://jwt.io/

  • https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_jwt_flow.htm

JWTExample.java

import org.apache.commons.codec.binary.Base64; import java.io.*; import java.security.*; import java.text.MessageFormat; public class JWTExample { public static void main(String[] args) { String header = "{\"alg\":\"RS256\"}"; String claimTemplate = "'{'\"iss\": \"{0}\", \"sub\": \"{1}\", \"aud\": \"{2}\", \"exp\": \"{3}\"'}'"; try { StringBuffer token = new StringBuffer(); //Encode the JWT Header and add it to our string to sign token.append(Base64.encodeBase64URLSafeString(header.getBytes("UTF-8"))); //Separate with a period token.append("."); //Create the JWT Claims Object String[] claimArray = new String[5]; claimArray[0] = "value"; claimArray[1] = "my@email.com"; claimArray[2] = "https://login.salesforce.com"; claimArray[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300); MessageFormat claims; claims = new MessageFormat(claimTemplate); String payload = claims.format(claimArray); //Add the encoded claims object token.append(Base64.encodeBase64URLSafeString(payload.getBytes("UTF-8"))); //Load the private key from a keystore KeyStore keystore = KeyStore.getInstance("JKS"); keystore.load(new FileInputStream("./keystore.jks"), "awsglue".toCharArray()); PrivateKey privateKey = (PrivateKey) keystore.getKey("jwtcert", "awsglue".toCharArray()); //Sign the JWT Header + "." + JWT Claims Object Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(privateKey); signature.update(token.toString().getBytes("UTF-8")); String signedPayload = Base64.encodeBase64URLSafeString(signature.sign()); //Separate with a period token.append("."); //Add the encoded signature token.append(signedPayload); System.out.println(token.toString()); } catch (Exception e) { e.printStackTrace(); } } }