阅读 423

JAVA调用腾讯云API-实现语音合成(TTS)(三)

JAVA调用腾讯云API-实现语音合成(TTS)(三)

package com.example.combat.controller;import com.example.combat.service.TTSService;import com.example.combat.ttsutis.param.TextToVoice;import io.swagger.annotations.ApiImplicitParam;import io.swagger.annotations.ApiImplicitParams;import io.swagger.annotations.ApiOperation;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;import javax.validation.Valid;/**
 * @description: 语音合成
 * @author:zhucj
 * @date: 2019-11-27 16:37 */@RestController
@RequestMapping("/tts")public class TTSControllerl {

    @Autowired    private TTSService ttsService;

    @ApiOperation(value = "语音合成")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "text",value = "合成语音的源文本",required = true,dataType = "String"),
            @ApiImplicitParam(name = "volume",value = "音量大小,范围:[0,10]",required = true,dataType = "String"),
            @ApiImplicitParam(name = "speed",value = "语速,范围:[-2,2]",required = true,dataType = "String"),
            @ApiImplicitParam(name = "voiceType",value = "音色",required = true,dataType = "Integer")
    })
    @PostMapping(value = "textToVoice")    public void textToVoice(@Valid @RequestBody TextToVoice textToVoice, HttpServletResponse response){
        ttsService.voiceSynthesis(textToVoice,response);
    }
}

复制代码

复制代码

package com.example.combat.service;import com.example.combat.ttsutis.R;import com.example.combat.ttsutis.param.TextToVoice;import javax.servlet.http.HttpServletResponse;/**
 * @description: 语音合成实现类
 * @author: zhucj
 * @date: 2019-11-27 16:12 */public interface TTSService {    /**
     * 语音合成
     * @param textToVoice     */
    void voiceSynthesis(TextToVoice textToVoice, HttpServletResponse response);
}

复制代码

复制代码

package com.example.combat.service.Impl;import cn.hutool.core.io.FileUtil;import com.example.combat.afsutils.Base64ConvertUtils;import com.example.combat.gaodemapUtils.SystemConstant;import com.example.combat.service.TTSService;import com.example.combat.ttsutis.R;import com.example.combat.ttsutis.TTSUtil;import com.example.combat.ttsutis.param.TextToVoice;import com.example.combat.ttsutis.param.TextToVoiceResponse;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import sun.misc.BASE64Decoder;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.*;import java.util.Objects;/**
 * @description:
 * @author: zhucj
 * @date: 2019-11-27 16:15 */@Service
@Slf4jpublic class TTSServiceImpl implements TTSService {

    @Autowired    private TTSUtil ttsUtil;

    @Value("${tencent.pathImg}")    private String pathImg;

    @Override    public void voiceSynthesis(TextToVoice textToVoice, HttpServletResponse response) {
        R r = ttsUtil.voiceSynthesis(textToVoice);        if (r.getSuccess() && Objects.equals(r.getCode(), SystemConstant.SUCCESS_CODE)){
            TextToVoiceResponse data =(TextToVoiceResponse) r.getData();
            String audio = data.getAudio();            //将base64编码的wav/mp3音频数据 下载给前端            exportImg(response,pathImg,audio);
        }else {            throw new RuntimeException("语音合成失败");
        }
    }    public static void exportImg(HttpServletResponse response, String filePath,String imgStr) {        //对字节数组字符串进行Base64解码并生成图片
        String imgFilePath = null;        //图像数据为空
        if (imgStr == null){            return ;
        }
        BASE64Decoder decoder = new BASE64Decoder();
        BufferedOutputStream buff = null;
        ServletOutputStream outStr = null;        try {            //Base64解码
            byte[] b = decoder.decodeBuffer(imgStr);            for (int i = 0; i < b.length; ++i) {                if (b[i] < 0) {                    //调整异常数据
                    b[i] += 256;
                }
            }
            response.setCharacterEncoding("utf-8");            //设置响应的内容类型
            response.setContentType("text/plain");            //设置文件的名称和格式
            response.setHeader("content-type", "application/octet-stream");
            response.setContentType("application/octet-stream");
            response.addHeader("Content-Disposition", "attachment;filename=" + "合成语音.wav");
            outStr = response.getOutputStream();
            buff = new BufferedOutputStream(outStr);
            buff.write(b);
            buff.flush();
            buff.close();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件导出异常:", e);
        } finally {            try {
                buff.close();
            } catch (Exception e) {
                log.error("流关闭异常:", e);
            }            try {
                outStr.close();
            } catch (Exception e) {
                log.error("流关闭异常:", e);
            }
        }
    }
}

