本周有同学问为啥Java访问NetSuite Restlet时,按照知识会之前的文章分享,会一直报403 INVALID_LOGIN_ATTEMPT错误。
https://nk-community.blog.csdn.net/article/details/131399801https://nk-community.blog.csdn.net/article/details/131399801原因是之前的文章是基于访问REST Webservice的场景,由于访问Restlet时是有些细微区别的,照搬代码是会报错的。这个区别在于Base String的构建。
通过TBA访问NetSuite的服务时,不同的服务其对Base String的格式要求是有不同的。SOAP Webservice, REST Webservice,Restlet三种各不相同。
区别于REST Webservice的是,Restlet的Base String需要加上deploy和script参数。
下面附上调试通过的Java脚本,请参考。
//访问NetSuite Restlet,请注意Base String中Host的小写格式。
//Rick Mao 2024-1-5
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.net.URLEncoder;
import java.util.*;
public class NetSuiteRestletAccess {
private static final String NETSUITE_ACCOUNT = "XXX"; //对字母大写
private static final String NETSUITE_CONSUMER_KEY = "XXX";
private static final String NETSUITE_CONSUMER_SECRET = "XXX";
private static final String NETSUITE_TOKEN_ID = "XXX";
private static final String NETSUITE_TOKEN_SECRET = "XXX";
// Generate the timestamp and nonce
private static final String timestamp = Long.toString(System.currentTimeMillis() / 1000L);
private static final String nonce = UUID.randomUUID().toString();
public static void main(String[] args) throws Exception {
// Create OkHttpClient with logging interceptor for debugging
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build();
// Create the request URL
HttpUrl requestUrl = HttpUrl.parse("https://" + NETSUITE_ACCOUNT + ".restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=1795&deploy=1");
// Generate the Authorization header value
String authorizationHeader = generateAuthorizationHeader(requestUrl.toString());
// Create the request
Request request = new Request.Builder()
.url(requestUrl)
.header("Authorization", authorizationHeader)
.get()
.build();
// Execute the request
try (Response response = httpClient.newCall(request).execute()) {
// Process the response
String responseBody = response.body().string();
System.out.println(responseBody);
}
}
private static String generateAuthorizationHeader(String url) throws Exception {
String baseString = baseStringConcat();
// Generate the signature
String signature = generateSignature(baseString, NETSUITE_CONSUMER_SECRET, NETSUITE_TOKEN_SECRET);
// Construct the Authorization header value
String AuthString = "OAuth " +
"realm=\"" + NETSUITE_ACCOUNT + "\"," +
"oauth_consumer_key=\"" + NETSUITE_CONSUMER_KEY + "\"," +
"oauth_token=\"" + NETSUITE_TOKEN_ID + "\"," +
"oauth_signature_method=\"HMAC-SHA256\"," +
"oauth_timestamp=\"" + timestamp + "\"," +
"oauth_nonce=\"" + nonce + "\"," +
"oauth_version=\"1.0\"," +
"oauth_signature=\"" + URLEncoder.encode(signature, StandardCharsets.UTF_8) + "\"";
return AuthString;
}
private static String generateSignature(String baseString, String consumerSecret, String tokenSecret) throws NoSuchAlgorithmException, InvalidKeyException {
String EMPTY_STRING = "";
String CARRIAGE_RETURN = "\r\n";
String key = URLEncoder.encode(consumerSecret, StandardCharsets.UTF_8) + "&" + URLEncoder.encode(tokenSecret, StandardCharsets.UTF_8);
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
sha256Hmac.init(secretKey);
byte[] signatureBytes = sha256Hmac.doFinal(baseString.getBytes(StandardCharsets.UTF_8));
String resultSignature = new String(java.util.Base64.getEncoder().encode(signatureBytes));
return resultSignature.replace(CARRIAGE_RETURN, EMPTY_STRING);
}
public static String generateSignatureBaseString(String httpMethod, String url, Map<String, String> parameters) throws Exception {
StringBuilder baseString = new StringBuilder();
// URL-encode the components of the URL
String encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);
// Sort and encode the parameters
Map<String, String> sortedParameters = new HashMap<>(parameters);
sortedParameters.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEachOrdered(entry -> {
try {
String key = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8);
String value = URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8);
baseString.append(key).append("=").append(value).append("&");
} catch (Exception e) {
e.printStackTrace();
}
});
// Remove the trailing '&' character
if (baseString.length() > 0) {
baseString.setLength(baseString.length() - 1);
}
// Construct the signature base string
String signatureBaseString = httpMethod.toUpperCase() + "&" + encodedUrl + "&" + URLEncoder.encode(baseString.toString(), "UTF-8");
return signatureBaseString;
}
private static String baseStringConcat() throws Exception {
String httpMethod = "GET";
//NETSUITE_ACCOUNT 需要转为小写,否则服务端报InvalidSignature错。
String url = "https://"+ NETSUITE_ACCOUNT.toLowerCase() + ".restlets.api.netsuite.com/app/site/hosting/restlet.nl";
Map<String, String> parameters = new HashMap<>();
parameters.put("deploy", "1"); //Restlet访问专用,deploy id需具实际调整。
parameters.put("oauth_consumer_key", NETSUITE_CONSUMER_KEY);
parameters.put("oauth_nonce", nonce);
parameters.put("oauth_signature_method", "HMAC-SHA256");
parameters.put("oauth_timestamp", timestamp);
parameters.put("oauth_token", NETSUITE_TOKEN_ID);
parameters.put("oauth_version", "1.0");
parameters.put("script", "1795");//Restlet访问专用,script id需具实际调整。
String signatureBaseString = generateSignatureBaseString(httpMethod, url, parameters);
System.out.println(signatureBaseString);
return signatureBaseString;
}
}
如果有任何关于NetSuite的问题,欢迎来谈。邮箱:service@truston.group