開發函數
部署「API 閘道」之前,您將需要開發及部署功能。
關於商業邏輯
實行的摘要是將部分程式碼放入函數中。此代碼可能會視需要複雜,呼叫多個端點或可能執行某些聚總。商業邏輯函數是「Oracle Cloud Infrastructure API 閘道」需要時所要呼叫的程式碼。
在此範例架構中,API 閘道會呼叫一個「Oracle 函數」,它可以透過 REST API 查詢某些 Oracle Fusion Applications Cloud Service 的資料、操控它,然後將資料傳回使用者。撰寫函數本身時,您可以使用您想要的任何適當架構,但您需要知道無伺服器功能之架構的影響。在此範例中,我們使用 Java 作為語言,而 Apache HttpClient 程式庫則連線 REST Service。Apache 程式庫已被選擇、因為可以輕鬆地使用和實行;不過、在 Java 11 中現在已經有可供使用的新 HTTP 從屬端。
您也應該避免在呼叫 REST api 時建立大量記憶體內物件的架構,因為每個呼叫都會捨棄這些物件,因此功能的執行速度會變慢。
關於取得使用者名稱和角色
當函數是由「Oracle Cloud Infrastructure API 閘道」呼叫時,閘道會使用「HTTP 標頭」將部分描述資料插入呼叫中。您可以使用「函數 SDK」來存取這些標頭。
例如,在下列的 Java 程式碼片段中 (從此解決方案播放書籍提供的範例程式碼中的類別 JWTUtils
取得),我們將認證記號解碼,然後以主體的一部分傳回給呼叫者。
public OutputEvent handleRequest(InputEvent rawInput) {
Optional<string> optionalToken=rawInput.getHeaders().get("Fn-Http-H-Authorization");
if (!optionalToken.isPresent())
{
throw new Exception("No Authentication Bearer token found");
}
String jwtToken=optionalToken.get().substring(TOKEN_BEARER_PREFIX.length());
String[] split_string = jwtToken.split("\\.");
String base64EncodedHeader = split_string[0];
String base64EncodedBody = split_string[1];
String base64EncodedSignature = split_string[2];
byte[] decodedJWT = Base64.getDecoder().decode(base64EncodedBody);
try {
String JSONbody = new String(decodedJWT, "utf-8");
ObjectMapper mapper = new ObjectMapper();
JsonNode root=mapper.readTree(JSONbody);
username=root.get("sub").asText();
System.out.println(“Username = “+username);
} catch (Exception e)
{
Throw new Exception (e.getMessage());
}
Return OutputEvent.fromBytes(
username.getBytes(), // Data
OutputEvent.Status.Success,// Any numeric HTTP status code can be used here
"text/plain");
使用者名稱可用於函數內,以視需要實作業務邏輯。「認證」記號中無法使用角色,但是可以使用 REST api 從 Oracle Identity Cloud Service 查詢角色。
當 Oracle API Gateway 呼叫函數時,也會傳送一些實用的標頭。可以使用函數的 API 讀取這些標頭。功能開發人員套件中的有用標頭包括:
Fn-Http-Method | 用來呼叫函數的方法 (GET/POST/PUT等等)。 |
Fn-Http-Request-Url | 用來呼叫函數的 URL;這包括查詢參數。 |
Fn-Http-H-User-Agent | 從屬端詳細資訊,例如作業系統、廠商及版本。 |
例如,使用 Fn-Http-Method 與 Fn-Http-Request-Url 的組合,您可以在您的程式碼內實行路由器,以便您的函數根據呼叫方式來執行不同的相關作業 (例如 PUT、PATCH 等)。此方法通常稱為「無伺服器服務」樣式,並且有開發人員必須維持較少函數的好處,且每個函數都有很少的路由器來決定應該執行的作業。
public OutputEvent handleRequest(InputEvent rawInput, HTTPGatewayContext hctx) throws JsonProcessingException {
String httpMethod = hctx.getMethod();
String httpRequestURI = hctx.getRequestURL();
// etc
}
關於使用 JWT 宣告存取權杖呼叫 Oracle Fusion Applications Cloud Service
您必須使用內送「授權」標頭中的主體,從 Oracle Functions 實行 JWT 宣告。
此解決方案播放書本提供的「函數」範例可以使用名為 idcsOAuthAsserter
的協助程式庫來執行此安全程序。在呼叫 Oracle Fusion Applications Cloud Service 之前,協助程式程式庫會先交換產生器記號,以執行完整的「OAuth 宣告」流程。此程式庫已與範例函數整合。
「函數」需要私密金鑰和公用憑證,才能建立用來呼叫 Oracle Identity Cloud Service 的使用者和從屬端宣告,以使用 OAuth JWT 宣告建立新的負擔者存取權杖。
idcsOAuthAsserter
程式庫需要您可以在「函數組態」中定義的部分特性。下表中的所有變數都是必要的:
組態名稱 | 描述 | 範例 |
---|---|---|
IDCS_URL | 您的 Oracle Identity Cloud Service 執行處理 URL | https://<您的識別雲端主機名稱 |
CLIENT_ID | 與 Oracle Functions 和 Oracle API Gateway 關聯的 Oracle Identity Cloud Service 應用程式從屬端 ID | 1a2b3c4d5e6f7g8h9i01j2k3l4m5o6p7 |
KEY_ID | 匯入信任 Oracle Identity Cloud Service 應用程式之憑證的別名 | fnassertionkey |
領域 | 此範圍應與目標 OAuth 資源相符,亦即與您 Oracle Fusion Applications Cloud Service 相關聯的「Oracle Identity Cloud Service 應用程式」 | urn:opc:resource:fa:instanceid = xxxxxurn:opc:resource:consumer:::all https://my_fusion_hostname:443/ |
適用對象 | 「宣告」處理作業的對象。請使用逗號區隔值。 | myhostname.identity116 cloud.com,https://myfusionservice.dc1.oraclecloud.com |
IDDOMAIN | Oracle Fusion Applications Cloud Service 執行處理用戶的名稱 | mytenant |
此功能也需要組態特性,才能存取與 idcsOAuthAsserter
相關的宣告密碼。JWT 宣告需要憑證和私密金鑰,才能產生從屬端和使用者宣告。此函數會以 V_KEYSTORE
中指定的 OCID 擷取金鑰存放區。擷取該資訊的別名應與組態中的 KEY_ID
值相符。金鑰存放區和私密金鑰的密碼詞組應該使用 V_KS_PASS
和 V_PK_PASS
中指定的 OCID,從 Oracle Cloud Infrastructure Vault Secrets Service 擷取。
組態名稱 | 描述 | 範例 |
---|---|---|
V_KEYSTORE | 包含金鑰存放區安全儲存內容的密碼 | ocid1.vaultsecret.oc1.dc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
V_KS_PASS | 包含金鑰存放區安全儲存密碼的密碼 | ocid1.vaultsecret.oc1.dc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
V_PK_PASS | 包含私密金鑰之安全儲存密碼的密碼 | ocid1.vaultsecret.oc1.dc1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
其他支援的組態值為 USE_CACHE_TOKEN
特性,預設值為 True
。此特性可讓您儲存產生的 Oracle Identity Cloud Service 宣告記號,供日後呼叫 Oracle Fusion Applications Cloud Service 時重複使用,只要記號仍然有效。將「主體」與內送記號做比較並驗證到期時間來檢查快取記號是否仍然有效,之前會先驗證快取記號。否則,會使用「OAuth 宣告」要求新的存取權杖。若要停用此功能,請將 USE_CACHE_TOKEN
設為 False
。
要實行的函數可以使用 idcsOAuthAsserter
程式庫中的 SecurityHelper object
從 access-token
標頭擷取主體、使用 OAuth JWT 宣告產生新的 Bearer 存取權杖,以及使用新的存取權杖作為 Authorization
標頭,將要求傳送給 Oracle Fusion Applications Cloud Service。
原始程式碼範例中的函數 saasopportunitiesfn
已經與 idcsOAuthAsserter
程式庫整合。下列程式碼片段可以從範例函數的 handleRequest
評量取得,其中顯示如何起始 idcsOAuthAsserter
的物件,以及使用 Oracle Identity Cloud Service 宣告執行記號之間的事先變更:
// Full Oauth scenario Perform exchange of tokens
if(fullOAauth) {
LOGGER.log(Level.INFO, "Full Oauth Assertion scenario - Perform exchange of tokens");
SecurityHelper idcsSecurityHelper = new SecurityHelper(context) // Initialize SecurityHelper with RuntimeContext
.setOciRegion(Region.US_PHOENIX_1) // Specify the OCI region, used to retrieve Secrets.
.extractSubFromJwtTokenHeader(rawInput); // Extracts the subject from Token in Fn-Http-H-Authorization.
// Get OAuth Access token with JWT Assertion using the principal extracted from Fn-Http-H-Access-Token Header
jwttoken = idcsSecurityHelper.getAssertedAccessToken();
LOGGER.log(Level.INFO, "Successfully token retrived with IDCS Assertion");
LOGGER.log(Level.FINEST, "Access Token from assertion [" + jwttoken + "]");
}
請注意,此程式碼片段中的物件 SecurityHelper
已經使用類型為 RuntimeContext
的相關資訊環境物件起始。這將用來起始含有 idcsOAuthAsserter
Helper Library 之組態的 SecurityHelper
。
範例 saasopportunitiesfn
函數設定為使用 US_PHOENIX_1
區域,因此您應該將程式碼片段中顯示之方法 setOciRegion
中的區域變更為指向您的區域。
SecurityHelper
也使用 extractSubFromJwtTokenHeader
方法,從 handleRequest
函數方法取得 InputEvent
物件,以擷取 API Gateway Authorization
標頭中出現的 Bearer 記號。因為 Oracle Identity Cloud Service 宣告,您應該能夠擷取存取記號。
如需有關 idcsOAuthAsserter
使用狀況和與「函數」整合的詳細資訊,請複查程式碼儲存區域中 idcsOAuthAsserter
的 README 檔案,以及此解決方案播放書籍的可下載程式碼範例。
設定組態參數
Oracle Functions 環境提供一個非常實用的功能,您可以在此定義 Oracle Cloud Infrastructure 環境內的某些參數,然後從您的程式碼參照它們。
在此使用案例中,您會將 Fusion 和 OverrideJWT 記號 URL 以及名為 full_oauth
的旗標設為您程式碼中使用的參數。新增參數:
在程式碼內,您可以使用特殊 Java 註解 (@FnConfiguration
),並透過相關資訊環境變數存取參數,利用函數 SDK 來存取這些組態變數:
private String jwtoverride = "";
private String fusionHostname = "";
private String fnURIBase ="/fnsaaspoc/opportunities";
/**
* @param ctx : Runtime context passed in by Fn
*/
@FnConfiguration
public void config(RuntimeContext ctx) {
fusionHostname = ctx.getConfigurationByKey("fusionhost").orElse("NOTSET");
jwtoverride = ctx.getConfigurationByKey("overridejwt").orElse("NOTSET");
fullOAauth = Boolean.parseBoolean(ctx.getConfigurationByKey("full_oauth").orElse("false"));
LOGGER.info("Configuration read : jwt=" + jwtoverride + " saasurl=" + fusionHostname);
}
此外,由於此解決方案使用 idcsOAuthAsserter
協助程式庫,因此您必須在「函數組態」中提供「函數組態」的特定變數,才能使用 Oracle Identity Cloud Service OAuth 宣告來交換存取權杖。由於此處理作業需要數個必要組態,建議您使用 yaml 檔案方法來設定「函數」組態。例如:
config:
AUDIENCE: <AUDIENCE_VALUES>
CLIENT_ID: <YOUR_CLIENT_ID>
IDCS_URL: <YOUR_IDCS_URL>
IDDOMAIN: <YOUR_FA_TENANT_NAME>
KEY_ID: <YOUR_IDCS_URL>
SCOPE: <FA_RESOURCE_SCOPE>
V_KEYSTORE: <YOUR_KS_OCID>
V_KS_PASS: <YOUR_KSPASS_OCID>
V_PK_PASS: <YOUR_PKPASS_OCID>
fusionhost: <value>
overridejwt: <value>
full_oauth: <value>
將使用者認證記號傳送至 Fusion Applications
您的功能必須處理使用者認證權杖,才能保護 REST API 互動。
呼叫函數時,也會傳送包含認證記號的「授權」標頭變數。呼叫應用程式的識別伺服器所產生的此記號與您 Oracle Cloud Infrastructure API 閘道功能用來驗證要求的識別伺服器相同。
此解決方案播放報表簿中描述的方法,您可以使用 Oracle Identity Cloud Service OAuth JWT 宣告,利用 idcsOAuthAsserter
協助程式庫來執行記號交換。這會提供一個額外的安全層來從 Oracle Functions 呼叫 Oracle Fusion Applications Cloud Service。如架構圖表所示,「API 閘道」標頭之內送呼叫中包含之記號的「具有 OAuth JWT 宣告的函數」查詢,並使用它在「宣告」處理作業與 Oracle Identity Cloud Service 進行時交換另一個記號。這個新記號將用於對目的地伺服器 ( Oracle Fusion Applications Cloud Service ) 的外送呼叫,而且 ( Oracle Fusion Applications Cloud Service ) 將以 Oracle Visual Builder 中的使用者身分執行呼叫。
在提供的範例函數 (saasopportunitiesfn
) 中,有一個名為 full_oauth
的組態,此組態預設為 True
,其行為將在上述範例中。
您也可以選擇將 full_oauth
設為 False
。在此情況下,「函數」會查詢「API 閘道」標頭之內送呼叫中的記號,並在外送呼叫 Oracle Fusion Applications Cloud Service 中重複使用此記號。未產生第二個記號。
下列程式碼片段使用 Apache HTTP 程式庫,使用傳入的認證記號呼叫 Oracle Fusion Applications Cloud Service REST API。
String fusionURL=”<yourfusionresturl>”;
HttpClient client = HttpClients.custom().build();
HttpUriRequest request = RequestBuilder.get().setUri(fusionURL).
setHeader(HttpHeaders.CONTENT_TYPE, "application/json").
setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + jwtToken).
build();
HttpResponse response = client.execute(request);
responseJson = EntityUtils.toString(response.getEntity());
status = response.getStatusLine().getStatusCode();
選擇性定義 Oracle Cloud Infrastructure 中的認證函數
Oracle Cloud Infrastructure API 閘道原生支援 IDCS 作為認證提供者。不過,閘道也允許它所呼叫之函數的定義。您也可以使用此功能來建立自訂認證功能。
自訂認證功能會接收閘道的呼叫,並傳送內送的授權標頭。如果函數傳回 True,則允許函數呼叫;如果傳回 False,則會以 HTTP 401 錯誤代碼拒絕要求。函數是以原始程式碼格式提供,並且是部署在「函數」內,然後在 OCI API GATEWAY 部署檔案中透過其 OCID 參照這些函數。您可以瀏覽至主控台中的部署函數,並展開其 OCID 欄,以尋找 OCID。您必須先編輯 ResourceServerConfig.java
檔案之後,才能夠部署函數;這會定義函數如何連線至 Oracle Identity Cloud Service 以及使用哪些 Oracle Identity Cloud Service OAuth 應用程式。
下方的函數範例可避免硬式編碼機密值,例如用戶端密碼。在 // KMS Key for IDCS Client Secret
註解下的四行,程式碼會使用 Oracle Cloud Infrastructure 金鑰管理功能 Oracle Cloud Infrastructure Vault 來解密 OAuth 密碼。您可以在部署函數之前,先在 Oracle Cloud Infrastructure 主控台中輸入這些值。