复制代码

复制代码

package com.example.combat.ttsutis;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.example.combat.afsutils.HttpUtil;import com.example.combat.afsutils.SignUtils;import com.example.combat.asrutils.param.SentenceRecognitionApi;import com.example.combat.asrutils.param.SentenceResponse;import com.example.combat.asrutils.param.SystemConstants;import com.example.combat.config.constant.ContentTypeEnum;import com.example.combat.config.constant.HttpMethodEnum;import com.example.combat.config.constant.SignMenodEnum;import com.example.combat.ttsutis.param.TextToVoice;import com.example.combat.ttsutis.param.TextToVoiceResponse;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.util.*;/**
 * @description: 语音合成
 * @author: zhucj
 * @date: 2019-11-27 15:35 */@Component
@Slf4jpublic class TTSUtil {

    @Value("${tencent.secretId}")    private String sercretId;

    @Value("${tencent.secretKey}")    private String sercretKey;    /**
     * 语音合成
     * @param textToVoice
     * @return
     */
   public R voiceSynthesis(TextToVoice textToVoice){

       TreeMap treeMap = createPublicMap("TextToVoice", "2019-08-23", "ap-shenzhen-fsi");
       HashMap<String,Object> hashMap = new HashMap<>();       try {
           hashMap.put("Text", URLEncoder.encode(textToVoice.getText(),"UTF-8"));
       } catch (UnsupportedEncodingException e) {
          log.error("URL Encoder异常:{}",e.getMessage());          return R.error("语音合成失败").setCode(SystemConstants.SERVER_ERROR_CODE);
       }
       hashMap.put("SessionId", UUID.randomUUID().toString());
       hashMap.put("ModelType",1);
       hashMap.put("Volume",Float.valueOf(textToVoice.getVolume()));
       hashMap.put("Speed",Float.valueOf(textToVoice.getSpeed()));
       hashMap.put("VoiceType",textToVoice.getVoiceType());       //签名,公共参数不需要放到body中
       String sign = null;       try {
           sign = SignUtils.sign(treeMap, HttpMethodEnum.POST, SignMenodEnum.TC3_HMAC_SHA256, JSON.toJSONString(hashMap)
                   , TextToVoiceConstant.TEXT_TO_VOICE, sercretKey, ContentTypeEnum.JSON);
       } catch (Exception e) {
           log.error("签名异常:{}",e.getMessage());           return R.error("签名异常").setCode(SystemConstants.SERVER_ERROR_CODE);
       }       try {
           String respJson = HttpUtil.httpPost(TextToVoiceConstant.TEXT_TO_VOICE, JSON.parseObject(sign, Map.class),hashMap);
           JSONObject jsonObject = JSON.parseObject(respJson);
           String response = jsonObject.getString("Response");
           JSONObject error =(JSONObject) JSON.parseObject(response).get("Error");           if (Objects.nonNull(error)){               return R.error(String.valueOf(error.get("Message"))).setCode(SystemConstants.SERVER_ERROR_CODE);
           }else {
               TextToVoiceResponse textToVoiceResponse = JSON.parseObject(response, TextToVoiceResponse.class);               return R.ok(textToVoiceResponse.getAudio()).setCode(SystemConstants.SUCCESS_CODE);
           }

       } catch (Exception e) {
           log.error("语音合成失败:{}",e.getMessage());           return R.error("语音合成失败").setCode(SystemConstants.SERVER_ERROR_CODE);
       }
   }    /**
     * 封装请求公共参数
     * @param action
     * @param version
     * @return
     */
    public TreeMap createPublicMap(String action, String version,String region ){

        TreeMap<String,Object> treeMap = new TreeMap<>();
        treeMap.put("Action",action);
        treeMap.put("Version",version);
        treeMap.put("Region",region);
        treeMap.put("Timestamp",getCurrentTimestamp());
        treeMap.put("Nonce",new Random().nextInt(Integer.MAX_VALUE));
        treeMap.put("SecretId",sercretId);        return treeMap;
    }    /**
     * 获取当前时间戳,单位秒
     * @return
     */
    public static long getCurrentTimestamp() {        return System.currentTimeMillis()/1000;
    }

}

