微信实现分享转发功能

来源:互联网 发布:成都买车位划算 知乎 编辑:程序博客网 时间:2024/06/06 15:49

根据开发文档,首先看一下前端需要什么数据:

var currentUrl  = window.location.href;//微信分享功能实现$.ajax({//调取接口返回验证码    type:"get",    url:"http://域名/接口名",    data:{'url':currentUrl},    success:function(res){        console.log(res)        if(res.success==true){            //分享链接            var imgUrl = '图片地址';            var lineLink = 'http://域名/文件名/';            var descContent = "分享展示内容";            var shareTitle = '分享标题';            wx.config({                debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。                appId: 'wxdbcbd7962d016d0b', // 必填,公众号的唯一标识                timestamp: res.data.timestamp, // 必填,生成签名的时间戳                nonceStr: res.data.nonceStr, // 必填,生成签名的随机串                signature: res.data.signature,// 必填,签名,见附录1                jsApiList: [                    'checkJsApi',                    'onMenuShareAppMessage',                    'onMenuShareTimeline',                    'onMenuShareQQ'                ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2            });            wx.ready(function(){                // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。                wx.checkJsApi({                    jsApiList: ['onMenuShareAppMessage','onMenuShareTimeline','onMenuShareQQ'], // 需要检测的JS接口列表,所有JS接口列表见附录2,                    success: function(res) {                        // console.log(res);                        // 以键值对的形式返回,可用的api值true,不可用为false                        // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}                    }                });                wx.onMenuShareAppMessage({                    title: shareTitle, // 分享标题                    desc: descContent, // 分享描述                    link: lineLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致                    imgUrl: imgUrl, // 分享图标                    type: '', // 分享类型,music、video或link,不填默认为link                    dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空                    success: function () {                        // 用户确认分享后执行的回调函数                        //alert('success')                    },                    cancel: function () {                        // 用户取消分享后执行的回调函数                        // alert('false');                    }                });                wx.onMenuShareTimeline({                    title: shareTitle, // 分享标题                    link: lineLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致                    imgUrl: imgUrl, // 分享图标                    success: function () {                        // 用户确认分享后执行的回调函数                        // alert('success')                    },                    cancel: function () {                        // 用户取消分享后执行的回调函数                        // alert('false');                    }                });                wx.onMenuShareQQ({                    title: shareTitle, // 分享标题                    desc: descContent, // 分享描述                    link: lineLink, // 分享链接                    imgUrl: imgUrl, // 分享图标                    success: function () {                        // 用户确认分享后执行的回调函数                    },                    cancel: function () {                        // 用户取消分享后执行的回调函数                    }                });            });            wx.error(function(res){                // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。                //alert(res);                return;            });        }        else {           // alert(res.message)            return        }    },    error:function(){        console.log("error");    }});

通过上面的js可以看到后台需要提供三个参数
一、timestamp
根据文档得知,要想得到timestamp需要得到jsapi_ticket,而要想得到jsapi_ticket需要得到aaccess_token。
1、获取access_token
access_token有效期为7200秒,重复获取将导致上次获取的access_token失效,所以问题是如何定时获取并且把得到的数据放在本地。
1)新建一个AccessToken实体类

public class AccessToken {    private String  accessToken;    private int expiresin;    public String getAccessToken() {        return accessToken;    }    public void setAccessToken(String accessToken) {        this.accessToken = accessToken;    }    public int getExpiresin() {        return expiresin;    }    public void setExpiresin(int expiresin) {        this.expiresin = expiresin;    }}

2)写一个servlet定时获取access_token,并配置在web.xml中
AccessTokenServlet:

