天天看点

微信实现分享转发功能

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

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 = L;
    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( * ); //获取到access_token 休眠7000秒

                } else {
                    Thread.sleep( * ); //获取的access_token为空 休眠3秒
                }
            } catch (Exception e) {
                System.out.println("发生异常:" + e.getMessage());
                e.printStackTrace();
                try {
                    Thread.sleep( * ); //发生异常休眠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() / );
           

三、nonceStr

这样就能得到三个参数啦。

最后是总的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();
        //、获取accessToken
        String accessToken= TokenThread.accessToken.getAccessToken();
        //、获取Ticket
        String jsapi_ticket=WxCommonUtil.getTicket(accessToken);
        //、时间戳和随机字符串
        String noncestr = UUID.randomUUID().toString().replace("-", "").substring(, );
        String timestamp = String.valueOf(System.currentTimeMillis() / );
        //、获取url
        String url=request.getParameter("url");
        //、讲参数排序并拼接字符串
        String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"&timestamp="+timestamp+"&url="+url;
        //、将字符串进行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;
    }
           

继续阅读