本周有同学问为啥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-5import 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 nonceprivate 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 debuggingHttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(loggingInterceptor).build();// Create the request URLHttpUrl requestUrl = HttpUrl.parse("https://" + NETSUITE_ACCOUNT + ".restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=1795&deploy=1");// Generate the Authorization header valueString authorizationHeader = generateAuthorizationHeader(requestUrl.toString());// Create the requestRequest request = new Request.Builder().url(requestUrl).header("Authorization", authorizationHeader).get().build();// Execute the requesttry (Response response = httpClient.newCall(request).execute()) {// Process the responseString responseBody = response.body().string();System.out.println(responseBody);}}private static String generateAuthorizationHeader(String url) throws Exception {String baseString = baseStringConcat();// Generate the signatureString signature = generateSignature(baseString, NETSUITE_CONSUMER_SECRET, NETSUITE_TOKEN_SECRET);// Construct the Authorization header valueString 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 URLString encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);// Sort and encode the parametersMap<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 '&' characterif (baseString.length() > 0) {baseString.setLength(baseString.length() - 1);}// Construct the signature base stringString 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