微信分享详细步骤--java版

公司需要做一个H5手机端投票活动,涉及到分享授权登陆和微信分享,这里就只讲微信分享。废话不多说了。欢迎指出文中不足。

一.线下测试(测试公众号)

1.申请一个微信测试公众号

网址:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login&token=68030871&lang=zh_CN

2.微信官方的JS-SDK使用步骤

网址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

1步骤一:绑定域名

测试公众号的域名可以是IP也可以域名,注意的是不要使用http://或者https://;因为这里的域名不是网址,而是一个字符串。我也被坑了一下

步骤二:引入JS文件

在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.4.0.js

如需进一步提升服务稳定性,当上述资源不可访问时,可改访问:http://res2.wx.qq.com/open/js/jweixin-1.4.0.js (支持https)。

备注:支持使用 AMD/CMD 标准模块加载方法加载

步骤三:通过config接口注入权限验证配置(请看官方文档)

步骤四:通过ready接口处理成功验证(请看官方文档)

步骤五:通过error接口处理失败验证(请看官方文档)

3.通过java后台获取 微信 JS 接口签名

微信 JS 接口签名校验工具:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 

上代码前发下效验工具,在后台打印下jsapi_ticket、noncestr、timestamp、url

  1. 控制层
    WXLoginController:
    @Controller
    @RequestMapping("/wx")
    public class WXLoginController {
        private static final Logger logger = Logger.getLogger(WXLoginController.class);
    
        @RequestMapping("/sgture.do")
        @ResponseBody
        public Map<String, Object> sgture(HttpServletRequest request) {
            String strUrl=request.getParameter("url");
            WinXinEntity wx = WeiXinUnitl.getWinXinEntity(strUrl);
            // 将wx的信息到给页面
            Map<String, Object> map = new HashMap<String, Object>();
            String sgture = WXUnitl.getSignature(wx.getTicket(), wx.getNoncestr(), wx.getTimestamp(), strUrl);
            map.put("sgture", sgture.trim());//签名
            map.put("timestamp", wx.getTimestamp().trim());//时间戳
            map.put("noncestr",  wx.getNoncestr().trim());//随即串
            map.put("appid","appID");//你的公众号APPID
            return map;
        }
    }
    

    2.获取token 获取ticket 工具类(WeiXinUnitl)

    package com.dny_inside.util;
    
    import com.dny_inside.entity.WinXinEntity;
    import net.sf.json.JSONObject;
    
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.Map;
    
    
    public class WeiXinUnitl {
        public static WinXinEntity getWinXinEntity(String url) {
            WinXinEntity wx = new WinXinEntity();
            String access_token = getAccessToken();
            String ticket = getTicket(access_token);
            Map<String, String> ret = Sign.sign(ticket, url);
            wx.setTicket(ret.get("jsapi_ticket"));
            wx.setSignature(ret.get("signature"));
            wx.setNoncestr(ret.get("nonceStr"));
            wx.setTimestamp(ret.get("timestamp"));
            return wx;
        }
    
        // 获取token
        private static String getAccessToken() {
            String access_token = "";
            String grant_type = "client_credential";//获取access_token填写client_credential
            String AppId="你的AppId";//第三方用户唯一凭证
            String secret="你的secret";//第三方用户唯一凭证密钥,即appsecret
            //这个url链接地址和参数皆不能变
    
            String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type="+grant_type+"&appid="+AppId+"&secret="+secret;  //访问链接
    
            try {
                URL urlGet = new URL(url);
                HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
                http.setRequestMethod("GET"); // 必须是get方式请求
                http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
                http.setDoOutput(true);
                http.setDoInput(true);
                http.connect();
                InputStream is = http.getInputStream();
                int size = is.available();
                byte[] jsonBytes = new byte[size];
                is.read(jsonBytes);
                String message = new String(jsonBytes);
                JSONObject demoJson = JSONObject.fromObject(message);
                access_token = demoJson.getString("access_token");
                is.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("access_token:"+access_token);
            return access_token;
            //return "24_gcxt36ZpYuFN7B-kGzNFe3HseuYbVIDZpcQhBc6r29sA3fQ6C7Gff14iY-CFQKjHczlCh3ElJvI_NhI0BJtsJM6EG577dN8U61x1GuOjlHviiLoll9nQQhTRy28ySRDwv4bB0G_s0vQ26VaQTLKbADAZRQ";
        }
    
        // 获取ticket
        private static String getTicket(String access_token) {
            String ticket = null;
            String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi";// 这个url链接和参数不能变
            try {
                URL urlGet = new URL(url);
                HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
                http.setRequestMethod("GET"); // 必须是get方式请求
                http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                http.setDoOutput(true);
                http.setDoInput(true);
                System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
                System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
                http.connect();
                InputStream is = http.getInputStream();
                int size = is.available();
                byte[] jsonBytes = new byte[size];
                is.read(jsonBytes);
                String message = new String(jsonBytes, "UTF-8");
                JSONObject demoJson = JSONObject.fromObject(message);
                ticket = demoJson.getString("ticket");
                is.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("ticket:"+ticket);
            return ticket;
        }
    }
    

    3.工具类Sign

    package com.dny_inside.util;
    
    
    import java.util.UUID;
    import java.util.Map;
    import java.util.HashMap;
    import java.util.Formatter;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.io.UnsupportedEncodingException;
    
    public class Sign {
        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:"+string1);
    
            try {
                MessageDigest crypt = MessageDigest.getInstance("SHA-1");
                crypt.reset();
                crypt.update(string1.getBytes("UTF-8"));
                signature=	WXUnitl.getSignature(jsapi_ticket, nonce_str, timestamp, url);
            } 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;
        }
    
        // 生成nonce_str
        private static String create_nonce_str() {
            return UUID.randomUUID().toString();
        }
    
        // 生成timestamp
        private static String create_timestamp() {
            return Long.toString(System.currentTimeMillis() / 1000);
        }
    }
    

    4.实体类

    package com.dny_inside.entity;
    
    public class WinXinEntity {
        private String access_token;
        private String ticket;
        private String noncestr;
        private String timestamp;
        private String str;
        private String signature;
    
        public String getAccess_token() {
            return access_token;
        }
    
        public void setAccess_token(String access_token) {
            this.access_token = access_token;
        }
    
        public String getTicket() {
            return ticket;
        }
    
        public void setTicket(String ticket) {
            this.ticket = ticket;
        }
    
        public String getNoncestr() {
            return noncestr;
        }
    
        public void setNoncestr(String noncestr) {
            this.noncestr = noncestr;
        }
    
        public String getTimestamp() {
            return timestamp;
        }
    
        public void setTimestamp(String timestamp) {
            this.timestamp = timestamp;
        }
    
        public String getStr() {
            return str;
        }
    
        public void setStr(String str) {
            this.str = str;
        }
    
        public String getSignature() {
            return signature;
        }
    
        public void setSignature(String signature) {
            this.signature = signature;
        }
    }
    

    5.签名校验工具类

    package com.dny_inside.util;
    
    
    import java.security.MessageDigest;
    import java.util.Arrays;
    import java.util.Date;
    
    /**
     * 签名校验工具类
     * @author xiaodong
     *
     */
    public class SignUtil {
        //校验签名的token 事先与app约定
        private static String token="...";
    
        /**
         * 校验签名
         * @param signature 微信加密签名
         * @param timestamp 时间戳
         * @param nonce 随机数
         * @return
         */
        public static boolean checkSignature(String signature,String timestamp,String nonce){
            if(signature==null || timestamp == null || nonce == null){
                return false;
            }
            //对token,timestamp nonce 按字典排序
            String[] paramArr=new String[]{token,timestamp,nonce};
            Arrays.sort(paramArr);
    
            //将排序后的结果拼接成一个字符串
            String content=paramArr[0].concat(paramArr[1]).concat(paramArr[2]);
            String ciphertext=null;
    
            try {
                MessageDigest md=MessageDigest.getInstance("SHA-1");
                //对拼接后的字符串进行sha1加密
                byte[] digest=md.digest(content.toString().getBytes());
                ciphertext=byteToStr(digest);
            } catch (Exception e) {
                // TODO: handle exception
            }
    
            //将sha1加密后的字符串与signature进行对比
            return ciphertext!=null?ciphertext.equals(signature.toUpperCase()):false;
        }
    
        /**
         * 生成签名 android使用
         * @param timestamp 时间戳
         * @param nonce 随机数
         * @return
         */
        public static String getSignature(String timestamp,String nonce){
            //对token,timestamp nonce 按字典排序
            String[] paramArr=new String[]{token,timestamp,nonce};
            Arrays.sort(paramArr);
    
            //将排序后的结果拼接成一个字符串
            String content=paramArr[0].concat(paramArr[1]).concat(paramArr[2]);
            String ciphertext=null;
    
            try {
                MessageDigest md=MessageDigest.getInstance("SHA-1");
                //对拼接后的字符串进行sha1加密 update// 使用指定的字节数组对摘要进行最后更新
                byte[] digest=md.digest(content.toString().getBytes());//完成摘要计算
                ciphertext=byteToStr(digest);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //将sha1加密后的字符串与signature进行对比
            return ciphertext;
        }
    
    
        /**
         * 将字节数组转换成十六进制字符串
         * @param byteArray
         * @return
         */
        private static String byteToStr(byte[] byteArray){
            String strDigest="";
            for (int i = 0; i < byteArray.length; i++) {
                strDigest+=byteToHexStr(byteArray[i]);
            }
            return strDigest;
        }
    
        private static String byteToHexStr(byte mByte){
            char[] Digit={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
            char[] tempArr=new char[2];
            tempArr[0]=Digit[(mByte >>> 4) & 0X0F];
            tempArr[1]=Digit[mByte & 0X0F];
    
            String s=new String(tempArr);
            return s;
        }
    
        /**
         *  生成随机数
         */
        /*public static String generateVerificationCode() {
            return RandomStringUtils.random(2, "123456789");
        }*/
    
    
        /**
         * 当前时间
         * 获取精确到秒的时间戳
         * @return
         */
        public static String getSecondTimestamp(){
            String timestamp = String.valueOf(new Date().getTime()/1000);
            return timestamp;
        }
    
    }
    

    4.WXUnitl 工具类

    package com.dny_inside.util;
    
    
    import java.io.UnsupportedEncodingException;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Formatter;
    
    public class WXUnitl {
        public static String getSignature(String jsapi_ticket, String nonce_str, String timestamp, String url) {
            // 注意这里参数名必须全部小写,且必须有序
            String string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "&timestamp=" + timestamp + "&url="
                    + url;
    
            String signature = "";
            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();
            }
            return signature;
        }
    
        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;
        }
    }
    

    4.页面代码(页面代码我遇到的坑最多,主要是比较难懂,自己也不够细心)

坑一.调用控制层接口时传的url需要特别注意。

确认url是页面完整的url(请在当前页面alert(location.href.split('#')[0])确认),包括'http(s)://'部分,以及'?'后面的GET参数部分,但不包括'#'hash后面的部分。

这个是重点:
确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用location.href.split('#')[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。

特别的说下我遇到坑;线下测试用的是下面的页面代码 完全没问题。但是上线测试的时候出问题了 一直报错config:invalid signature。最后查了好久发现是用来签名的url用问题。因为我们做的前后端分离项目,前端代码使用vue.js写的前端加了encodeURIComponent 导致url中的符号变成乱码了 导致获取的js签名出错了。最后不加就ok了。到底加不加还是要在后台打印下url看下是否有乱码。

坑二.官方文档:请注意,原有的 wx.onMenuShareTimeline、wx.onMenuShareAppMessage、wx.onMenuShareQQ、wx.onMenuShareQZone 接口,即将废弃。请尽快迁移使用客户端6.7.2及JSSDK 1.4.0以上版本支持的 wx.updateAppMessageShareData、updateTimelineShareData 接口。

我用的是JSSDK 1.4.0;实际上我用 updateAppMessageShareData、updateTimelineShareData接口并没用成功,最后只有使用wx.onMenuShareTimeline、wx.onMenuShareAppMessage。所以这里最后自己两个都试试。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
</head>
<body>
<div>
    <h1>微信分享</h1>
</div>
</body>
<script src="js/jquery-1.9.1.min.js"></script>
<script src="js/swiper.min.js"></script>
<script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
<!-- 分享配置 -->
<script>
    $(function () {
        getSgt(window.location.href.split('#')[0]);
    })
    //-------------begin
    //设置分享内容
    var title="分享标题";
    var desc="分享内容";
    var indexHref = window.location.href;
    var shareLink=indexHref.toString();//分享链接
    var  shareImgUrl="http://localhost:8080/Inside/image/meet-home/other/03e73ad6-2de1-45ed-a1d8-8083947ee8e5.JPG";//分享图片
    //-------------end
    function getSgt(currUrl) {
        $.ajax({
            type: "POST",
            url: "http://localhost:8080/Inside/wx/sgture.do?url="+encodeURIComponent(window.location.href.split('#')[0]),//注意此处有坑,
            dataType: "json",
            success: function (response) {
                initWeChat(response.sgture,response.appid,response.timestamp,response.noncestr);
                wx.ready(function() {
                    console.log(title,desc,shareLink,shareImgUrl);
                    //分享到---朋友圈,微信好友
                    wx.onMenuShareAppMessage({
                        title: title,
                        desc: desc,
                        link: shareLink,
                        imgUrl: shareImgUrl,
                        trigger: function (res) {
                            console.log(title,desc,shareLink,shareImgUrl);
                            // 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
                            alert('用户点击发送给朋友');
                        },
                        success: function (res) {
                            alert('已分享');
                        },
                        cancel: function (res) {
                            alert('已取消');
                        },
                        fail: function (res) {
                            alert(JSON.stringify(res));
                        }
                    });
                    wx.onMenuShareTimeline({
                        title: title, // 分享标题
                        link: shareLink,// 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                        imgUrl: shareImgUrl,// 分享图标
                        desc: desc,
                        trigger: function (res) {
                            // 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
                            alert('用户点击发送给朋友');
                        },
                        success: function (res) {
                            alert('已分享');
                        },
                        cancel: function (res) {
                            alert('已取消');
                        },
                        fail: function (res) {
                            alert(JSON.stringify(res));
                        }
                    });

                });
            }
        });
    }


    function initWeChat(signature, appId,timestamp,noncestr) {
        wx.config({
            debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
            appId: appId, // 必填,公众号的唯一标识
            timestamp: timestamp, // 必填,生成签名的时间戳
            nonceStr: noncestr, // 必填,生成签名的随机串
            signature:signature,//必填,签名
            jsApiList: [// 必填,需要使用的JS接口列表,所有JS接口列表见附录2
                'onMenuShareAppMessage',//-----------------我们这里用了分享朋友圈
                'onMenuShareTimeline',//----------------好友
            ]
        });
    }
    //window.location.href="http://localhost:8111";
    //console.log(decodeURIComponent(window.document.URL))
</script>

</html>

二.正式公众号配置问题

     好记性不如烂笔头,赘述下。

     1.设置服务器白名单 开发-->基本配置-->公众号开发信息-->IP白名单。我用的阿里云服务器

     2.JS接口安全域名 开发-->接口权限-->网页服务-->页面授权-->功能设置-->JS接口安全域名。

最后展示下成功效果: 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值