阿里搜索下拉提示签名机制

最近公司在做搜索,要求下拉提示使用阿里的下拉提示,其他都还好说,就是这个签名机制,弄了一下午,也是遇到了坑,特此记录。

阿里签名机制文档

    //签名
    public static final String ALGORITHM = "HmacSHA1";
    public static final String ENCODING = "UTF-8";

    //分隔符
    public static final String SEPARATOR = "&";

     /**
     * 请求参数
     */
    public OkRequestParams getRequestParams() {
        OkRequestParams params = new OkRequestParams();
        //公共参数
        params.put("Version", "v2");
        params.put("AccessKeyId", ACCESS_KEY_ID);
        params.put("SignatureMethod", "HMAC-SHA1");
        params.put("Timestamp", DateTimeUtil.local2Utc(System.currentTimeMillis()));
        params.put("SignatureVersion", "1.0");
        params.put("SignatureNonce", System.currentTimeMillis() + Util.getNewRandomStr(4));

        //下拉提示请求参数
        params.put("query", mSearchStr);
        params.put("index_name", INDEX_NAME);
        params.put("suggest_name", SUGGEST_NAME);

        //签名:需要以上参数共同参与才能得出签名
        params.put("Signature", getSignature(params).trim());

        return params;
    }

    /**
     * 根据请求参数获得签名
     * @param urlParams
     * @return
     */
    private String getSignature(OkRequestParams urlParams) {
        String signature = "";
        try {
            //排序
            String[] sortedKeys = urlParams.getUrlParams().keySet().toArray(new String[]{});
            Arrays.sort(sortedKeys);

            // 生成stringToSign字符串
            StringBuilder stringToSign = new StringBuilder();
            stringToSign.append("GET").append(SEPARATOR);
            stringToSign.append(percentEncode("/")).append(SEPARATOR);

            StringBuilder canonicalizedQueryString = new StringBuilder();
            for (String key : sortedKeys) {
                //对key和value进行编码
                canonicalizedQueryString.append(SEPARATOR).append(percentEncode(key)).append("=").append(percentEncode(urlParams.getUrlParams().get(key)));
            }

            //对canonicalizedQueryString进行编码
            stringToSign.append(percentEncode(canonicalizedQueryString.toString().substring(1)));

            //用stringToSign计算签名HMAC值
            String key = SECRET + SEPARATOR;
            Mac mac = Mac.getInstance(ALGORITHM);
            mac.init(new SecretKeySpec(key.getBytes(ENCODING), ALGORITHM));
            byte[] signData = mac.doFinal(stringToSign.toString().getBytes(ENCODING));

            //按照Base64编码规则把上面的HMAC值编码成字符串
            signature = Base64.encodeToString(signData, Base64.DEFAULT);

        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
        return signature;

    }

    /**
     * RFC3986编码规则:编码后的字符串中加号(+)替换成%20、星号(*)替换成%2A、%7E替换回波浪号(~)即可
     * @param value
     * @return
     * @throws UnsupportedEncodingException
     */
    private String percentEncode(String value) throws UnsupportedEncodingException {
        return value != null ? URLEncoder.encode(value, "utf-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null;
    }
    /**
     * 本地时间转UTC时间
     * @param localTime
     * @return
     */
    public static String local2Utc(long localTime){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        return simpleDateFormat.format(new Date(localTime));
    }
    /**
     * 获取count个随机数
     * @param count
     * @return
     */
    public static String getNewRandomStr(int count){
        StringBuffer sb = new StringBuffer();
        String str = "0123456789";
        Random r = new Random();
        for (int i = 0; i < count; i++) {
            int num = r.nextInt(str.length());
            sb.append(str.charAt(num));
            str = str.replace((str.charAt(num) + ""), "");
        }
        return sb.toString();
    }

说说坑,第一个坑就是getSignature(params)这里没有去空格,导致编码多出个空格的编码;

        //签名:需要以上参数共同参与才能得出签名
        params.put("Signature", getSignature(params).trim());

第二个坑就是对签名多进行了一次编码,文档中说

注意: 得到的签名值在作为最后的请求参数值提交给OPENSEARCH服务器的时候,要和其他参数一样,按照RFC3986的规则进行URL编码)

好吧,怪我自己没有注意到我们的网络请求框架已经对请求参数进行了编码,结果我自己又多编码了一次。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值