复制代码

复制代码

package com.example.combat.ttsutis;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.ToString;import java.io.Serializable;/**
 * 返回类型
 * @author choleece
 * @date 2018/9/27 */@ApiModel
@ToStringpublic class R<T> implements Serializable {    private static final long serialVersionUID = -6287952131441663819L;    /**
     * 编码     */
    @ApiModelProperty(value = "响应码", example = "200")    private int code = 200;    /**
     * 成功标志     */
    @ApiModelProperty(value = "成功标志", example = "true")    private Boolean success;    /**
     * 返回消息     */
    @ApiModelProperty(value = "返回消息说明", example = "操作成功")    private String msg="操作成功";    /**
     * 返回数据     */
    @ApiModelProperty(value = "返回数据")    private T data;    /**
     * 创建实例
     * @return
     */
    public static R instance() {        return new R();
    }    public int getCode() {        return code;
    }    public R setCode(int code) {        this.code = code;        return this;
    }    public Boolean getSuccess() {        return success;
    }    public R setSuccess(Boolean success) {        this.success = success;        return this;
    }    public String getMsg() {        return msg;
    }    public R setMsg(String msg) {        this.msg = msg;        return this;
    }    public T getData() {        return data;
    }    public R setData(T data) {        this.data = data;        return this;
    }    public static R ok() {        return R.instance().setSuccess(true);
    }    public static R ok(Object data) {        return ok().setData(data);
    }    public static R ok(Object data, String msg) {        return ok(data).setMsg(msg);
    }    public static R error() {        return R.instance().setSuccess(false);
    }    public static R error(String msg) {        return error().setMsg(msg);
    }    /**
     * 无参     */
    public R() {
    }    public R(int code, String msg) {        this.code = code;        this.msg = msg;
    }    public R(int code, T data){        this.code = code;        this.data = data;
    }    /**
     * 有全参
     * @param code
     * @param msg
     * @param data
     * @param success     */
    public R(int code, String msg, T data, Boolean success) {        this.code = code;        this.msg = msg;        this.data = data;        this.success = success;
    }    /**
     * 有参
     * @param code
     * @param msg
     * @param data     */
    public R(int code, String msg, T data) {        this.code = code;        this.msg = msg;        this.data = data;
    }  }

复制代码

复制代码

package com.example.combat.ttsutis;/**
 * @description: 语音合成常量
 * @author: zhucj
 * @date: 2019-11-27 15:51 */public class TextToVoiceConstant {    /**
     * 语音合成Api     */
    public static final String TEXT_TO_VOICE = "https://tts.ap-shenzhen-fsi.tencentcloudapi.com";
}

复制代码

复制代码

package com.example.combat.ttsutis.param;import io.swagger.annotations.ApiModel;import lombok.*;import javax.validation.constraints.NotNull;/**
 * @description:
 * @author: zhucj
 * @date: 2019-11-27 15:39 */@Data
