关于公众号跳转小程序的签名问题

关于公众号跳转小程序的签名问题

官方开发文档https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62

首先介绍如何获取签名(signature):

首先需要jsapi_ticket,而获取jsapi_ticket需要使用到access_token,获取access_token需要用到公众号的APPID和APPSECRET。

签名算法

签名生成规则**(签名生成规则的代码在下面)**如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。

即signature=sha1(string1)。 示例:

noncestr=Wm3WZYPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VMP37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=141458457
url=http://mp.weixin.qq.com?params=value

步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:

jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3M0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7q&noncestr=Wm3WZYTPzwzccnW&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value

步骤2. 对string1进行sha1签名,得到signature:0f9de62fce790f9a083d5c99e95740ceb9027ed

直接上代码,在我的苦心摸索下:

获取access_token和jsapi_ticket过程,调用签名生成规则最终的签名(signature):

String nonceStr = StrUtil.uuid();//一个随机数
long timeStampSec = System.currentTimeMillis()/1000;
String timestamp = String.format("%010d", timeStampSec);//时间戳需要用到十位的(也就是到秒)
String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
String requestUrl = token_url.replace("APPID", "wxc35b08974f2aa28b").replace("APPSECRET","a17d1fe4e4b20c88af8ab019cd45e400");
String getTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
SmartLogger.info("url",Url);
// 发起GET请求获取凭证
try {
    String access_token ="";
    access_token = Jboot.getCache().get("access_token", "access_token");
    if (access_token=="" || access_token == null ) {
        URL url = new URL(requestUrl);
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        // 设置请求方式(GET/POST)
        conn.setRequestMethod("GET");
        // 从输入流读取返回内容
        InputStream inputStream = conn.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String str = null;
        StringBuffer buffer = new StringBuffer();
        while ((str = bufferedReader.readLine()) != null) {
            buffer.append(str);
        }

        // 释放资源
        bufferedReader.close();
        inputStreamReader.close();
        inputStream.close();
        conn.disconnect();
        JSONObject jsonObject = JSONObject.fromObject(buffer.toString());
        access_token = jsonObject.getString("access_token");
        Jboot.getCache().put("access_token", "access_token", access_token,7200);
    }

    String JsApiTicket ="";
    JsApiTicket = Jboot.getCache().get("JsApiTicket", "JsApiTicket");
    if (JsApiTicket=="" || JsApiTicket == null) {
        String requestGetTicketUrl = getTicketUrl.replace("ACCESS_TOKEN", access_token);
        URL url1 = new URL(requestGetTicketUrl);
        HttpsURLConnection conn1 = (HttpsURLConnection) url1.openConnection();
        // 设置请求方式(GET/POST)
        conn1.setRequestMethod("GET");
        // 从输入流读取返回内容
        InputStream inputStream1 = conn1.getInputStream();
        InputStreamReader inputStreamReader1 = new InputStreamReader(inputStream1, "utf-8");
        BufferedReader bufferedReader1 = new BufferedReader(inputStreamReader1);
        String str1 = null;
        StringBuffer buffer1 = new StringBuffer();
        while ((str1 = bufferedReader1.readLine()) != null) {
            buffer1.append(str1);
        }

        // 释放资源
        bufferedReader1.close();
        inputStreamReader1.close();
        inputStream1.close();
        conn1.disconnect();

        JSONObject jsonObject1 = JSONObject.fromObject(buffer1.toString());
        JsApiTicket = jsonObject1.getString("ticket");
        Jboot.getCache().put("JsApiTicket","JsApiTicket",JsApiTicket,7200);
    }
    String jsSdkSign = WxJsSignUtil.getJsSdkSign(nonceStr, JsApiTicket, timestamp + "", Url);
    SmartLogger.info("url",nonceStr+"======"+jsSdkSign +"======"+timestamp+"======"+JsApiTicket);
    renderJson(Ret.ok().set("noncestr",nonceStr).set("signature",jsSdkSign).set("timestamp",timestamp).set("JsApiTicket",JsApiTicket));

}catch (Exception e){
    SmartLogger.error("error","公众号跳转小程序签名错误");
    renderFailJson();
}

签名生成规则的代码:

/**
 * 获取jsSdkSign
 * @param noncestr
 * @param ticket
 * @param timestamp
 * @param url
 * @return
 */
    public static String getJsSdkSign(String noncestr,String ticket,String timestamp,String url){
        /* 加密/校验流程如下:
         *1. 将token、timestamp、nonce三个参数进行字典序排序
         * 2. 将三个参数字符串拼接成一个字符串进行sha1加密
         * 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信 */
        // 1.排序
        String sortString = sort(url, timestamp, ticket, noncestr);
        // 2.sha1加密
        String signature = sha1(sortString);
        return signature;
    }

/**
 * @Author zhaohp
 * @Date 2018/12/10 19:50
 * @Param [signature, timestamp, ticket, echostr]
 * @Return java.lang.String
 * @Description: 对所有待签名参数按照字段名的ASCII 码从小到大排序
 */
public static String sort(String url, String timestamp, String ticket, String echostr) {
    return "jsapi_ticket=" + ticket + "&noncestr=" + echostr + "&timestamp=" + timestamp + "&url=" + url;
}

/**
 * @Author zhaohp
 * @Date 2018/12/10 19:51
 * @Param [str]
 * @Return java.lang.String
 * @Description: 将字符串进行sha1加密
 */
public static String sha1(String str) {
    try {
        MessageDigest digest = MessageDigest.getInstance("SHA-1");
        digest.update(str.getBytes());
        byte messageDigest[] = digest.digest();
        // 创建 16进制字符串
        StringBuffer hexString = new StringBuffer();
        // 字节数组转换为 十六进制 数
        for (int i = 0; i < messageDigest.length; i++) {
            String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
            if (shaHex.length() < 2) {
                hexString.append(0);
            }
            hexString.append(shaHex);
        }
        return hexString.toString();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

值得注意的是:

生成的access_token和jspapi_ticket的有效期是7200秒,需要在你的项目中进行缓存,如果第三方不使用中控服务器,而是选择各个业务逻辑点各自去刷新access_token,那么就可能会产生冲突,导致服务不稳定。

请求的服务器域名需要在微信公众后台中:开发—>基本配置 中设置IP白名单

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值