数据服务api加密方式

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;

    }
}

posted @ 2023-02-23 20:00  积极向上的徐先生  阅读(84)  评论(0编辑  收藏  举报