Java微信开发——分享功能的实现(Spring boot框架)

            问题与需求:h5页面的内容在微信分享后分享的内容无法定制,要自定义分享内容为该h5页面的标题,内容,图片等。

            实现与排错:由于本人技术有限,基本都是参照网络上大神们提供的源码,在部分地方做了简单修改。感谢大神们的无私奉献!主要参考链接和实现代码如下:

                                    java开发微信分享到朋友圈功能  http://www.jb51.net/article/88690.htm
                                    微信公众平台开发:JS-SDK之分享功能整理(Java)http://blog.csdn.net/dcb_ripple/article/details/52066708

                                    实现】步骤一:参考微信公众平台的开发者文档https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html了解开发步骤

                                                步骤二:为了描述方便,我先把h5页面的js代码贴出,再描述其实现所依赖的java代码。

1.1h5页面的js代码

       h5页面:(获取该页面中的title-标题 discuss-内容 传入wx.config中实现分享内容的定制)                                  
      



       ps.由于我的页面用了angularjs,在js中如果通过document.getElementById拿值总是为空所以在js中再次使用了ajax向后台取值!!很影响性能的做法,在js中如何拿到angularjs渲染后的值呢?还希望有知道的朋友指点一下。
       weshare.js代码:
      

//获取签名
$.ajax({
    type: "GET",
    url: "wshare/getSignature",
    data:{url:url},
    success: function(data){
        console.log("success");
        var objData=JSON.parse(data);
        timestamp=objData.timestamp;
        noncestr=objData.noncestr;
        signature=objData.signature;
        console.log(objData);
        wxShare();
    },
    statusCode: {404: function(){
        alert('page not found');
    }}
});
function wxShare(){
    wx.config({
        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: 'wxca57e6589ccebff0', // 和获取Ticke的必须一样------必填,公众号的唯一标识
        timestamp:timestamp, // 必填,生成签名的时间戳
        nonceStr: noncestr, // 必填,生成签名的随机串
        signature: signature,// 必填,签名,见附录1
        jsApiList: [
            'onMenuShareAppMessage','onMenuShareTimeline','onMenuShareTimeline','onMenuShareQQ','onMenuShareQZone'
        ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    });
}
wx.ready(function(){
    //config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,
    //config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关
    //接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。

    //----------“分享给朋友”
    wx.onMenuShareAppMessage({
        title:title, // 分享标题
        desc:discuss, // 分享描述
        link: url, // 分享链接
        imgUrl: shareImgUrl, // 分享图标
        success: function () {
            // 用户确认分享后执行的回调函数、
        },
        cancel: function () {
            // 用户取消分享后执行的回调函数
        }
    });
    //------------"分享到朋友圈"
    wx.onMenuShareTimeline({
        title: title, // 分享标题
        desc: discuss, // 分享描述
        link: url, // 分享链接
        imgUrl: shareImgUrl, // 分享图标
        type: '', // 分享类型,music、video或link,不填默认为link
        dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
        success: function () {
            // 用户确认分享后执行的回调函数、
        },
        cancel: function () {
            // 用户取消分享后执行的回调函数
        }
    });
    //-------------分享到QQ
    wx.onMenuShareQQ({
        title: title, // 分享标题
        desc: discuss, // 分享描述
        link: url, // 分享链接
        imgUrl: shareImgUrl, // 分享图标
        type: '', // 分享类型,music、video或link,不填默认为link
        dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
        success: function () {
            // 用户确认分享后执行的回调函数、
        },
        cancel: function () {
            // 用户取消分享后执行的回调函数
        }
    });
    //-------------分享到QQ空间
    wx.onMenuShareQZone({
        title: title, // 分享标题
        desc: discuss, // 分享描述
        link: url, // 分享链接
        imgUrl: shareImgUrl, // 分享图标
        type: '', // 分享类型,music、video或link,不填默认为link
        dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
        success: function () {
            // 用户确认分享后执行的回调函数、
        },
        cancel: function () {
            // 用户取消分享后执行的回调函数
        }
    });

});
wx.error(function(res){
    // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
    console.log(res);
});



1.2java后台实现代码
      1.2.1 weixin.controller
      
package com.weixin.controller;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.weixin.util.WeixinUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.google.gson.Gson;
import com.weixin.model.Ticket;
import com.weixin.util.GetRandomStr;
import com.weixin.util.SignatureBean;



/**
 * Created by Administrator on 2017/1/9.
 */
@Controller
@RequestMapping("/wshare")
public class WeixinshareController {

        private static Ticket aticket = null;

        @RequestMapping("/getSignature")
        public String getSignature( HttpServletRequest request,
                                    HttpServletResponse response) throws IOException, ParseException{
            //获取签名页面链接
            String url = request.getParameter("url");
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //获取标签,并检查标签是否过期
            if(aticket==null){//第一次访问,标签不存在。
                aticket = executeTicket(response,"1",url,format);
                return null;
            }else{//标签存在,判断标签是否超时
                String oldAcquiretime = aticket.getAcquiretime();
                long difference=format.parse(format.format(new Date())).getTime()-format.parse(oldAcquiretime).getTime();
                if(difference>7100000){//标签超时,重新到微信服务器请求标签超时时间为7200秒(7200000毫秒)
                    aticket = executeTicket(response,"2",url,format);
                    return null;
                }else{//标签未超时
                    /**
                     * 注意事项
                     * 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
                     * 2.签名用的url必须是调用JS接口页面的完整URL。
                     * 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。
                     *
                     ****根据第1点要求   signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端***
                     */
                    System.out.print("ticket:"+aticket.getTicket());
                    String signature = signature(aticket.getTicket(),aticket.getTimestamp(),aticket.getNoncestr(),url);
                    SignatureBean signatureBean = new SignatureBean();
                    signatureBean.setNoncestr(aticket.getNoncestr());
                    signatureBean.setSignature(signature);
                    signatureBean.setTimestamp(aticket.getTimestamp());
                    signatureBean.setUrl(url);
                    response.setContentType("text/html;charset=UTF-8");
                    response.getWriter().print(new Gson().toJson(signatureBean));
                    return null;
                }
            }


        }
        /**
         更新和获取ticket的方法,因为用的solr所以更新和新增是一样的ID无则添加,有则更新
         */
        public Ticket executeTicket(HttpServletResponse response,String flag,String url,SimpleDateFormat format) throws IOException{

            //获取签名随机字符串
            GetRandomStr randomStr = new GetRandomStr();
            String noncestr = randomStr.getRandomString(15);
            //获取签名时间戳
            String timestamp = Long.toString(System.currentTimeMillis());
            //请求accessToken
            String accessTokenUrl ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wxca57e6589ccebff0&secret=b7d7c5df24644563be32bd84b90c8bf6";
            String tokenJson = WeixinUtil.httpRequest(accessTokenUrl, "GET", null);
            Gson gson = new Gson();
            ShareAccess_Token token = gson.fromJson(tokenJson, ShareAccess_Token.class);
            String to= token.getAccess_token();
            //获取标签
            String urlTicket ="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+to+"&type=jsapi";
           String ticketJson = WeixinUtil.httpRequest(urlTicket, "GET", null);
            Ticket ticket = gson.fromJson(ticketJson, Ticket.class);
            String t = ticket.getTicket();
            //String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
            //我的Ticket ID是写死的 20160114wiimediamrylsong1152
            String acquiretime = format.format(new Date());
            ticket.setTid("20160114wiimediamrylsong1152");
            ticket.setAcquiretime(acquiretime);
            ticket.setTimestamp(timestamp);
            ticket.setNoncestr(noncestr);

            /**
             * 注意事项
             * 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
             * 2.签名用的url必须是调用JS接口页面的完整URL。
             * 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。
             *
             *根据第1点要求   signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端*
             */

            String signature = signature(t,timestamp,noncestr,url);
            System.out.print("ticket: "+t);
            SignatureBean signatureBean = new SignatureBean();
            signatureBean.setNoncestr(noncestr);
            signatureBean.setSignature(signature);
            signatureBean.setTimestamp(timestamp);
            signatureBean.setUrl(url);
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().print(new Gson().toJson(signatureBean));

            return ticket;
        }

        /**
         *
         *

Project:mryl_phone_v2

* *

:mryl_phone_v2

* *

Description:根据标签,时间戳,密匙,URL进行签名

* *

Company:Wiimedia

* *@Athor:SongJia * *@Date:2016-7-15 上午09:37:13 * */ private String signature(String jsapi_ticket, String timestamp, String noncestr, String url) { jsapi_ticket = "jsapi_ticket=" + jsapi_ticket; timestamp = "timestamp=" + timestamp; noncestr = "noncestr=" + noncestr; url = "url=" + url; String[] arr = new String[] { jsapi_ticket, timestamp, noncestr, url }; // 将token、timestamp、nonce,url参数进行字典序排序 Arrays.sort(arr); StringBuilder content = new StringBuilder(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); if (i != arr.length - 1) { content.append("&"); } } MessageDigest md = null; String tmpStr = null; try { md = MessageDigest.getInstance("SHA-1"); // 将三个参数字符串拼接成一个字符串进行sha1加密 byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } content = null; return tmpStr; } /** * 将字节转换为十六进制字符串 * * @param mByte * @return */ 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; } /** * 将字节数组转换为十六进制字符串 * * @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; } class ShareAccess_Token{ private String access_token; private String expires_in; public String getAccess_token() { return access_token; } public void setAccess_token(String accessToken) { access_token = accessToken; } public String getExpires_in() { return expires_in; } public void setExpires_in(String expiresIn) { expires_in = expiresIn; } } }
       原大神的实现中是将Ticket存入数据库中,但是考虑到TicketId已写死,Ticket只有一条数据,存入数据库,再从数据库中取值 新等过于麻烦,故将Ticket作为static变量。第一次访问时创建,超时时给他赋新值。
      1.2.2weixin.util
      {CSDN:CODE:2126562
     
package com.weixin.util;

/**
 * Created by Administrator on 2017/1/9.
 */
public class SignatureBean {
    private String noncestr;
    private String url;
    private String timestamp;
    private String signature;
    public String getNoncestr() {
        return noncestr;
    }
    public void setNoncestr(String noncestr) {
        this.noncestr = noncestr;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }
    public String getSignature() {
        return signature;
    }
    public void setSignature(String signature) {
        this.signature = signature;
    }

}

     
package com.weixin.util;

import java.util.Random;

/**
 * Created by Administrator on 2017/1/9.
 * 获取随机字符串
 */

public class GetRandomStr {
    public String getRandomString(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}
  
     
package com.weixin.util;

import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * Created by Administrator on 2017/1/10.
 */
public class MyX509TrustManager implements X509TrustManager {
    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

    }

    @Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }
}

      ps.因为大神的注释写的很清楚,拉入我的项目中是可用滴,当然关于这个MyX509TrustManager我也不懂,百度之后还是不知道应该信任哪些证书,然后,干脆什么都没改,信任全部。当然网上也有很多不是通过Https请求的,然而根据微信现在的规定,好像都需要Https请求了。对于这个MyX509TrustManager大家有什么好的实现欢迎指教。
      1.2.3weixin.model     
     
package com.weixin.model;

/**
 * Created by Administrator on 2017/1/9.
 */
public class Ticket {
    private String tid;
    private String ticket;
    private String errcode;
    private String errmsg;
    private String expires_in;
    private String acquiretime;
    private String noncestr;
    private String timestamp;
    public Ticket(String tid, String ticket, String errcode, String errmsg,
                  String expiresIn, String acquiretime, String noncestr,
                  String timestamp) {
        super();
        this.tid = tid;
        this.ticket = ticket;
        this.errcode = errcode;
        this.errmsg = errmsg;
        expires_in = expiresIn;
        this.acquiretime = acquiretime;
        this.noncestr = noncestr;
        this.timestamp = timestamp;
    }
    public String getTid() {
        return tid;
    }
    public void setTid(String tid) {
        this.tid = tid;
    }
    public String getTicket() {
        return ticket;
    }
    public void setTicket(String ticket) {
        this.ticket = ticket;
    }
    public String getErrcode() {
        return errcode;
    }
    public void setErrcode(String errcode) {
        this.errcode = errcode;
    }
    public String getErrmsg() {
        return errmsg;
    }
    public void setErrmsg(String errmsg) {
        this.errmsg = errmsg;
    }
    public String getExpires_in() {
        return expires_in;
    }
    public void setExpires_in(String expiresIn) {
        expires_in = expiresIn;
    }
    public String getAcquiretime() {
        return acquiretime;
    }
    public void setAcquiretime(String acquiretime) {
        this.acquiretime = acquiretime;
    }
    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;
    }
}

      最后,由于是在项目下新开的包,别忘了在Application.java中加入
@ComponentScan(basePackages = {"com.mzqadmin","com.baidu","com.weixin"})
      另外,调试时把debug:true如果debug:ok却始终无法完成定制,一定是你的js代码有问题,后台验证已经通过了,看看你的js代码里参数是否带“”什么的。别问我怎么知道的,低级错误害死人呐~
      以上,是我做微信分享功能参考和实现的所有代码,有很多不足之处,还希望各位看官不吝赐教。
     
   
            




      
      
                        

                                                

                                                
  


                                        

                                                        

                                   

                                 

你可能感兴趣的:(Java微信开发——分享功能的实现(Spring boot框架))