@ApiModel(description = "语音合成请求实体")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToStringpublic class TextToVoice {    /**
     * 合成语音的源文本,按UTF-8编码统一计算。
     * 中文最大支持100个汉字(全角标点符号算一个汉字);
     * 英文最大支持400个字母(半角标点符号算一个字母)。包含空格等字符时需要url encode再传输。     */
    @NotNull(message = "合成语音的源文本不为空")    private String text;    /**
     * 音量大小,范围:[0,10],分别对应11个等级的音量,默认为0     */
    @NotNull(message = "音量大小不为空")    private String volume;    /**
     *语速,范围:[-2,2],分别对应不同语速:
     * -2代表0.6倍
     * -1代表0.8倍
     * 0代表1.0倍(默认)
     * 1代表1.2倍
     * 2代表1.5倍
     * 输入除以上整数之外的其他参数不生效,按默认值处理。     */
    @NotNull(message = "语速大小不为空")    private String speed;    /**
     * 音色
     * 0-亲和女声(默认)
     * 1-亲和男声
     * 2-成熟男声
     * 4-温暖女声
     * 5-情感女声
     * 6-情感男声     */
    @NotNull(message = "音色选择不为空")    private Integer voiceType;

}

复制代码

复制代码

package com.example.combat.ttsutis.param;import com.example.combat.afsutils.param.resp.Response;import io.swagger.annotations.ApiModel;import lombok.*;/**
 * @description:
 * @author: zhucj
 * @date: 2019-11-27 16:09 */@Data
@ApiModel(description = "语音合成响应实体")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToStringpublic class TextToVoiceResponse extends Response {    private String Audio;    private String SessionId;
}

复制代码

复制代码

package com.example.combat.afsutils;import com.alibaba.fastjson.JSON;import lombok.extern.slf4j.Slf4j;import org.apache.http.HttpEntity;import org.apache.http.NameValuePair;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.client.methods.HttpRequestBase;import org.apache.http.client.utils.URIBuilder;import org.apache.http.config.Registry;import org.apache.http.config.RegistryBuilder;import org.apache.http.conn.socket.ConnectionSocketFactory;import org.apache.http.conn.socket.PlainConnectionSocketFactory;import org.apache.http.conn.ssl.NoopHostnameVerifier;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.entity.ContentType;import org.apache.http.entity.StringEntity;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.message.BasicNameValuePair;import org.apache.http.ssl.SSLContextBuilder;import org.apache.http.ssl.SSLContexts;import org.apache.http.ssl.TrustStrategy;import org.apache.http.util.EntityUtils;import javax.net.ssl.SSLContext;import java.io.File;import java.io.FileInputStream;import java.nio.charset.StandardCharsets;import java.security.KeyStore;import java.security.cert.CertificateException;import java.security.cert.X509Certificate;import java.util.ArrayList;import java.util.List;import java.util.Map;

@Slf4jpublic class HttpUtil {    public static final ContentType TEXT_PLAIN = ContentType.create("text/plain", StandardCharsets.UTF_8);    /**
     * HttpClient 连接池     */
    private static PoolingHttpClientConnectionManager cm = null;    static {        // 初始化连接池,可用于请求HTTP/HTTPS(信任所有证书)
        cm = new PoolingHttpClientConnectionManager(getRegistry());        // 整个连接池最大连接数
        cm.setMaxTotal(200);        // 每路由最大连接数,默认值是2
        cm.setDefaultMaxPerRoute(5);
    }    /**
     * 发送 HTTP GET请求
     * <p>不带请求参数和请求头</p>
     * @param url 地址
     * @return
     * @throws Exception     */
    public static String httpGet(String url) throws Exception {
        log.info("请求参数:{}",url);
        HttpGet httpGet = new HttpGet(url);        return doHttp(httpGet);
    }    /**
     * 发送 HTTP GET请求
     * <p>带请求参数,不带请求头</p>
     * @param url    地址
     * @param params 参数
     * @return
     * @throws Exception
     * @throws Exception     */
    public static String httpGet(String url, Map<String, Object> params) throws Exception {        // 转换请求参数
        List<NameValuePair> pairs = covertParams2NVPS(params);        // 装载请求地址和参数
        URIBuilder ub = new URIBuilder();
        ub.setPath(url);
        ub.setParameters(pairs);

        HttpGet httpGet = new HttpGet(ub.build());        return doHttp(httpGet);
    }    /**
     * 发送 HTTP GET请求
     * <p>带请求参数和请求头</p>
     * @param url     地址
     * @param headers 请求头
     * @param params  参数
     * @return
     * @throws Exception
     * @throws Exception     */
    public static String httpGet(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {        // 转换请求参数
        List<NameValuePair> pairs = covertParams2NVPS(params);        // 装载请求地址和参数
        URIBuilder ub = new URIBuilder();
        ub.setPath(url);
        ub.setParameters(pairs);

        HttpGet httpGet = new HttpGet(ub.build());        // 设置请求头
        for (Map.Entry<String, Object> param : headers.entrySet()){
            httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));}        return doHttp(httpGet);
    }    /**
     * 发送 HTTP POST请求
     * <p>不带请求参数和请求头</p>
     *
     * @param url 地址
     * @return
     * @throws Exception     */
    public static String httpPost(String url) throws Exception {
        HttpPost httpPost = new HttpPost(url);        return doHttp(httpPost);
    }    /**
     * 发送 HTTP POST请求
     * <p>带请求参数,不带请求头</p>
     *
     * @param url    地址
     * @param params 参数
     * @return
     * @throws Exception     */
    public static String httpPost(String url, Map<String, Object> params) throws Exception {        // 转换请求参数
        List<NameValuePair> pairs = covertParams2NVPS(params);

        HttpPost httpPost = new HttpPost(url);        // 设置请求参数
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name()));        return doHttp(httpPost);
    }    /**
     * 发送 HTTP POST请求
     * <p>带请求参数和请求头</p>
     *
     * @param url     地址
     * @param headers 请求头
     * @param params  参数
     * @return
     * @throws Exception     */
    public static String httpPost(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
        log.info("POST请求参数:{}",params);
        HttpPost httpPost = new HttpPost(url);        // 设置请求参数
        StringEntity entity = new StringEntity(JSON.toJSONString(params),ContentType.APPLICATION_JSON);
        httpPost.setEntity(entity);        // 设置请求头
        for (Map.Entry<String, Object> param : headers.entrySet()){
            httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));}        return doHttp(httpPost);
    }    /**
     * 转换请求参数
     *
     * @param params
     * @return
     */
    public static List<NameValuePair> covertParams2NVPS(Map<String, Object> params) {
        List<NameValuePair> pairs = new ArrayList<NameValuePair>();        for (Map.Entry<String, Object> param : params.entrySet()){
            pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));}        return pairs;
    }    /**
     * 发送 HTTP 请求
     *
     * @param request
     * @return
     * @throws Exception     */
    private static String doHttp(HttpRequestBase request) throws Exception {        // 通过连接池获取连接对象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();        return doRequest(httpClient, request);
    }    /**
     * 发送 HTTPS 请求
     * <p>使用指定的证书文件及密码</p>
     *
     * @param request
     * @param path
     * @param password
     * @return
     * @throws Exception
     * @throws Exception     */
    private static String doHttps(HttpRequestBase request, String path, String password) throws Exception {        // 获取HTTPS SSL证书
        SSLConnectionSocketFactory csf = getSSLFactory(path, password);        // 通过连接池获取连接对象
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();        return doRequest(httpClient, request);
    }    /**
     * 获取HTTPS SSL连接工厂
     * <p>使用指定的证书文件及密码</p>
     *
     * @param path     证书全路径
     * @param password 证书密码
     * @return
     * @throws Exception
     * @throws Exception     */
    private static SSLConnectionSocketFactory getSSLFactory(String path, String password) throws Exception {        // 初始化证书,指定证书类型为“PKCS12”
        KeyStore keyStore = KeyStore.getInstance("PKCS12");        // 读取指定路径的证书
        FileInputStream input = new FileInputStream(new File(path));        try {            // 装载读取到的证书,并指定证书密码            keyStore.load(input, password.toCharArray());
        } finally {
            input.close();
        }        // 获取HTTPS SSL证书连接上下文
        SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray()).build();        // 获取HTTPS连接工厂,指定TSL版本
        SSLConnectionSocketFactory sslCsf = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());        return sslCsf;
    }    /**
     * 获取HTTPS SSL连接工厂
     * <p>跳过证书校验,即信任所有证书</p>
     *
     * @return
     * @throws Exception     */
    private static SSLConnectionSocketFactory getSSLFactory() throws Exception {        // 设置HTTPS SSL证书信息,跳过证书校验,即信任所有证书请求HTTPS
        SSLContextBuilder sslBuilder = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {                return true;
            }
        });        // 获取HTTPS SSL证书连接上下文
        SSLContext sslContext = sslBuilder.build();        // 获取HTTPS连接工厂
        SSLConnectionSocketFactory sslCsf = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);        return sslCsf;
    }    /**
     * 获取 HTTPClient注册器
     *
     * @return
     * @throws Exception     */
    private static Registry<ConnectionSocketFactory> getRegistry() {
        Registry<ConnectionSocketFactory> registry = null;        try {
            registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", new PlainConnectionSocketFactory()).register("https", getSSLFactory()).build();
        } catch (Exception e) {
            log.error("获取 HTTPClient注册器失败", e);
        }        return registry;
    }    /**
     * 处理Http/Https请求,并返回请求结果
     * <p>注:默认请求编码方式 UTF-8</p>
     *
     * @param httpClient
     * @param request
     * @return
     * @throws Exception     */
    private static String doRequest(CloseableHttpClient httpClient, HttpRequestBase request) throws Exception {
        String result = null;
        CloseableHttpResponse response = null;        try {            // 获取请求结果
            response = httpClient.execute(request);            // 解析请求结果
            HttpEntity entity = response.getEntity();            // 转换结果
            result = EntityUtils.toString(entity, StandardCharsets.UTF_8.name());            // 关闭IO流            EntityUtils.consume(entity);
        } finally {            if (null != response){
                response.close();}
        }        return result;
    }
}

复制代码

复制代码

package com.example.combat.afsutils;import com.alibaba.fastjson.JSON;import com.example.combat.config.constant.ContentTypeEnum;import com.example.combat.config.constant.HttpMethodEnum;import com.example.combat.config.constant.SignMenodEnum;import lombok.extern.slf4j.Slf4j;import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import javax.xml.bind.DatatypeConverter;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.nio.charset.Charset;import java.nio.charset.StandardCharsets;import java.security.MessageDigest;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Objects;import java.util.TimeZone;import java.util.TreeMap;/**
 * @description: 腾讯云 签名方法
 * @author: zhucj
 * @date: 2019-10-18 14:14 */@Slf4jpublic class SignUtils {    private final static String CHARSET = "UTF-8";    private final static Charset UTF8 = StandardCharsets.UTF_8;    public static String sign(TreeMap<String, Object> params, HttpMethodEnum menth, SignMenodEnum signMenodEnum,
                              String jsonString, String reqUrl, String sercretKey, ContentTypeEnum typeEnum) throws Exception {
        String signString = null;
        String  sercretId  = String.valueOf(params.get("SecretId"));        switch (signMenodEnum){            case TC3_HMAC_SHA256:
                String replace = reqUrl.replace("https://", "");
                String service = replace.substring(0,3);
                String host = replace;
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");                // 注意时区,否则容易出错
                sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
                String date = sdf.format(new Date(Long.valueOf(params.get("Timestamp") + "000")));                // ************* 步骤 1:拼接规范请求串 *************
                String canonicalUri = "/";
                String canonicalQueryString = "";
                String canonicalHeaders = "content-type:"+typeEnum.getName() +"\n" + "host:" + host + "\n";
                String signedHeaders = "content-type;host";

                String hashedRequestPayload = sha256Hex(jsonString);
                String canonicalRequest = menth.getName() + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
                        + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;                // ************* 步骤 2:拼接待签名字符串 *************
                String credentialScope = date + "/" + service + "/" + "tc3_request";
                String hashedCanonicalRequest = sha256Hex(canonicalRequest);
                String stringToSign = signMenodEnum.getMendoName() + "\n" + params.get("Timestamp") + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
                log.info("待签名参数:{}",stringToSign);                // ************* 步骤 3:计算签名 *************
                byte[] secretDate = hmac256(("TC3" +sercretKey).getBytes(UTF8), date);                byte[] secretService = hmac256(secretDate, service);                byte[] secretSigning = hmac256(secretService, "tc3_request");
                String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();
                log.info("生成签名参数:{}",signature);                // ************* 步骤 4:拼接 Authorization *************
                String authorization = signMenodEnum.getMendoName() + " " + "Credential=" + sercretId + "/" + credentialScope + ", "
                        + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
                log.info("生成authorization参数:{}",authorization);

                TreeMap<String, String> headers = new TreeMap<String, String>();
                headers.put("Authorization", authorization);
                headers.put("Content-Type",typeEnum.getName());
                headers.put("Host", host);
                headers.put("X-TC-Action",String.valueOf(params.get("Action")) );
                headers.put("X-TC-Timestamp",String.valueOf(params.get("Timestamp")));
                headers.put("X-TC-Version",String.valueOf(params.get("Version")));                if (Objects.nonNull(params.get("Region"))){
                    headers.put("X-TC-Region",String.valueOf(params.get("Region")));
                }
                signString = JSON.toJSONString(headers);                break;                default:
                    StringBuilder s2s = new StringBuilder(reqUrl.replace("https://",menth.getName())+"/?");                    // 签名时要求对参数进行字典排序,此处用TreeMap保证顺序
                    for (String k : params.keySet()) {
                        s2s.append(k).append("=").append(params.get(k).toString()).append("&");
                    }
                    String s = s2s.toString().substring(0, s2s.length() - 1);
                    Mac mac = Mac.getInstance(signMenodEnum.getMendoName());
                    SecretKeySpec secretKeySpec = new SecretKeySpec(sercretKey.getBytes(CHARSET), mac.getAlgorithm());
                    mac.init(secretKeySpec);                    byte[] hash = mac.doFinal(s.getBytes(CHARSET));
                    signString = DatatypeConverter.printBase64Binary(hash);                    break;
        }        return signString ;


    }    /**
     * 获取签名之后的请求Url
     * @param params
     * @return
     * @throws UnsupportedEncodingException     */
    public static String getUrl(TreeMap<String, Object> params,String reqUrl) throws UnsupportedEncodingException {
        StringBuilder url = new StringBuilder(reqUrl+"/?");        // 实际请求的url中对参数顺序没有要求
        for (String k : params.keySet()) {            // 需要对请求串进行urlencode,由于key都是英文字母,故此处仅对其value进行urlencode
            url.append(k).append("=").append(URLEncoder.encode(params.get(k).toString(), CHARSET)).append("&");
        }        return url.toString().substring(0, url.length() - 1);
    }    public static String getUrl(TreeMap<String, Object> params,String reqUrl,String jsonString,ContentTypeEnum typeEnum){
        String replace = reqUrl.replace("https://", "");
        StringBuilder sb = new StringBuilder();
        sb.append("curl -X POST https://").append(replace)
                .append(" -H \"Authorization: ").append(params.get("Authorization")).append("\"")
                .append(" -H \"Content-Type:").append(typeEnum.getName())
                .append(" -H \"Host: ").append(replace).append("\"")
                .append(" -H \"X-TC-Action: ").append(params.get("Action")).append("\"")
                .append(" -H \"X-TC-Timestamp: ").append(params.get("Timestamp")).append("\"")
                .append(" -H \"X-TC-Version: ").append(params.get("Version")).append("\"");        if (Objects.nonNull(params.get("Region"))){
            sb.append(" -H \"X-TC-Region: ").append(params.get("Region")).append("\"");
        }
        sb.append(" -d '").append(jsonString).append("'");        return sb.toString();
    }    public static byte[] hmac256(byte[] key, String msg) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        mac.init(secretKeySpec);        return mac.doFinal(msg.getBytes(UTF8));
    }    public static String sha256Hex(String s) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");        byte[] d = md.digest(s.getBytes(UTF8));        return DatatypeConverter.printHexBinary(d).toLowerCase();
    }


}

复制代码

#腾讯云服务配置
tencent:
  secretId: *******
  secretKey: *******
  pathImg: E:/upload/


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