인증 토큰 만들기
인증 절차에 사용할 인증서 및 개인키는 Salesforce Connected App 설정 설명에서 사용된 인증서(company.crt)에 대응하는 만든 키스토어 또는 개인키를 사용합니다. company.jks, company.key 파일을 프로젝트의 루트 폴더에 복사를 해둡니다. 실제로는 jks, key 둘 중에 한가지만 필요합니다. 뒤에서 설명하는 인증서/개인키 사용 방법에 따라 선택을 하시면 됩니다. Client Key와 Connected App에 접속할 수 있는 사용자 Id도 준비를 해둡니다.
Class 만들기
인증 받기 코드에 사용할 Java Class를 생성합니다.
이 설명에서는 Maven 프로젝트를 기준으로 설명을 합니다.
-
JAVA PROJECTS의
App오른쪽의 ‘+' 기호를 클릭합니다.
-
Select resource type to create. 목록에서 Class를 선택합니다.

-
생성하려는 클래스 이름을 입력하고 엔터키를 누릅니다. 여기서는 Auth 클래스를 생성합니다.

-
Auth.java Java 클래스 파일이 생성됩니다. 같은 방법으로 ConnectionInfo 클래스를 생성합니다.

ConnectionInfo.java 파일을 열고 아래와 같이 내용을 추가한 후에 저장을 합니다. 데이터 조작을 위한 API를 사용할때 필요한 instanceUrl과 accessToken을 처리하기 위한 클래스입니다.
public class ConnectionInfo { private String instanceUrl; private String accessToken; public void setInstanceUrl(String param){ this.instanceUrl = param; } public String getInstanceUrl(){ return this.instanceUrl; } public void setAccessToken(String param){ this.accessToken = param; } public String getAccessToken(){ return this.accessToken; } }
코드 구성
인증을 받기 위하여 Salesforce Connnected App에 전송하는 토큰은 다음과 같이 .으로 연결된 3부분으로 이루어져 있습니다.
[JWT 헤더].[인증 요청정보].[개인키로 암호화된 헤더+요청정보] (JWT Header.Claim Payload.Header + Signed Payload)
순서대로 만들어 보도록 하겠습니다.
-
Auth.java클래스 윗 부분에 아래의 내용을 추가를 합니다.package com.example; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.file.Files; import java.security.KeyFactory; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Signature; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.spec.PKCS8EncodedKeySpec; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import org.apache.commons.codec.binary.Base64; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; public class Auth { }사용하지 않는 패키지는 나중에 제거할 수 있습니다.
-
가장 먼저 개인키(PrivateKey)를 프로그램에 적재하여야 합니다. 개인키를 적재하는 방법은 아래의 세가지 방법이 있으니 상황에 맞는 방법을 사용하시면 됩니다.
-
JKS 키스토어의 개인키를 적재하는 방법
아래의 메소드 코드를
Auth클래스에 추가합니다.public static PrivateKey getPrivateKeyFromJKS(String filePath, String alias, String password) throws Exception { PrivateKey privateKey = null; try { //Load the private key from a jks keystore KeyStore keystore = KeyStore.getInstance("JKS"); keystore.load(new FileInputStream(filePath), password.toCharArray()); privateKey = (PrivateKey) keystore.getKey(alias, password.toCharArray()); } catch (Exception e) { System.out.println(e); } return privateKey; }App.java파일의main메소드에 아래와 같이 호출 코드를 추가하고 ‘Run'을 실행해 봅니다.public static void main( String[] args ) throws Exception { try { PrivateKey privateKey = Auth.getPrivateKeyFromJKS("./company.jks", "company", "password"); if(privateKey != null) { System.out.println("Private Key loaded..."); } } catch(Exception e) { System.out.println(e); } }- company.jks : jks 키스토어 파일입니다.
- company : jks 키스토어를 생성할 때
-destalias로 지정한 alias입니다. - password : jks 키스토어를 생성할 떄 지정한 암호입니다.
-
개인키 파일의 개인키를 스트링으로 적재하는 방법
아래의 메소드 코드를
Auth클래스에 추가합니다.public static PrivateKey getPrivateKeyFromString(String pkcs8PrivateKeyString) throws Exception { PrivateKey privateKey = null; try { KeyFactory factory; factory = KeyFactory.getInstance("RSA"); byte[] encodedPK = Base64.decodeBase64(pkcs8PrivateKeyString); PKCS8EncodedKeySpec keySpecPv = new PKCS8EncodedKeySpec(encodedPK); privateKey = factory.generatePrivate(keySpecPv); } catch (Exception e) { System.out.println(e); } return privateKey; }App.java파일의main메소드에 아래와 같이 호출 코드를 추가하고 ‘Run'을 실행해 봅니다.company.key 파일을 노트패드에서 열고, "—–BEGIN PRIVATE KEY—–" 행과 "—–END PRIVATE KEY—–" 행 사이의 문자열을 개행 문자를 제외하고
pkcs8PrivateKeyString변수에 지정을 합니다.public static void main( String[] args ) throws Exception { try { String pkcs8PrivateKeyString = "" + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDN4c80VU9yjbAu" + "JWq3RYPBsLnP2wcipVdGIzPYLqyXUq2GBlhyaLjN0NI5mO+AyXE8FSJtD/Y22ILQ" + "..." + "..." + "..." + "Qmh4OV5ZHXwx5R6ffAOAwAY="; PrivateKey privateKey = Auth.getPrivateKeyFromString(pkcs8PrivateKeyString); if(privateKey != null) { System.out.println("Private Key loaded..."); } } catch(Exception e) { System.out.println(e); } } -
개인키 파일의 개인키를 적재하는 방법
이 메소드를 사용하려면 아래의 라이브러리를 추가로 지정하여야 합니다.
-
Maven dependency에 추가 (pom.xml)
bcpkix-jdk18on(org.bouncycastle) -
Gradle dependency에 추가 (build.gradle)
org.bouncycastle:bcpkix-jdk18on:1.77
Auth.java파일에 import할 패키지를 추가합니다.import java.io.FileReader; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemReader;Auth.java파일에 아래의 메소드를 추가합니다.public static PrivateKey getPrivateKeyFromKeyFile(String filePath) throws Exception { PrivateKey privateKey = null; File file = new File(filePath); try (FileReader keyReader = new FileReader(file); PemReader pemReader = new PemReader(keyReader)){ PemObject pemObject = pemReader.readPemObject(); byte[] content = pemObject.getContent(); PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content); KeyFactory factory = KeyFactory.getInstance("RSA"); privateKey = (PrivateKey) factory.generatePrivate(privKeySpec); } catch (Exception e) { System.out.println(e); } return privateKey; }App.java파일의main메소드에 아래와 같이 호출 코드를 추가하고 ‘Run'을 실행해 봅니다.public static void main( String[] args ) throws Exception { try { PrivateKey privateKey = Auth.getPrivateKeyFromKeyFile("./company.key"); if(privateKey != null) { System.out.println("Private Key loaded..."); } } catch(Exception e) { System.out.println(e); } }company.key : 개인키 파일입니다.
-
위의 세가지 방법중의 하나를 사용하도록 합니다.
-
-
인증 요청 Token을 만들기 위하여
Auth클래스에 아래의 메소드를 추가합니다.public static StringBuffer getToken(String clientId, String loginUrl, String loginId, PrivateKey privateKey) { StringBuffer token = new StringBuffer(); try { ////////////////////// // JWT Token Header // ////////////////////// String header = "{\"alg\":\"RS256\"}"; //Encode the JWT Header and add it to our string to sign token.append(Base64.encodeBase64URLSafeString(header.getBytes("UTF-8"))); ////////////////////// // Claim Payload // ////////////////////// //Separate with a period token.append("."); //Create the JWT Claims Object String claimTemplate = "'{'\"iss\": \"{0}\", \"sub\": \"{1}\", \"aud\": \"{2}\", \"exp\": \"{3}\"'}'"; String[] claimArray = new String[4]; claimArray[0] = clientId; claimArray[1] = loginId; claimArray[2] = loginUrl; 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"))); ////////////////////// // Private Key // ////////////////////// //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){ System.out.println(e); } return token; }-
clientId : ConnectedApp에서 제공하는 Consumer Key 입니다.
-
loginUrl : login.salesforce.com 또는 test.salesforce.com을 사용합니다.
-
loginId : ConnectedApp에서 Profile 또는 Permission Set으로 잡속이 허용된 이메일 형식의 사용자 계정입니다.
이 코드로 생성되는 Token 값은 Salesforce의 JWT Bearer 플로우를 위한 값입니다. 다른 시스템들은 Heade와 Claim이 다른 형식을 사용할 수 있습니다.
App.java파일의main메소드에 아래와 같이 호출 코드를 추가하고 ‘Run'을 실행해 봅니다.public static void main( String[] args ) throws Exception { try { PrivateKey privateKey = Auth.getPrivateKeyFromKeyFile("./company.key"); ////////////////// // Make a Token // ////////////////// String consumerKey = "3MVG9YDQS5WtC11q60F............QYX68awYhUzKVMMB"; String loginUrl = "https://login.salesforce.com/"; String loginUser = "username@example.com"; StringBuffer token = Auth.getToken(consumerKey, loginUrl, loginUser, privateKey); if(token != null) { System.out.println(token); } } catch(Exception e) { System.out.println(e); } }eyJhbGciOiJSUzI1NiJ9.ey....와 같은 인코딩된 문자열이 출력되면 정상입니다. -