How to authenticate with user credentials in Thingworx Rest API
Thingworx에서 개발하다보면, 특정 서비스를 호출해서 이용해야하는 경우들이 발생한다.
- Batch파일을 실행시켜 cURL로 Thingworx Source Control 서비스 호출하여 전체 Entities Export해 백업/Import 하는 경우
- 대량의 Remote Thing들을 이름과 Tag만 달리하여 Entity XML을 생성하고 Import 하는 절차를 자동화 하는 경우
- 타 시스템에서 Thingworx의 특정 값을 조회하기 위한 서비스를 실행하는 경우
- 배포 자동화를 위해 Jenkins에서 스크립트를 통해 Extension 프로젝트들을 빌드하고 Importer API를 실행하여 Extension들을 Import하는 경우
매우 다양한 경우들이 있는데, 모든 경우에 Thingworx의 API를 어떤 방식으로던 호출하기 위해서는 인증을 통과해야하고, 인증을 통과하기 위한 방법은 두가지가 있다.
- Application Key 방식
- ID, Password 방식
두가지 방식이 Request에 실어 보내는 방법이 조금 다른데, 기본적으로 Thingworx 는 외부에서 API 호출하는 방식으로 서비스를 사용할 때는 Application Key 방식을 강하게 권장한다.
(사실 ID, Password 방식을 사용하는 아티클은 본적이 없는 것 같긴한데 일단 방법을 소개는 하겠다)
Application Key 방식 인증
Thingworx에서 New Entity를 클릭하여 Application Key Entity를 생성할 수 있다.
AppKey 생성 시 AppKey 이름과, 로그인 유저 지정이 필수이며 AppKey 만료일 지정 또한 필수이다.
많은 프로젝트에서 잘 되다가 갑자기 동작이 안되는 기능들 중 많은 원인이 AppKey 만료일에 있다.
기본으로 설정되는 AppKey 만료일은 매우 짧기 때문에, 시스템에서 계속 사용하는 인증 방식이면 만료일을 길게 설정을 변경하는것을 잊지말자.
참고로 만료일 지정 시 캘린더가 열리면서 연/월/일 순서대로 클릭해서 지정하게 되어있는데, 일자까지 정확히 지정하지 않으면 만료일이 변경되지 않으니 꼭 일자까지 클릭해서 변경이 잘 되어있는지 꼼꼼히 확인하자.
많은 경우에 만료일에서 실수가 일어난다.
Application Key 사용 예제
아래의 사용 예제는 Batch 파일로 만들었던 예제인데, Thingworx SourceControlFunctions Resource에 전체 Entities Export를 cURL로 요청하는 스크립트이다.
서버의 특정 Repository 하위에 Export하도록 설정되어있고, cURL 결과 응답 코드에 따라 성공/실패 여부를 출력해 준다.
프로젝트와 Collection 등을 더 세분화 한 내용을 지정해서 API를 호출할 수 있지만, 본 예제에서는 전체 프로젝트와 전체 콜렉션 대상으로 한다.
@echo off
setlocal enabledelayedexpansion
set "TW_URL=http://localhost:8080"
set "ENDPOINT=%TW_URL%/Thingworx/Resources/SouceControlFunctions/Services/ExportSourceControlledEntities"
set "APP_KEY=123123-1233-1234-1234-12341234123"
:: ===사용자 정의 변수===
set "REPO_NAME=TestRepository"
set "EXPORT_PATH=/"
set PATH=%PATH%;C:\Windows\System32;C:\Windows\System32\WindowsPowerShell\v1.0
:: ==== Export ====
echo [INFO] Exporting collection: !current!
set "PAYLOAD={\"repositoryName\":\"%REPO_NAME%\", \"path\":\"%EXPORT_PATH%\",\"exportMatchingModelTags\":true,\"includeDependents\":false,\"collection":\"\"}"
echo [DEBUG] Payload: !PAYLOAD!
:: -w/--write-out <format> Defines what to display on stdout after a completed and successful operation.
set STATUS=
for /f {http_code}" ^
-X POST "%ENDPOINT%" ^
-H "Content-Type: application/json" ^
-H "Accept: text/xml" ^
-H "appKey: %APP_KEY%" ^
-d "!PAYLOAD!"') DO (
set "STATUS=%%i"
)
echo [HTTP Response Code] - !STATUS!
if "!STATUS!"=="200" (
echo [INFO] Done Export
) else (
echo [ERROR] Fail Request. check URL: %TW_URL%
)
echo.
endlocal
pause
ID, Password 방식 인증
ID/Password 방식은 AppKey와 다르게 Plain Text로 실어보낼 수 없다.
{id}:{password} 형태의 텍스트의 Byte를 얻어 Base64로 인코딩한 뒤 “Basic” 이라는 문자열과 합쳐서 Authorization key에 실어보내는 방식으로 사용한다.
⚠️ 공식 문서를 참고한 방법이 아니므로, 반드시 테스트가 필요한 방식이고 버전에 따라 차이가 있을 수 있습니다.
⚠️ Thingworx 8.3대에서 테스트 되었던 방식으로, 최신 버전에서는 같은 방식으로 동작하지 않을 수 있습니다.
⚠️ 보안상 소스에 id/password를 하드코딩하는 방식은 위험하고 권장하지 않습니다. 해당 인증방식은 참고용으로만 봐주세요.
ID, Password 방식 사용 예제
Best Practice는 아니긴 하지만.. 먼 옛날에 프로젝트에서 사용했던 Sample을 살펴보도록 하자.
본 예제는 특정 서비스에서 차트용 json을 추출하기 위해 테스트 했던 코드이다.
비밀번호가 ptc인 develop2라는 유저로 로그인을 시도한다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
private String auth = "develop2:ptc";
public JSONObject getChartResponseFromTWX(String jsonValue, String URLString) {
BufferedReader in = null;
StringBuffer buffer = null;
try {
URL url = new URL(URLString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
byte[] authEncBytes = Base64.encodeBase64(auth.getBytes());
String authStringEnc = new String(authEncBytes);
conn.setDoOutput(true);
conn.setRequestProperty("Authorization","Basic" + authStringEnc);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept", "application/json-compressed");
conn.setRequestProperty("_twsr", "1");
OutputStream os = conn.getOutputStream();
os.write(jsonValue.getBytes("UTF-8"));
os.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
buffer = new StringBuffer();
String line;
while((line = in.readLine())!=null) {
buffer.append(line);
}
JSONParser parser = new JSONParser();
Object obj = parser.parse(buffer.toString());
JSONObject jsonObj = (JSONObject) obj;
JSONArray jsonArray = (JSONArray) jsonObj.get("array");
JSONObject chartJsonWithId = (JSONObject) jsonArray.get(0);
JSONObject pureChartJson = (JSONObject) chartJsonWithId.get("chart");
return pureChartJson;
} catch(Exception e) {
e.printStackTrace();
} finally {
if(in !=null) try {in.close();} catch(Exception e) {e.printStackTrace();}
}
return null;
}인증과 관련된 공식 Document 정보
정확한 정보는 Updating, Deleting, and Executing Through the API의 Passing in Authentication with your REST API Call 항목을 참고하길 바란다.