md5 + 签名认证
package com.alibaba.dt.dataphin;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import java.io.Closeable;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* postman方式调用API示例
* @author: 夜雨
* @date: 2020/12/15
* @version: V1.0.0
*/
@SuppressWarnings({"Duplicates"})
public class PostManDemo {
/**
* 访问域名或ip
*/
private static final String HOST = "127.0.0.1:8080";
/**
* 应用appKey
*/
private static final String APP_KEY = "103787611";
/**
* 应用appSecret
*/
private static final String APP_SECRET = "1234hupacoc07ll7if586g72dvyr8v3o";
/**
* apiId
*/
private static final String API_ID = "1254131194177711110";
/**
* content-md5
* 请求body MD5,body可以参考SDK中请求参数封装类QueryParamRequest,转成json字符串
* String jsonStr = JSONObject.toJSONString(queryParamRequest);
* 也可直接按以下字符串格式自行封装
* conditions为请求参数(查询条件),returnFields为返回参数
*/
private final static String bodyJsonStr = "{\"conditions\":{\"name\":\"应用名称1\"},\"returnFields\":[\"id\",\"name\"],\"useModelCache\":false,\"useResultCache\":false}";
private static final Map<String, String> headers = new HashMap<String, String>();
/**
* //TODO 执行此方法之前,请按照实际情况(可以从oneservice中拿到相关参数值)替换以下参数值
* @see com.alibaba.dt.dataphin.PostManDemo#HOST
* @see com.alibaba.dt.dataphin.PostManDemo#APP_KEY
* @see com.alibaba.dt.dataphin.PostManDemo#APP_SECRET
* @see com.alibaba.dt.dataphin.PostManDemo#API_ID
*
*/
public static void main(String[] args) throws IOException, InvalidKeyException, NoSuchAlgorithmException {
//生成headers
createHears();
//body
System.out.println("请求body:"+bodyJsonStr);
//初始化httpclient
init();
//获取返回结果
String result = sendSyncRequest(headers, bodyJsonStr.getBytes("UTF-8"));
System.out.println("返回结果:");
System.out.println(result);
}
private static void createHears() throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException {
//date,设置请求头中的时间戳
Date current = new Date();
headers.put("date", getHttpDateHeaderValue(current));
System.out.println("生成header[date]:"+getHttpDateHeaderValue(current));
//x-ca-timestamp,设置请求头中的时间戳,以timeIntervalSince1970的形式
headers.put("x-ca-timestamp", String.valueOf(current.getTime()));
System.out.println("生成header[x-ca-timestamp]:"+String.valueOf(current.getTime()));
//x-ca-nonce,请求放重放Nonce,15分钟内保持唯一,建议使用UUID
// String uuid = UUID.randomUUID().toString();
// headers.put("x-ca-nonce", uuid);
// System.out.println("生成header[x-ca-nonce]:"+ uuid);
// //user-agent,设置请求头中的UserAgent
// System.out.println("生成header[user-agent]:ALIYUN-ANDROID-DEMO");
//host,设置请求头中的主机地址
headers.put("host", HOST);
System.out.println("生成header[host]:"+ HOST);
//x-ca-key,设置请求头中的Api绑定的的AppKey
headers.put("x-ca-key", APP_KEY);
System.out.println("生成header[x-ca-key]:"+ APP_KEY);
// //ca_version,设置签名版本号
// System.out.println("生成header[ca_version]:"+ 1);
//content-type,设置请求数据类型
headers.put("content-type", "application/json; charset=utf-8");
System.out.println("生成header[content-type]:application/json; charset=utf-8");
//accept,设置应答数据类型
headers.put("accept", "application/json; charset=utf-8");
System.out.println("生成header[accept]:application/json; charset=utf-8");
//x-ca-signature-method,签名加密方式
headers.put("x-ca-signature-method", "HmacSHA256");
System.out.println("生成header[x-ca-signature-method]:HmacSHA256");
String md5 = base64AndMD5(bodyJsonStr.getBytes("UTF-8"));
headers.put("content-md5", md5);
System.out.println("生成header[content-md5]:"+md5);
//x-ca-signature,签名用作服务器校验
String stringToSign = buildStringToSign(headers);
String signature = sign(stringToSign);
headers.put("x-ca-signature",signature);
System.out.println("生成header[x-ca-signature]:"+signature);
//x-ca-stage,生产环境标识
headers.put("x-ca-stage","RELEASE");
System.out.println("生成header[x-ca-stage]:RELEASE");
//x-ca-signature-headers,同时所有加入签名的头的列表,需要用逗号分隔形成一个字符串,加入一个新HTTP头@"X-Ca-Signature-Headers"
System.out.println("生成header[x-ca-signature-headers]:"+headers.get("x-ca-signature-headers"));
}
private static String getHttpDateHeaderValue(Date date) {
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
return dateFormat.format(date);
}
public static String base64AndMD5(byte[] bytes) {
if (bytes == null) {
throw new IllegalArgumentException("bytes can not be null");
}
try {
final MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
md.update(bytes);
byte[] md5Result = md.digest();
String base64Result = Base64.encodeBase64String(md5Result);
/*
* 正常情况下,base64的结果为24位,因与服务器有约定,在超过24位的情况下,截取前24位
*/
return base64Result.length() > 24 ? base64Result.substring(0, 23) : base64Result;
} catch (final NoSuchAlgorithmException e) {
throw new IllegalArgumentException("unknown algorithm MD5");
}
}
//换行符
private static final String CLOUDAPI_LF = "\n";
private static final String HTTP_METHOD = "POST";
/**
* path为oneservice创建的API指定的请求方式(get或者list)+API_ID生成
*/
private static final String PATH = "/get/"+API_ID;
private static final String ENV = "PROD";
private static final String URL = PATH+"?appKey="+APP_KEY+"&env="+ENV;
/**
* 构建需要参与签名的字符串
*/
public static String buildStringToSign(Map<String, String> headers) {
StringBuilder sb = new StringBuilder();
sb.append(HTTP_METHOD).append(CLOUDAPI_LF);
//如果有@"Accept"头,这个头需要参与签名
if (headers.get("accept") != null) {
sb.append(headers.get("accept"));
}
sb.append(CLOUDAPI_LF);
//如果有@"Content-MD5"头,这个头需要参与签名
if (headers.get("content-md5") != null) {
sb.append(headers.get("content-md5"));
}
sb.append(CLOUDAPI_LF);
//如果有@"Content-Type"头,这个头需要参与签名
if (headers.get("content-type") != null) {
sb.append(headers.get("content-type"));
}
sb.append(CLOUDAPI_LF);
//签名优先读取HTTP_CA_HEADER_DATE,因为通过浏览器过来的请求不允许自定义Date(会被浏览器认为是篡改攻击)
if (headers.get("date") != null) {
sb.append(headers.get("date"));
}
sb.append(CLOUDAPI_LF);
//将headers合成一个字符串
sb.append(buildHeaders(headers));
//url
sb.append(URL);
return sb.toString();
}
/**
* 将headers合成一个字符串
* 需要注意的是,HTTP头需要按照字母排序加入签名字符串
* 同时所有加入签名的头的列表,需要用逗号分隔形成一个字符串,加入一个新HTTP头@"X-Ca-Signature-Headers"
*/
private static String buildHeaders(Map<String, String> headers) {
//使用TreeMap,默认按照字母排序
Map<String, String> headersToSign = new TreeMap<String, String>();
StringBuilder signHeadersStringBuilder = new StringBuilder();
int flag = 0;
for (Map.Entry<String, String> header : headers.entrySet()) {
if (header.getKey().startsWith("x-ca-")) {
if (flag != 0) {
signHeadersStringBuilder.append(",");
}
flag++;
signHeadersStringBuilder.append(header.getKey());
headersToSign.put(header.getKey(), header.getValue());
}
}
//同时所有加入签名的头的列表,需要用逗号分隔形成一个字符串,加入一个新HTTP头@"X-Ca-Signature-Headers"
headers.put("x-ca-signature-headers",signHeadersStringBuilder.toString());
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> e : headersToSign.entrySet()) {
sb.append(e.getKey()).append(':').append(e.getValue()).append(CLOUDAPI_LF);
}
return sb.toString();
}
/**
* 生成签名串
* @param stringToSign
* @return
*/
public static String sign(String stringToSign) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
byte[] keyBytes = APP_SECRET.getBytes("UTF-8");
hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, "HmacSHA256"));
String signature = new String(Base64.encodeBase64(hmacSha256.doFinal(stringToSign.getBytes("UTF-8"))));
return signature;
}
public static String sendSyncRequest(Map<String, String> headers,byte[] body) {
HttpUriRequest httpRequest = buildRequest(headers,body);
CloseableHttpResponse httpResponse = null;
try {
httpResponse = httpClient.execute(httpRequest);
return parseToApiResponse(httpResponse);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
closeQuietly(httpResponse);
}
}
public static void closeQuietly(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (IOException var2) {
}
}
private static HttpUriRequest buildRequest(Map<String, String> headers,byte[] body) {
RequestBuilder builder = RequestBuilder.create(HTTP_METHOD);
/*
* 拼接URL
* HTTP + HOST + PATH(With pathparameter) + Query Parameter
*/
try {
URIBuilder uriBuilder = new URIBuilder();
uriBuilder.setScheme("HTTP");
uriBuilder.setHost(HOST);
uriBuilder.setPath(PATH);
//增加url上参数,/get/1254131194177714180?appKey=203787616&env=PROD
uriBuilder.addParameter("appKey",APP_KEY);
uriBuilder.addParameter("env",ENV);
builder.setUri(uriBuilder.build());
} catch (URISyntaxException e) {
throw new RuntimeException("build http request uri failed", e);
}
EntityBuilder bodyBuilder = EntityBuilder.create();
bodyBuilder.setContentType(ContentType.parse("application/octet-stream; charset=utf-8"));
bodyBuilder.setBinary(body);
builder.setEntity(bodyBuilder.build());
for (Map.Entry<String, String> entry : headers.entrySet()) {
builder.addHeader(entry.getKey(), entry.getValue());
}
return builder.build();
}
private static CloseableHttpClient httpClient;
private static PoolingHttpClientConnectionManager connectionManager;
public static void init() {
SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).setSoKeepAlive(true).setSoReuseAddress(true)
.setSoTimeout(10000).build();
Registry<ConnectionSocketFactory> registry = getRegistry();
connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setDefaultConnectionConfig(ConnectionConfig.custom().build());
connectionManager.setDefaultSocketConfig(socketConfig);
connectionManager.setMaxTotal(64);
connectionManager.setDefaultMaxPerRoute(5);
RequestConfig defaultConfig = RequestConfig.custom()
.setConnectTimeout(10000)
.setSocketTimeout(10000)
.setConnectionRequestTimeout(10000)
.build();
httpClient = HttpClients.custom().setConnectionManager(connectionManager).setDefaultRequestConfig(defaultConfig).build();
}
private static Registry<ConnectionSocketFactory> getRegistry() {
RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();
try {
registryBuilder.register("http", PlainConnectionSocketFactory.INSTANCE).build();
registryBuilder.register("https",new SSLConnectionSocketFactory(SSLContext.getDefault(), new DefaultHostnameVerifier()));
} catch (Exception e) {
throw new RuntimeException("HttpClientUtil init failure !", e);
}
return registryBuilder.build();
}
private static String parseToApiResponse(HttpResponse httpResponse) throws IOException {
System.out.println("code:"+httpResponse.getStatusLine().getStatusCode());
// message
System.out.println("message:"+httpResponse.getStatusLine().getReasonPhrase());
if(httpResponse.getEntity() != null){
// content type
Header contentType = httpResponse.getEntity().getContentType();
if(contentType != null){
System.out.println("ContentType:"+contentType.getValue());
}
else
{
System.out.println("ContentType:application/text; charset=utf-8");
}
System.out.println(httpResponse.getEntity());
// body
return new String(EntityUtils.toByteArray(httpResponse.getEntity()));
}
return null;
}
}