zoukankan      html  css  js  c++  java
  • 微信 JS-SDK 签名验证

    doc: http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
    demo:http://demo.open.weixin.qq.com/jssdk/
    sandbox:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign

    生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
    参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token):
    用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):
    https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

    注意事项

    1.设置JS接口安全域名
    2.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
    3.签名用的url必须是调用JS接口页面的完整URL。
    4.出于安全考虑,开发者必须在服务器端实现签名的逻辑。

    JAVA实现

    public class WxSign {
        @SuppressWarnings({ "unchecked", "unchecked" })
        public static void main(String[] args) throws Exception {
            /*
            String  access_token= WeChat.getAccessToken();
            String jsapi_ticket = WeChat.getJsApiTicket(access_token);
            */
            //System.out.println("access_token : "+access_token+ " jsapi_ticket: " +jsapi_ticket);
            String jsapi_ticket="jsapi_ticket";
            String url = "http://cmsplus.com.cn";
            Map<String, String> ret = sign(jsapi_ticket, url);
            for (Map.Entry entry : ret.entrySet()) {
                //System.out.println(entry.getKey() + "======== " + entry.getValue());
            }
            System.out.println("signature:  "+ret.get("signature") +  ": timestamp " +ret.get("timestamp"));
            System.out.println(createLinkString(ret));
        };
    
        //对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后
         /** 
         * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
         * @param params 需要排序并参与字符拼接的参数组
         * @return 拼接后字符串
         */
        public static String createLinkString(Map<String, String> params) {
            List<String> keys = new ArrayList<String>(params.keySet());
            Collections.sort(keys);
            String prestr = "";
            for (int i = 0; i < keys.size(); i++) {
                String key = keys.get(i);
                String value = params.get(key);
                if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
                    prestr = prestr + key + "=" + value;
                } else {
                    prestr = prestr + key + "=" + value + "&";
                }
            }
            return prestr;
        }
        
        public static Map<String, String> sign(String jsapi_ticket, String url) {
            Map<String, String> ret = new HashMap<String, String>();
            String nonce_str = create_nonce_str();
            String timestamp = create_timestamp();
            String string1;
            String signature = "";
            // 注意这里参数名必须全部小写,且必须有序
            string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "&timestamp=" + timestamp + "&url=" + url;
            //System.out.println(string1);
            try {
                MessageDigest crypt = MessageDigest.getInstance("SHA-1");
                crypt.reset();
                crypt.update(string1.getBytes("UTF-8"));
                signature = byteToHex(crypt.digest());
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            ret.put("url", url);
            ret.put("jsapi_ticket", jsapi_ticket);
            ret.put("nonceStr", nonce_str);
            ret.put("timestamp", timestamp);
            ret.put("signature", signature);
            return ret;
        }
    
        private static String byteToHex(final byte[] hash) {
            Formatter formatter = new Formatter();
            for (byte b : hash) {
                formatter.format("%02x", b);
            }
            String result = formatter.toString();
            formatter.close();
            return result;
        }
    
        private static String create_nonce_str() {
            return UUID.randomUUID().toString();
        }
    
        private static String create_timestamp() {
            return Long.toString(System.currentTimeMillis() / 1000);
        }
    }
    public class WeChat {
        private static final String ACCESSTOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
        private static final String PAYFEEDBACK_URL = "https://api.weixin.qq.com/payfeedback/update";
        /**
         * 获取access_token
         * 
         * @return
         * @throws Exception
         */
        public static String getAccessToken() throws Exception {
            String appid = ConfKit.get("AppId");
            String secret = ConfKit.get("AppSecret");
            String jsonStr = HttpKit.get(ACCESSTOKEN_URL.concat("&appid=") + appid + "&secret=" + secret);
            Map<String, Object> map = JSONObject.parseObject(jsonStr);
            return map.get("access_token").toString();
        }
        
        /**
         * 获取jsapi_ticket
         * 
         * @return
         * @throws Exception
         */
        public static String getJsApiTicket(String accessToken) throws Exception {
            String jsonStr = HttpKit.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+accessToken+"&type=jsapi");
            Map<String, Object> map = JSONObject.parseObject(jsonStr);
            return map.get("ticket").toString();
        }
    
        /**
         * 获取access_token
         * 
         * @return
         * @throws Exception
         */
        public static String getAccessToken(String appid, String secret) throws Exception {
            String jsonStr = HttpKit.get(ACCESSTOKEN_URL.concat("&appid=") + appid + "&secret=" + secret);
            Map<String, Object> map = JSONObject.parseObject(jsonStr);
            return map.get("access_token").toString();
        }
    
        /**
         * 支付反馈
         * 
         * @param openid
         * @param feedbackid
         * @return
         * @throws Exception
         */
        public static boolean payfeedback(String openid, String feedbackid) throws Exception {
            Map<String, String> map = new HashMap<String, String>();
            String accessToken = getAccessToken();
            map.put("access_token", accessToken);
            map.put("openid", openid);
            map.put("feedbackid", feedbackid);
            String jsonStr = HttpKit.get(PAYFEEDBACK_URL, map);
            Map<String, Object> jsonMap = JSONObject.parseObject(jsonStr);
            return "0".equals(jsonMap.get("errcode").toString());
        }
    
        /**
         * 判断是否来自微信, 5.0 之后的支持微信支付
         * 
         * @param request
         * @return
         */
        public static boolean isWeiXin(HttpServletRequest request) {
            String userAgent = request.getHeader("User-Agent");
            if (StringUtils.isNotBlank(userAgent)) {
                Pattern p = Pattern.compile("MicroMessenger/(\d+).+");
                Matcher m = p.matcher(userAgent);
                String version = null;
                if (m.find()) {
                    version = m.group(1);
                }
                return (null != version && NumberUtils.toInt(version) >= 5);
            }
            return false;
        }

    javascript

    wx.config({
            debug: false,
            appId: '${appId}',
            timestamp: '${timestamp}',
            nonceStr: '${nonceStr}',
            signature: '${signature}',
            jsApiList: [
            'checkJsApi',
            'onMenuShareTimeline',
            'onMenuShareAppMessage',
            'onMenuShareQQ',
            'onMenuShareWeibo',
            'hideMenuItems',
            'showMenuItems',
            'hideAllNonBaseMenuItem',
            'showAllNonBaseMenuItem',
            'translateVoice',
            'startRecord',
            'stopRecord',
            'onRecordEnd',
            'playVoice',
            'pauseVoice',
            'stopVoice',
            'uploadVoice',
            'downloadVoice',
            'chooseImage',
            'previewImage',
            'uploadImage',
            'downloadImage',
            'getNetworkType',
            'openLocation',
            'getLocation',
            'hideOptionMenu',
            'showOptionMenu',
            'closeWindow',
            'scanQRCode',
            'chooseWXPay',
            'openProductSpecificView',
            'addCard',
            'chooseCard',
            'openCard'
            ]
            });
            wx.ready(function () {
                var shareData = {
                    title: '这是活动的介绍页',
                    desc: '这里是发送给好友的时候的简介',
                    link: 'http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html',
                    imgUrl: 'http://demo.open.weixin.qq.com/jssdk/images/p2166127561.jpg',
                };
                 //wx.onMenuShareAppMessage(shareData);
                 wx.onMenuShareAppMessage({
                      title: '互联网之子',
                      desc: '在长大的过程中,我才慢慢发现,我身边的所有事,别人跟我说的所有事,那些所谓本来如此,注定如此的事,它们其实没有非得如此,事情是可以改变的。更重要的是,有些事既然错了,那就该做出改变。',
                      link: 'http://movie.douban.com/subject/25785114/',
                      imgUrl: 'http://demo.open.weixin.qq.com/jssdk/images/p2166127561.jpg',
                      trigger: function (res) {
                        // 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
                        alert('用户点击发送给朋友');
                      },
                      success: function (res) {
                        alert('已分享');
                      },
                      cancel: function (res) {
                        alert('已取消');
                      },
                      fail: function (res) {
                        alert(JSON.stringify(res));
                      }
                    });
                wx.onMenuShareTimeline(shareData);
                // 要隐藏的菜单项,只能隐藏“传播类”和“保护类”按钮,所有menu项见附录3
                wx.hideMenuItems({
                    menuList: [
                               'menuItem:copyUrl'
                              ] 
                });
                //hide
                //wx.hideAllNonBaseMenuItem();
                //wx.hideOptionMenu();
            });
            wx.error(function (res) {
                alert("error: "+ res.errMsg);
            });

    实现隐藏复制链接,分享的时候改变分析的地址与内容等,其他接口按照文档来吧,没什么复杂滴。

    其他语言实现

    Nodejs实现:http://www.57kan.com/show/index/id/15907
    https://github.com/willian12345/wechat-JS-SDK-demo
    PHP实现:https://github.com/wjfz/weixin-jssdk

    Refer:
    微信js sdk invalid signature签名错误 问题解决
    http://my.oschina.net/u/2308739/blog/371414

  • 相关阅读:
    Transcation And Lock--SQL SERVER 事务隔离级别
    Transaction And Lock--常用的查询事务和锁的语句
    使用shell读取文本文件发送到kafka
    VIM打开shell脚本中文乱码解决
    shell中日期操作
    oozie常见错误问题
    error: No implicit Ordering defined for Any
    启动mysql时显示:/tmp/mysql.sock 不存在的解决方法
    mysql中创建用户和赋权限
    (转)maven3.3.9编译oozie4.3.0
  • 原文地址:https://www.cnblogs.com/Irving/p/4320923.html
Copyright © 2011-2022 走看看