[英]HTTPS POST Request from Java Application to OData-Service in S/4HANA System with valid CSRF-Token
情況如下:一方面,我創建了一個 OData 服務,它在收到 POST 請求時應該創建一個條目。 該服務在 S/4HANA 系統中創建,可通過 SAP 網關訪問。
另一方面,我有一個 Java 應用程序 (OpenJDK 11),它本質上執行一個循環,並且必須向每個循環發出一個 POST 請求到 OData 服務。
我正在使用 IntelliJ IDEA 社區版和 OpenJDK 11。這也是我第一次將 OData 與 Java 和 SAP 一起使用。
起初我嘗試了以下方法:
private static void postRequest() throws IOException {
//Setting authenticator needed for login
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password.toCharArray());
}
};
Authenticator.setDefault(authenticator);
//Creating the connection
URL url = new URL("<my_service_link>");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json; utf-8");
con.setRequestProperty("Accept", "application/json");
con.setDoOutput(true);
try(OutputStream os = con.getOutputStream()) {
byte[] input = this.getJsonRequest().getBytes("utf-8");
os.write(input, 0, input.length);
}
//Reading response
int status = con.getResponseCode();
Reader streamReader = null;
if (status > 299) {
streamReader = new InputStreamReader(con.getErrorStream());
} else {
streamReader = new InputStreamReader(con.getInputStream());
}
BufferedReader in = new BufferedReader(streamReader);
String inputLine;
StringBuffer content = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
con.disconnect();
System.out.println(content.toString());
}
但是我得到了錯誤,我的 CSRF-Token 無效。
因此,在谷歌搜索找出什么是 CSRF-Token 之后,我嘗試首先使用自己的HttpsURLConnection
創建一個 GET-Request:
private static String getRequest() {
//Setting authenticator needed for login
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password.toCharArray());
}
};
Authenticator.setDefault(authenticator);
//Creating the connection
URL url = new URL("<my_service_link>");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Content-Type", "application/json; utf-8");
con.setRequestProperty("X-CSRF-Token","fetch");
con.connect();
return con.getHeaderField("x-csrf-token").toString();
}
然后我會向同一個 URL 發出實際的 POST-Request,並將之前的 X-CSRF-Token 設置到 HTTPS-Header 中
con.setRequestProperty("X-CSRF-Token",theGETToken);
在postRequest()
中
但我仍然遇到同樣的錯誤。
我究竟做錯了什么?
經過更多的谷歌搜索后,我終於明白了我錯過了什么。
CSRF-Token 僅對用戶的特定session有效。 session 由 HTTPS 標頭中傳遞的 cookies 標識。
需要做的是以下內容(另請參閱: https://blogs.sap.com/2021/06/04/how-does-csrf-token-work-sap-gateway/ ):
HTTP-Request: Type: GET Header-Fields: x-csrf-token = fetch set-cookie = fetch
HTTP-Request: Type: POST Header-Fields: x-csrf-token = <tokenFromGet> cookie = <allSessionCookies>
請注意,請求的 header 字段名為cookie
而不是set-cookie
,並將 set- set-cookie
的 HeaderField 的所有值傳遞給 POST-Request-Header。
值得一提的是,CSRF 令牌和會話 cookie 在提供或調整的時間范圍或對 session 進行任何更改后過期,並且必須重新獲取兩者(請參閱https://blogs.sap。 com/2021/06/04/how-does-csrf-token-work-sap-gateway/#comment-575524 )。
我的工作代碼示例:
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URL;
public class ODataLogger {
private String sessionCookies;
private String csrfToken;
public ODataLogger() {}
public void logOdata (String user, String pass, String jsonBody) throws IOException {
this.setDefaultAuthenticator(user, pass);
fetchSessionHeaderFields();
postRequest(jsonBody);
}
private void setDefaultAuthenticator (String user, String pass) {
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, pass.toCharArray());
}
};
Authenticator.setDefault(authenticator);
}
private void fetchSessionHeaderFields() throws IOException {
URL url = new URL("<my-service-link>");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("x-csrf-token", "fetch");
con.setRequestProperty("set-cookie","fetch");
//Reading Response
int status = con.getResponseCode();
Reader streamReader = null;
if (status < 299) {
StringBuffer sb = new StringBuffer(con.getHeaderFields().get("set-cookie").toString());
//Delete leading [ and trailing ] character
sb.deleteCharAt(this.sessionCookies.length()-1);
sb.deleteCharAt(0);
this.sessionCookies = sb.toString();
this.csrfToken = con.getHeaderField("x-csrf-token");
return;
}
}
private void postRequest(String jsonBody) throws IOException {
//Creating the connection
URL url = new URL("<my-service-link>");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("x-csrf-token", this.csrfToken);
con.setRequestProperty("Cookie", this.sessionCookies);
con.setRequestProperty("Accept", "application/json");
//Setting JSON Body
con.setDoOutput(true);
try(OutputStream os = con.getOutputStream()) {
byte[] input = jsonBody.getBytes("utf-8");
os.write(input, 0, input.length);
}
//Reading response
int status = con.getResponseCode();
Reader streamReader = null;
if (status > 299) {
streamReader = new InputStreamReader(con.getErrorStream());
} else {
streamReader = new InputStreamReader(con.getInputStream());
}
BufferedReader in = new BufferedReader(streamReader);
String inputLine;
StringBuffer content = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
con.disconnect();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.