@WebServlet(name = "accessTokenServlet")public class AccessTokenServlet extends HttpServlet{    private static final long serialVersionUID = 1L;    private static Logger log = LoggerFactory.getLogger(WxCommonUtil.class);    @Override    public void init() throws ServletException {        TokenThread.appId = getInitParameter("appid");  //获取servlet初始参数appid和appsecret        TokenThread.appSecret = getInitParameter("appsecret");        log.info("weixin api appid:{}", TokenThread.appId);        log.info("weixin api appsecret:{}", TokenThread.appSecret);        if ("".equals(TokenThread.appId) || "".equals(TokenThread.appSecret)) {            log.error("appid and appsecret configuration error, please check carefully.");        } else {            // 启动定时获取access_token的线程            new Thread(new TokenThread()).start();        }    }    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {    }}

web.xml配置

<servlet>    <servlet-name>accessTokenServlet</servlet-name>    <servlet-class>com.loan.servlet.AccessTokenServlet</servlet-class>    <init-param>      <param-name>appid</param-name>      <param-value>你的appid</param-value>    </init-param>    <init-param>      <param-name>appsecret</param-name>      <param-value>你的appsecret</param-value>    </init-param>    <load-on-startup>0</load-on-startup>  </servlet>

3)TokenThread类

public class TokenThread implements Runnable {    private static Logger log = LoggerFactory.getLogger(TokenThread.class);    public static String appId = "";    public static String appSecret= "";    public static AccessToken accessToken = null;    @Override    public void run() {        while (true) {            try {                accessToken = WxCommonUtil.getToken(appId,appSecret);                if (null != accessToken) {                    System.out.println(accessToken.getAccessToken());                    Thread.sleep(7000 * 1000); //获取到access_token 休眠7000秒                } else {                    Thread.sleep(1000 * 3); //获取的access_token为空 休眠3秒                }            } catch (Exception e) {                System.out.println("发生异常:" + e.getMessage());                e.printStackTrace();                try {                    Thread.sleep(1000 * 10); //发生异常休眠1秒                } catch (Exception e1) {                }            }        }    }}

4)WxCommonUtil工具类

public class WxCommonUtil {    private static Logger log = LoggerFactory.getLogger(WxCommonUtil.class);    public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=你的appid&secret=你的secret";    public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {        JSONObject jsonObject = null;        try {            // 创建SSLContext对象,并使用我们指定的信任管理器初始化            TrustManager[] tm = { new MyX509TrustManager() };            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");            sslContext.init(null, tm, new java.security.SecureRandom());            // 从上述SSLContext对象中得到SSLSocketFactory对象            SSLSocketFactory ssf = sslContext.getSocketFactory();            URL url = new URL(requestUrl);            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();            conn.setSSLSocketFactory(ssf);            conn.setDoOutput(true);            conn.setDoInput(true);            conn.setUseCaches(false);            // 设置请求方式(GET/POST)            conn.setRequestMethod(requestMethod);            // 当outputStr不为null时向输出流写数据            if (null != outputStr) {                OutputStream outputStream = conn.getOutputStream();                // 注意编码格式                outputStream.write(outputStr.getBytes("UTF-8"));                outputStream.close();            }            // 从输入流读取返回内容            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();            inputStream = null;            conn.disconnect();            jsonObject = JSONObject.fromObject(buffer.toString());        } catch (ConnectException ce) {            log.error("连接超时:{}", ce);        } catch (Exception e) {            log.error("https请求异常:{}", e);        }        return jsonObject;    }

这样accessToken可以通过TokenThread.accessToken.getAccessToken()获取。
2、获取jsapi_ticket

/**     * 获取jsapi_ticket     *     * @param access_token     * @return     */    public 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);            System.out.println("JSON字符串:"+demoJson);            ticket = demoJson.getString("ticket");            is.close();        } catch (Exception e) {            e.printStackTrace();        }        return ticket;    }

3、获取url

//这里URL要从前端动态获取,不能写死 String url=request.getParameter("url");

4、拼接字符串并进行sha1加密

String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"&timestamp="+timestamp+"&url="+url;
/**     * sha1的加密算法     *     * @param decript     * @return     */    public static String SHA1(String decript) {        String signature="";        try        {            MessageDigest crypt = MessageDigest.getInstance("SHA-1");            crypt.reset();            crypt.update(decript.getBytes("UTF-8"));            signature = byteToHex(crypt.digest());            return signature;        }        catch (NoSuchAlgorithmException e)        {            e.printStackTrace();        }        catch (UnsupportedEncodingException e)        {            e.printStackTrace();        }        return "";    }    /**     * 进制转换     * @param hash     * @return     */    public 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;    }

这样一系列步骤之后就可以得到signature。
二、timestamp

String timestamp = String.valueOf(System.currentTimeMillis() / 1000);

三、nonceStr

String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);

这样就能得到三个参数啦。
最后是总的controller:

@RequestMapping(value = "/sign",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})    @ResponseBody    public ObjectOutput sign(HttpServletRequest request, HttpServletResponse response, HttpSession session){        Hashtable hashtable = new Hashtable();        //1、获取accessToken        String accessToken= TokenThread.accessToken.getAccessToken();        //2、获取Ticket        String jsapi_ticket=WxCommonUtil.getTicket(accessToken);        //3、时间戳和随机字符串        String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);        //4、获取url        String url=request.getParameter("url");        //5、讲参数排序并拼接字符串        String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"&timestamp="+timestamp+"&url="+url;        //6、将字符串进行sha1加密        String signature =WxCommonUtil.SHA1(str);        hashtable.put("signature",signature);        hashtable.put("timestamp",timestamp);        hashtable.put("nonceStr",noncestr);        hashtable.put("url",url);        output = Format.output(true, Constant.Success, hashtable, "");        return output;    }