cn.com.antcloud.api.common.BaseGwClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of antcloud-api-sdk Show documentation
Show all versions of antcloud-api-sdk Show documentation
Ant Fin Tech API SDK For Java
Copyright (c) 2015-present Alipay.com, https://www.alipay.com
The newest version!
/*
* Copyright (c) 2015-present Alipay.com, https://www.alipay.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.com.antcloud.api.common;
import cn.com.antcloud.api.acapi.AntCloudHttpClient;
import cn.com.antcloud.api.acapi.Constants;
import cn.com.antcloud.api.acapi.HttpConfig;
import cn.com.antcloud.api.acapi.StringUtils;
import cn.com.antcloud.api.security.util.globalgw.GlobalGwRsaAesUtil;
import cn.com.antcloud.api.security.util.globalgw.GlobalGwSm2Sm4Util;
import cn.com.antcloud.api.security.util.model.GlobalCiphertext;
import cn.com.antcloud.api.security.util.model.GlobalGwConstants;
import cn.com.antcloud.api.security.util.model.GlobalPlaintext;
import cn.com.antcloud.api.security.util.signencrypt.AesUtil;
import cn.com.antcloud.api.security.util.signencrypt.BcSm4Util;
import cn.com.antcloud.api.security.util.signencrypt.Jdk8Base64;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.*;
import static cn.com.antcloud.api.common.SDKConstants.GlobalHeaderKeys.*;
import static cn.com.antcloud.api.common.SDKConstants.*;
import static cn.com.antcloud.api.common.SDKConstants.ParamKeys.*;
import static cn.com.antcloud.api.common.SDKConstants.ResultCodes.PARSE_URL_ERROR;
import static cn.com.antcloud.api.security.util.model.Constants.AES_PADDING;
import static cn.com.antcloud.api.security.util.model.GlobalGwConstants.*;
public abstract class BaseGwClient {
private static final Logger logger = LoggerFactory.getLogger(BaseGwClient.class);
private static final String[] removeGlobalReqeustKeys = new String[]{REQ_MSG_ID, METHOD, VERSION, REQ_BIZ_ID,AUTH_TOKEN, BASE_SDK_VERSION,
PROD_CODE, CHANNEL, SDKConstants.ParamKeys.SIGN_TYPE, ENCRYPT, SIGN_KEY_VERSION, ENCRYPTION_VERSION, PRODUCT_INSTANCE_ID,
REGION_NAME, ParamKeys.SDK_VERSION, ParamKeys._PROD_CODE};
private final String endpoint;
private final String accessKey;
private final String accessSecret;
private final boolean checkSign;
private final boolean enableAutoRetry;
private final int autoRetryLimit;
private final String clientId;
protected AntCloudHttpClient httpClient;
protected String securityToken;
public BaseGwClient(String endpoint, String accessKey, String accessSecret, boolean checkSign,
boolean enableAutoRetry, int autoRetryLimit,
AntCloudHttpClient httpClient, String securityToken, String clientId) {
SDKUtils.checkNotNull(endpoint);
SDKUtils.checkNotNull(accessKey);
SDKUtils.checkNotNull(accessSecret);
this.endpoint = endpoint;
this.accessKey = accessKey;
this.accessSecret = accessSecret;
this.checkSign = checkSign;
this.enableAutoRetry = enableAutoRetry;
this.autoRetryLimit = autoRetryLimit;
this.httpClient = httpClient;
this.securityToken = securityToken;
this.clientId = clientId;
}
public String getEndpoint() {
return endpoint;
}
public String getAccessKey() {
return accessKey;
}
public String getAccessSecret() {
return accessSecret;
}
public boolean isCheckSign() {
return checkSign;
}
/**
* build HmacSHA1 or HmacSHA256 reqeust
* @param endpoint
* @param request
* @param headersParameters
* @return
* @throws UnsupportedEncodingException
*/
protected HttpUriRequest buildHmacSHARequest(String endpoint,
Map request,
Map headersParameters) throws UnsupportedEncodingException {
URI uri;
try {
URIBuilder builder = new URIBuilder(endpoint);
uri = builder.build();
} catch (URISyntaxException e) {
throw new ClientException(PARSE_URL_ERROR, e);
}
List nameValuePairs = new ArrayList();
for (String key : request.keySet()) {
//去掉req_msg_id,否则网关侧会报参数错误
if(StringUtils.equals(key, REQ_MSG_ID)){
continue;
}
nameValuePairs.add(new BasicNameValuePair(key, request.get(key)));
}
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(nameValuePairs,
SDKConstants.DEFAULT_CHARSET);
HttpPost httpPost = new HttpPost(uri);
prepareCustomizedHeaders(httpPost, headersParameters);
httpPost.setEntity(urlEncodedFormEntity);
return httpPost;
}
/**
* Build rsa or sm2 format reqeust
* @param endpoint
* @param globalPlaintext
* @param request
* @return
* @throws Exception
*/
protected HttpUriRequest buildRsaSm2Request(String endpoint,
GlobalPlaintext globalPlaintext,
REQ request) throws Exception {
URI uri;
try {
URIBuilder builder = new URIBuilder(endpoint);
uri = builder.build();
} catch (URISyntaxException e) {
throw new ClientException(PARSE_URL_ERROR, e);
}
HttpPost httpPost = new HttpPost(uri); //把原本数科网关请求体内的数据移到header中
httpPost.setHeader(GLOBAL_HEADER_PRODUCT_INSTANCE_ID, request.getParameter(PRODUCT_INSTANCE_ID));
httpPost.setHeader(GLOBAL_HEADER_REQ_MSG_ID, request.getParameter(REQ_MSG_ID));
httpPost.setHeader(GLOBAL_HEADER_METHOD, request.getParameter(METHOD));
httpPost.setHeader(GLOBAL_HEADER_API_VERSION, request.getParameter(VERSION));
httpPost.setHeader(GLOBAL_HEADER_REQ_BIZ_ID, request.getParameter(REQ_BIZ_ID));
httpPost.setHeader(GLOBAL_HEADER_AUTH_TOKEN, request.getParameter(AUTH_TOKEN));
httpPost.setHeader(GLOBAL_HEADER_REQ_MSG_ID, request.getParameter(REQ_MSG_ID));
httpPost.setHeader(GLOBAL_HEADER_PROD_CODE, request.getParameter(PROD_CODE));
httpPost.setHeader(GLOBAL_HEADER_CHANNEL, request.getParameter(CHANNEL));
httpPost.setHeader(GLOBAL_HEADER_BASE_SDK_VERSION, request.getParameter(BASE_SDK_VERSION));
httpPost.setHeader(GLOBAL_HEADER_SDK_VERSION, request.getParameter(ParamKeys.SDK_VERSION));
httpPost.setHeader(GLOBAL_HEAGLOBAL_HEADER__PROD_CODE, request.getParameter(ParamKeys._PROD_CODE));
for(String key : removeGlobalReqeustKeys) {
request.removeParameter(key);
}
//国际化网关的格式为json,因此这里要把打平的map重新转换为json字符串格式
globalPlaintext.setContent(GwKeyValues.toJSONString(request.getParameters(), String.class));
GlobalCiphertext globalCiphertext = GlobalGwRsaAesUtil.genRequest(globalPlaintext);
prepareCustomizedHeaders(httpPost, request.getHeadersParameters());
httpPost.setHeader(GlobalGwConstants.CONTENT_TYPE, globalCiphertext.getHeaders().get(GlobalGwConstants.CONTENT_TYPE));
httpPost.setHeader(HEADER_SIGNATURE, globalCiphertext.getHeaders().get(HEADER_SIGNATURE));
httpPost.setHeader(HEADER_ENCRYPT, globalCiphertext.getHeaders().get(HEADER_ENCRYPT));
httpPost.setHeader(CLIENT_ID, globalCiphertext.getHeaders().get(CLIENT_ID));
httpPost.setHeader(REQUEST_TIME, globalCiphertext.getHeaders().get(REQUEST_TIME));
StringEntity stringEntity = new StringEntity(globalCiphertext.getEncryptContext(), Constants.CHARSET_UTF8);
httpPost.setEntity(stringEntity);
return httpPost;
}
/**
* 判断是否有压测标识,如果有压测标识就添加到头文件
* @param httpPost
* @param headersParameters
*/
private void prepareCustomizedHeaders(HttpPost httpPost, Map headersParameters) {
if(headersParameters.containsKey(GLOBAL_HEADER_LOAD_TEST_MARK) && headersParameters.containsKey(GLOBAL_HEADER_LOAD_TEST_UID)) {
httpPost.setHeader(GLOBAL_HEADER_LOAD_TEST_MARK, headersParameters.get(GLOBAL_HEADER_LOAD_TEST_MARK));
httpPost.setHeader(GLOBAL_HEADER_LOAD_TEST_UID, headersParameters.get(GLOBAL_HEADER_LOAD_TEST_UID));
}
if(headersParameters.containsKey(GLOBAL_HEADER_GW_EXT_FLAG)) {
httpPost.setHeader(GLOBAL_HEADER_GW_EXT_FLAG, headersParameters.get(GLOBAL_HEADER_GW_EXT_FLAG));
}
}
public RES execute(REQ request) throws Exception {
SDKUtils.checkNotNull(request);
SDKUtils.checkNotNull(request.getMethod(), "method cannot be null");
SDKUtils.checkNotNull(request.getVersion(), "version cannot be null");
if(request.getReqMsgId() == null) {
request.setReqMsgId(SDKUtils.generateReqMsgId());
}
return sendRequest(request);
}
private RES sendRequest(REQ request) throws Exception {
int retried = 0;
String signType = StringUtils.isEmpty(request.getSignType()) ? SDKConstants.DEFAULT_SIGN_TYPE : request.getSignType();
for (;;) {
String reqMsgId = request.getReqMsgId();
try {
String endpointReqMsgId = endpoint;
String productUri = getUri(endpointReqMsgId);
//添加req_msg_id到uri,方便在slb日志上查询
if(!StringUtils.isEmpty(reqMsgId)) {
String ch = endpointReqMsgId.contains("?")?"&":"?";
endpointReqMsgId = endpointReqMsgId + ch + REQ_MSG_ID + "=" + URLEncoder.encode(request.getReqMsgId(), "UTF-8");
}
HttpUriRequest httpUriRequest = prepareRequest(endpointReqMsgId, request, productUri, signType);
HttpResponse httpResponse = this.httpClient.invoke(httpUriRequest);
RES response;
if(StringUtils.equals(signType, GwSignType.RSA_AES.getCode()) ||
StringUtils.equals(signType, GwSignType.SM2_SM4.getCode())) {
response = handleRsaSm2Response(httpResponse, signType, productUri);
} else {
response = handleHmacSHAResponse(httpResponse, signType);
}
return response;
} catch (Exception e) {
try {
if (enableAutoRetry &&
retried < autoRetryLimit &&
e instanceof ClientException &&
StringUtils.equals(((ClientException) e).getResultCode(), SDKConstants.ResultCodes.RESPONSE_FORMAT_ERROR)) {
// 具备重试条件
logger.error("retry send request, retried count = {}", retried);
retried++;
continue;
} else {
throw new RuntimeException(REQ_MSG_ID + ":" + reqMsgId + ", originalErrorMessage:" + e.getMessage(), e);
}
} catch (Exception ex) {
throw new ClientException(SDKConstants.ResultCodes.TRANSPORT_ERROR, ex);
}
}
}
}
protected RES newResponse() {
Class clazz = GenericTypeResolver.resolveTypeArguments(getClass(),
BaseGwClient.class)[1];
try {
return clazz.newInstance();
} catch (InstantiationException e) {
throw new IllegalStateException("Cannot create response");
} catch (IllegalAccessException e) {
throw new IllegalStateException("Cannot create response");
}
}
private void putParameterIfAbsent(REQ request, String key, String value) {
if (request.getParameter(key) == null) {
request.putParameter(key, value);
}
}
private HttpUriRequest prepareRequest(String endpointReqMsgId, REQ request, String productUri, String signType) throws Exception {
GlobalPlaintext globalPlaintext = new GlobalPlaintext();
globalPlaintext.setSignKeyVersion(request.getSignKeyVersion());
globalPlaintext.setEncryptKeyVersion(request.getEncryptionVersion());
// STS token
if (!StringUtils.isEmpty(securityToken)) {
putParameterIfAbsent(request, SDKConstants.ParamKeys.SECURITY_TOKEN, securityToken);
}
// 基础包版本信息
putParameterIfAbsent(request, SDKConstants.ParamKeys.BASE_SDK_VERSION,
SDKConstants.BASE_SDK_VERSION_VALUE);
globalPlaintext.setEncrypt(StringUtils.equals(request.getEncrypt(), TRUE));
globalPlaintext.setEncryptPubKey(accessKey);
globalPlaintext.setSignPriKey(accessSecret);
globalPlaintext.setClientId(clientId);
globalPlaintext.setRequestTime(new Date());
globalPlaintext.setUri(productUri);
try {
if(StringUtils.equals(signType, GwSignType.RSA_AES.getCode())) {
if(StringUtils.equals(request.getEncrypt(), TRUE)) {
String key = AesUtil.aes_key(AES_PADDING, 256);
globalPlaintext.setSecurityKey(key);
}
return buildRsaSm2Request(endpointReqMsgId, globalPlaintext, request);
} else if(StringUtils.equals(signType, GwSignType.SM2_SM4.getCode())) {
if(StringUtils.equals(request.getEncrypt(), TRUE)) {
String key = Jdk8Base64.base64urlsafe_encode(BcSm4Util.generateKey(256));
globalPlaintext.setSecurityKey(key);
}
return buildRsaSm2Request(endpointReqMsgId, globalPlaintext, request);
} else {
putParameterIfAbsent(request, SDKConstants.ParamKeys.REQ_TIME,
SDKUtils.formatDate(new Date()));
putParameterIfAbsent(request, SDKConstants.ParamKeys.SIGN_TYPE,
SDKConstants.DEFAULT_SIGN_TYPE);
request.putParameter(SDKConstants.ParamKeys.ACCESS_KEY, accessKey);
try {
String sign = GwSigns.sign(request.getParameters(),
request.getParameter(SDKConstants.ParamKeys.SIGN_TYPE), accessSecret,
SDKConstants.SIGN_CHARSET);
request.putParameter(SDKConstants.ParamKeys.SIGN, sign);
} catch (Exception e) {
throw new ClientException(SDKConstants.ResultCodes.UNKNOWN_ERROR, e);
}
return buildHmacSHARequest(endpointReqMsgId, request.getParameters(), request.getHeadersParameters());
}
} catch (Exception ex) {
throw new ClientException(REQ_MSG_ID + ":" + request.getReqMsgId()+","+SDKConstants.ResultCodes.TRANSPORT_ERROR, ex);
}
}
private RES handleHmacSHAResponse(HttpResponse httpResponse, String signType) throws ClientException, IOException {
String responseString = EntityUtils.toString(httpResponse.getEntity(),
Charset.forName("UTF-8"));
// 如果数据解析失败,则开启重试逻辑
JSONObject wholeJson = new JSONObject();
try {
wholeJson = JSON.parseObject(responseString);
} catch (Throwable e) {
logger.error("error when parse response as json, response = {}",
responseString);
throw new ClientException(SDKConstants.ResultCodes.RESPONSE_FORMAT_ERROR, e);
}
if (wholeJson == null) {
logger.error(responseString);
throw new ClientException(SDKConstants.ResultCodes.TRANSPORT_ERROR,
"Unexpected gateway response: " + responseString);
}
JSONObject responseNode = wholeJson.getJSONObject("response");
if (responseNode == null) {
logger.error(responseString);
throw new ClientException(SDKConstants.ResultCodes.TRANSPORT_ERROR,
"Unexpected gateway response: " + responseString);
}
RES response = newResponse();
response.setData(responseNode);
if (response.isSuccess() && checkSign) {
String sign = wholeJson.getString(SDKConstants.ParamKeys.SIGN);
String stringToSign = GwSigns.extractStringToSign(responseString);
String calculatedSign;
try {
calculatedSign = GwSigns.sign(stringToSign, signType, accessSecret, SDKConstants.SIGN_CHARSET);
} catch (Exception e) {
throw new ClientException(SDKConstants.ResultCodes.BAD_SIGNATURE,
"Invalid signature in response");
}
if (!calculatedSign.equals(sign)) {
throw new ClientException(SDKConstants.ResultCodes.BAD_SIGNATURE,
"Invalid signature in response");
}
}
return response;
}
private RES handleRsaSm2Response(HttpResponse httpResponse, String signType, String uri) throws Exception {
GlobalCiphertext requestCiphertext = new GlobalCiphertext();
requestCiphertext.setEncryptContext(EntityUtils.toString(httpResponse.getEntity(), Charset.forName("UTF-8")));
Map headers = new HashMap();
for(Header header : httpResponse.getAllHeaders()) {
headers.put(header.getName(), header.getValue());
}
requestCiphertext.setHeaders(headers);
requestCiphertext.setVerifyPubKey(accessKey);
requestCiphertext.setDecryptPriKey(accessSecret);
requestCiphertext.setUri(uri);
requestCiphertext.setClientId(clientId);
GlobalPlaintext globalPlaintext = new GlobalPlaintext();
if(StringUtils.equals(signType, GwSignType.SM2_SM4.getCode())) {
globalPlaintext = GlobalGwSm2Sm4Util.handleReceiveText(requestCiphertext);
} else if(StringUtils.equals(signType, GwSignType.RSA_AES.getCode())) {
globalPlaintext = GlobalGwRsaAesUtil.handleReceiveText(requestCiphertext);
}
JSONObject wholeJson;
try {
wholeJson = JSON.parseObject(globalPlaintext.getContent());
} catch (Throwable e) {
logger.error("error when parse response as json, response = {}", globalPlaintext.getContent());
throw new ClientException(SDKConstants.ResultCodes.RESPONSE_FORMAT_ERROR, e);
}
JSONObject responseNode = wholeJson.getJSONObject("result");
if (responseNode == null) {
logger.error(wholeJson.toJSONString());
throw new ClientException(SDKConstants.ResultCodes.TRANSPORT_ERROR,
"Unexpected gateway response: " + wholeJson.toJSONString());
}
/**
* 大安全国际站返回格式为{"result":{"resultCode":"SUCCESS","resultMessage":"success","resultStatus":"S"},"业务参数":"业务参数"}
* 此处修改返回格式适配大安全
*/
wholeJson.put(RESULT_CODE, responseNode.get(SECURITY_RESULT_CODE));
wholeJson.put(RESULT_MSG, responseNode.get(SECURITY_RESULT_MSG));
wholeJson.put(SECURITY_RESULT_STATUS, responseNode.get(SECURITY_RESULT_STATUS));
if(headers.containsKey(REQ_MSG_ID)) {
wholeJson.put(REQ_MSG_ID, headers.get(REQ_MSG_ID));
}
wholeJson.remove(SECURITY_RESPONSE);
RES response = newResponse();
response.setData(wholeJson);
return response;
}
/**
* 获取uri,endpoint格式为http://host/uri
* @param endpoint
* @return
*/
private String getUri(String endpoint) {
if (StringUtils.isEmpty(endpoint)) {
throw new ClientException(PARSE_URL_ERROR, "Endpoint is empty");
}
String[] splits = endpoint.split(URI_DELIMITER);
return endpoint.substring(splits[0].length() + splits[2].length() + 2);
}
protected static class Builder> {
private String endpoint;
private String accessKey;
private String accessSecret;
private boolean checkSign = true;
private boolean enableAutoRetry = false;
private int autoRetryLimit = 3;
private AntCloudHttpClient httpClient;
private String securityToken;
private String clientId;
public T build() {
@SuppressWarnings("unchecked")
Class clazz = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
if (httpClient == null){
HttpConfig httpConfig = new HttpConfig();
httpClient = new AntCloudHttpClient(httpConfig);
}
try {
Constructor ctor = clazz.getDeclaredConstructor(String.class, String.class,
String.class, boolean.class, boolean.class, int.class,
AntCloudHttpClient.class, String.class, String.class);
ctor.setAccessible(true);
return ctor.newInstance(endpoint, accessKey, accessSecret, checkSign,
enableAutoRetry, autoRetryLimit, httpClient, securityToken,clientId);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
public B setHttpClient(AntCloudHttpClient httpClient) {
this.httpClient = httpClient;
return (B) this;
}
public B setEndpoint(String endpoint) {
this.endpoint = endpoint;
return (B) this;
}
public B setAccess(String accessKey, String accessSecret) {
this.accessKey = accessKey.trim();
this.accessSecret = accessSecret.trim();
return (B) this;
}
public B setCheckSign(boolean checkSign) {
this.checkSign = checkSign;
return (B) this;
}
public B setEnableAutoRetry(boolean enableAutoRetry) {
this.enableAutoRetry = enableAutoRetry;
return (B) this;
}
public B setAutoRetryLimit(int autoRetryLimit) {
this.autoRetryLimit = autoRetryLimit;
return (B) this;
}
public B setSecurityToken(String securityToken) {
this.securityToken = securityToken;
return (B) this;
}
public B setClientId(String clientId) {
this.clientId = clientId;
return (B) this;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy