网关认证

  新华智云OpenAPI是新华智云统一对外提供SaaS服务的统一API出口,服务包括但不限于:MAGIC,智能媒资,融媒平台,视频中台,AI中台、元卯、数字人等等。提供基础鉴权、流控控制、负载均衡等能力。

https://api.shuwen.com

query参数

参数名类型说明
access_keyString接入标识,需先申请
timestampString请求的时间戳
sign_typeString签名方式。取值范围:MD5。
sign_versionString签名算法版本。取值范围:2.0。
sign_nonceString签名唯一随机数。用于防止网络重放攻击,建议您每一次请求都使用不同的随机数(在一定时间范围内部允许重复)
signatureString 以上参数进行加密计算后的签名,该字段非加签字段

Java

import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/**
 * 签名工具类
 */
public class OpenApiV1Demo {
    /**
     * 过期时间:5分钟
     */
    private static final Long AUTH_TIME_EXPIRED = 5 * 60 * 1000L;

    /**
     * 检验
     *
     * @param accessKey ak(用户传递)
     * @param secretKey sk(自己保存)
     * @param timestamp 时间戳(用户传递)
     * @param signature 签名串(用户传递)
     * @param param 字典序的参数字符串(例如:access_key=***&status=test&state=bobo188&timestamp=1571744445945)
     * @return Boolean
     * @throws Exception 鉴权错误信息
     */
    public static Boolean check(String accessKey, String secretKey, String timestamp, String signature,String param)
            throws Exception {
        if (StringUtils.isBlank(timestamp)) {
            System.err.println("鉴权参数不可为空, timestamp=" + timestamp);
            throw new IllegalArgumentException();
        }
        long time = 0;
        try {
            time = Long.parseLong(timestamp);
        } catch (NumberFormatException e) {
            throw new RuntimeException("鉴权参数异常!");
        }
        if (System.currentTimeMillis() - time > AUTH_TIME_EXPIRED) {
            throw new RuntimeException("鉴权时间过期!");
        }

        String sign = getSignature(accessKey, secretKey, timestamp,param);
        return StringUtils.equals(sign, signature);
    }

    /**
     * @param params
     * @return
     */
    public static String sortParamToString(Map<String, String> params) {
        StringBuilder sb = new StringBuilder();
        if (Objects.nonNull(params) && params.size() > 0) {
            params.entrySet()
                    .stream()
                    .sorted(Map.Entry.comparingByKey())
                    .forEach(paramEntry -> {
                        sb.append(paramEntry.getKey()).append("=").append(paramEntry.getValue()).append("#");
                    });
        }
        String res = sb.toString();
        return res;
    }

    /**
     * 获取签名串
     *
     * @param accessKey ak
     * @param secretKey sk
     * @param timestamp 时间戳
     * @param param     字典序的参数(例如:access_key=***&status=test&state=bobo188&timestamp=1571744445945)
     * @return 签名串
     */
    public static String getSignature(String accessKey, String secretKey, String timestamp, String param) {
        if (StringUtils.isAnyBlank(accessKey, secretKey,timestamp)) {
            System.err.println("鉴权参数不可为空, accessKey=" + accessKey + ", secretKey=" + secretKey + ",timestamp=" + timestamp);
            throw new IllegalArgumentException();
        }
        String needSignatureStr = secretKey + "$" + timestamp + "$" + accessKey + "$" + param;
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(needSignatureStr.getBytes("UTF-8"));
            byte[] summery = md5.digest();
            StringBuilder md5StrBuilder = new StringBuilder();

            for (byte aSummery : summery) {
                if (Integer.toHexString(255 & aSummery).length() == 1) {
                    md5StrBuilder.append("0").append(Integer.toHexString(255 & aSummery));
                } else {
                    md5StrBuilder.append(Integer.toHexString(255 & aSummery));
                }
            }
            return md5StrBuilder.toString();
        } catch (Exception e) {
            System.err.println("获取签名串失败" + e);
            return "";
        }
    }

    /**
     *
     * @param accessKey
     * @param secretKey
     * @param bizParam  map类型的业务业务参数名
     * @return  返回构造好的带有签名和参数的url
     */
    public static String getSignUrl(String accessKey, String secretKey, Map<String, String> bizParam){
        if(Objects.isNull(bizParam)||bizParam.size()==0){
            bizParam = new HashMap<>();
        }
        if(Objects.isNull(bizParam.get("sign_version"))){
            bizParam.put("sign_version","2.0");
        }
        if(Objects.isNull(bizParam.get("sign_nonce"))){
            bizParam.put("sign_nonce", UUID.randomUUID().toString().replace("-",""));
        }
        if(Objects.isNull(bizParam.get("sign_type"))){
            bizParam.put("sign_type","MD5");
        }
        String currentTime = String.valueOf(System.currentTimeMillis());
        if(Objects.isNull(bizParam.get("timestamp"))){
            bizParam.put("timestamp",currentTime);
        }
        //添加access_key
        bizParam.put("access_key",accessKey);
        //排序后的参数字符串
        String sortParam = sortParamToString(bizParam);
        //MD5加密环节
        String sign = getSignature(accessKey,secretKey,currentTime,sortParam);
        //构造url后续参数
        String url = getUrlParamsByMap(bizParam);
        String signUrl = url.concat("&signature=").concat(sign);
        return signUrl;
    }

    /**
     * 将map转换成url
     *
     * @param map
     * @return
     */
    public static String getUrlParamsByMap(Map<String, String> map) {
        if (map == null) {
            return "";
        }
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            sb.append(entry.getKey() + "=" + entry.getValue());
            sb.append("&");
        }
        String s = sb.toString();
        if (s.endsWith("&")) {
            s = StringUtils.substringBeforeLast(s, "&");
        }
        return s;
    }


    /**
     * 获取url后拼接的参数,使用时在需要请求的url后拼接"?"和返回的参数
     * 本段代码返回样式如:
     *  sign_version=2.0&access_key=accessKey&sign_nonce=08b02b5b0e8243528369e1befddfbcef&sign_type=MD5&timestamp=1627456021388&signature=727faa633c944b3f756bef95d80df954
     * 拼接后的请求url为:
     *  http://api.shuwen.com/xxxxx/xxx?sign_version=2.0&access_key=accessKey&sign_nonce=08b02b5b0e8243528369e1befddfbcef&sign_type=MD5&timestamp=1627456021388&signature=727faa633c944b3f756bef95d80df954
     */
    public static void main(String args[]) {
        // todo 填充对应 aksk
        String ak = "";
        String sk = "";


        String auth = getSignUrl(ak, sk, null);
        System.out.println(auth);
    }
}

Python

# coding=utf-8

import time
import hashlib
import uuid


class AuthUtil:

    def __init__(self):
        pass

    @staticmethod
    def get_auth_url(ak_str, sk_str, params_dict):
        if not params_dict:
            params_dict = {}
        if 'sign_version' not in params_dict.keys():
            params_dict['sign_version'] = '2.0'
        if 'sign_nonce' not in params_dict.keys():
            params_dict['sign_nonce'] = str(uuid.uuid4()).replace('-', '')
        if 'sign_type' not in params_dict.keys():
            params_dict['sign_type'] = 'MD5'
        timestamp = (str(round(time.time() * 1000)))
        if 'timestamp' not in params_dict.keys():
            params_dict['timestamp'] = timestamp
        params_dict['access_key'] = ak_str
        sorted_params = AuthUtil.sort_params_str(params_dict)
        signature = AuthUtil.get_signature(ak_str, sk_str, sorted_params, str(round(time.time() * 1000)))
        url = AuthUtil.get_url_params(params_dict)
        return url + '&signature=' + signature

    @staticmethod
    def get_url_params(params_dict):
        if not params_dict:
            return ''
        url_params = ''
        for key, value in params_dict.items():
            url_params += key + '=' + value + '&'
        if url_params.endswith('&'):
            url_params = url_params[0:-1]
        return url_params

    @staticmethod
    def sort_params_str(params_dict):
        keys = sorted(params_dict.keys())
        sorted_params_str = ''
        for key in keys:
            sorted_params_str += key + '=' + params_dict[key] + '#'
        return sorted_params_str

    @staticmethod
    def get_signature(ak_str, sk_str, param_str, timestamp):
        if not ak_str or not sk_str or not param_str:
            raise Exception('参数缺失')
        need_sign_str = sk_str + '$' + timestamp + '$' + ak_str + '$' + param_str
        m = hashlib.md5()
        m.update(need_sign_str.encode("utf8"))
        return m.hexdigest()

6. 统一异步回调aksk验证

标题部分 6. 统一异步回调aksk验证

智云提供服务中如涉及对外回调部分,为保障请求来源安全,建议在请求参数中验证签名是否正确

  • 请求方式:POST
  • 请求体内容: 业务自定义
  • 请求参数:如下
字段类型说明
timestampString时间戳, 接口调用时的时间戳
signatureString 加签信息, 加签信息计算规则md5(sk + $ + timestamp + $ + ak) timestamp 就是上面的入参aksk 与向客户提供的 aksk 相同为保证接口请求安全, 客户需验证接收到的加签信息是否一致

如回调样例

http://demo.shuwen.com/digital/notify?timestamp=1679646235565&signature=xxxxx

失败code码:

错误码说明
SW-GW-1002鉴权时间过期
SW-GW-1003鉴权不通过
SW-GW-1004鉴权参数检验不通过
SW-GW-1005当前ak无权访问此接口,请联系管理员