zoukankan      html  css  js  c++  java
  • 微信分享功能开发

    一、服务器端程序

    package com.wiimedia.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 org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
     
    import com.google.gson.Gson;
    import com.wiimedia.model.Ticket;
    import com.wiimedia.service.ArticleSolrService;
    import com.wiimedia.service.TicketRepository;
    import com.wiimedia.service.TicketRepositorySolr;
    import com.wiimedia.utils.GetRandomStr;
    import com.wiimedia.utils.SignatureBean;
    import com.wiimedia.utils.weixin.WeixinUtil;
    /**
     *
     *
     *<p>Project:mryl_phone_v2</p>
     *
     *<p>Package:com.wiimedia.controller</p>
     *
     *<p>Description:微信分享Controller</p>
     *
     *<p>Company:Wiimedia</p>
     *
     *@Athor:SongJia
     *
     *@Date:2016-7-15 上午09:34:10
     *
     */
     
    @Controller
    @RequestMapping("/WeixinshareController/Api/Inteface")
    public class WeixinshareController {
     @Autowired
     private TicketRepositorySolr ticketRepositorySolr;
     
     @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");
     //从数据库中获取标签,并检查标签是否过期
     Ticket oldticket = ticketRepositorySolr.getTicketById("20160114wiimediamrylsong1152");
     if(oldticket==null){//第一次访问,标签不存在。
      executeTicket(response,"1",url,format);
      return null;
     }else{//标签存在,判断标签是否超时
      String oldAcquiretime = oldticket.getAcquiretime();
      long difference=format.parse(format.format(new Date())).getTime()-format.parse(oldAcquiretime).getTime();
      if(difference>7100000){//标签超时,重新到微信服务器请求标签超时时间为7200秒(7200000毫秒)
      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传给客户端***
       */
      String signature = signature(oldticket.getTicket(),oldticket.getTimestamp(),oldticket.getNoncestr(),url);
      SignatureBean signatureBean = new SignatureBean();
      signatureBean.setNoncestr(oldticket.getNoncestr());
      signatureBean.setSignature(signature);
      signatureBean.setTimestamp(oldticket.getTimestamp());
      signatureBean.setUrl(url);
      response.setContentType("text/html;charset=UTF-8");
      response.getWriter().print(new Gson().toJson(signatureBean));
      return null;
      }
     }
     
     
     }
     /**
     *
     *<p>Project:mryl_phone_v2</p>
     *
     *<p>:mryl_phone_v2</p>
     *
     *<p>Description:更新和获取ticket的方法,因为用的solr所以更新和新增是一样的ID无则添加,有责更新</p>
     *
     *<p>Company:Wiimedia</p>
     *
     *@Athor:SongJia
     *
     *@Date:2016-7-15 上午09:45:00
     *
     */
     public void 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=您的APPID&secret=您的密匙";
     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 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是写死的
     String acquiretime = format.format(new Date());
     ticket.setTid("20160114wiimediamrylsong1152");
     ticket.setAcquiretime(acquiretime);
     ticket.setTimestamp(timestamp);
     ticket.setNoncestr(noncestr);
     //因为用的SOLR所以更新和添加的方法是一样的,可以根据自己具体需求进行修改,本文不再贴出代码.
     if(flag.equals("2")){
      ticketRepositorySolr.addTicketToSolr(ticket);
     }else{
      ticketRepositorySolr.addTicketToSolr(ticket);
     }
     /**
      * 注意事项     
      * 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
      * 2.签名用的url必须是调用JS接口页面的完整URL。  
      * 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。
      *
      *根据第1点要求  signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端*
      */
     String signature = signature(t,timestamp,noncestr,url);
     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));
     }
     
     /**
     *
     *<p>Project:mryl_phone_v2</p>
     *
     *<p>:mryl_phone_v2</p>
     *
     *<p>Description:根据标签,时间戳,密匙,URL进行签名</p>
     *
     *<p>Company:Wiimedia</p>
     *
     *@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;
     }
     
     }
    }
     
    二、客户端代码.
     
    <script type="text/javascript">
      var url = window.location.href;
      var articleId = "";
      var shareTitle="明日医疗资讯";
      var shareImgUrl="";
      var userinfo = localStorage.getItem("_userinfo");
      var timestamp;
      var noncestr;
      var signature;
      //获取签名
      $.ajax({
       type: "GET",
       url: "WeixinshareController/Api/Inteface/getSignature",
       //data:{timestamp:timestamp,noncestr:noncestr,url:url},
       data:{url:url},
       success: function(data){
        var objData=JSON.parse(data);
        timestamp=objData.timestamp;
        noncestr=objData.noncestr;
        signature=objData.signature;
         console.log(objData);
         wxShare();
       }
       });
      function wxShare(){
      wx.config({
      debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
      appId: '您的appid', // 和获取Ticke的必须一样------必填,公众号的唯一标识
      timestamp:timestamp, // 必填,生成签名的时间戳
      nonceStr: noncestr, // 必填,生成签名的随机串
      signature: signature,// 必填,签名,见附录1
      jsApiList: [
      'onMenuShareAppMessage'
      ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
      });
      }
      wx.ready(function(){
       //config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,
       //config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关
       //接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
     
      //----------“分享给朋友”
      wx.onMenuShareAppMessage({
       title: "明日医疗资讯", // 分享标题
       desc: shareTitle, // 分享描述
       link: url, // 分享链接
       imgUrl: shareImgUrl, // 分享图标
       type: '', // 分享类型,music、video或link,不填默认为link
       dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
       success: function () {
       // 用户确认分享后执行的回调函数、
       },
       cancel: function () {
       // 用户取消分享后执行的回调函数
       }
      });
      //------------"分享到朋友圈"
      wx.onMenuShareTimeline({
       title: '明日医疗资讯', // 分享标题
       link: '', // 分享链接
       imgUrl: shareImgUrl, // 分享图标
       success: function () {
       // 用户确认分享后执行的回调函数
       },
       cancel: function () {
       // 用户取消分享后执行的回调函数
       }
      });
      //-------------分享到QQ
      wx.onMenuShareQQ({
       title: '明日医疗资讯', // 分享标题
       desc: shareTitle, // 分享描述
       link: '', // 分享链接
       imgUrl: shareImgUrl, // 分享图标
       success: function () {
       // 用户确认分享后执行的回调函数
       },
       cancel: function () {
       // 用户取消分享后执行的回调函数
       }
      });
      //-------------分享到QQ空间
      wx.onMenuShareQZone({
       title: '明日医疗资讯', // 分享标题
       desc: shareTitle, // 分享描述
       link: '', // 分享链接
       imgUrl: shareImgUrl, // 分享图标
       success: function () {
       // 用户确认分享后执行的回调函数
       },
       cancel: function () {
       // 用户取消分享后执行的回调函数
       }
      });
     
      });
    三、服务器需要的工具类和Model
    package com.wiimedia.model;
     
     
    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;
     }
     
     
    }
     
    ② 添加到数据库的业务根据自己需要进行实现. 
    ③ GetRandomStr
     
    package com.wiimedia.utils;
     
    import java.util.Random;
     
    public class GetRandomStr {
     /**
     *
     *<p>Project:mryl_phone_v2</p>
     *
     *<p>:mryl_phone_v2</p>
     *
     *<p>Description:生成随即字符串 </p>
     *
     *<p>Company:Wiimedia</p>
     *
     *@Athor:SongJia
     *
     *@Date:2016-7-14 上午11:14:46
     *
     */
     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();
     }
    }
    ④ SignatureBean
     
    package com.wiimedia.utils;
     
    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;
     }
     
    }
     
    ⑤ WeixinUtil
    package com.wiimedia.utils.weixin;
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.ConnectException;
    import java.net.URL;
     
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocketFactory;
    import javax.net.ssl.TrustManager;
     
    /**
     *
     *<p>Project:mryl_phone_v2</p>
     *
     *<p>:mryl_phone_v2</p>
     *
     *<p>Description:公众平台接口工具类</p>
     *
     *<p>Company:Wiimedia</p>
     *
     *@Athor:SongJia
     *
     *@Date:2016-7-15 上午09:37:13
     *
     */
    public class WeixinUtil {
     
     /**
     * 发起https请求并获取结果
     *
     * @param requestUrl 请求地址
     * @param requestMethod 请求方式(GET、POST)
     * @param outputStr 提交的数据
     * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
     */
     public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
     
     StringBuffer buffer = new StringBuffer();
     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 httpUrlConn = (HttpsURLConnection) url.openConnection();
      httpUrlConn.setSSLSocketFactory(ssf);
     
      httpUrlConn.setDoOutput(true);
      httpUrlConn.setDoInput(true);
      httpUrlConn.setUseCaches(false);
      // 设置请求方式(GET/POST)
      httpUrlConn.setRequestMethod(requestMethod);
     
      if ("GET".equalsIgnoreCase(requestMethod))
      httpUrlConn.connect();
     
      // 当有数据需要提交时
      if (null != outputStr) {
      OutputStream outputStream = httpUrlConn.getOutputStream();
      // 注意编码格式,防止中文乱码
      outputStream.write(outputStr.getBytes("UTF-8"));
      outputStream.close();
      }
     
      // 将返回的输入流转换成字符串
      InputStream inputStream = httpUrlConn.getInputStream();
      InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
      BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
     
      String str = null;
      while ((str = bufferedReader.readLine()) != null) {
      buffer.append(str);
      }
      bufferedReader.close();
      inputStreamReader.close();
      // 释放资源
      inputStream.close();
      inputStream = null;
      httpUrlConn.disconnect();
      return buffer.toString();
     } catch (ConnectException ce) {
      ce.printStackTrace();
     } catch (Exception e) {
      e.printStackTrace();
     }
     return "";
     }
    }

     四、至此,分享功能已经开发完成,但是,在生成signature的时候会遇到很多问题,这里提供一些wx.config失败的排错方法。

    ① 确认自己的生成的signature是否正确 
    在微信提供的http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign进行校验

    ② wx.config中使用的noncestr, timestamp与用以签名中的对应noncestr, timestamp是否一致一致…如上面(一.服务器代码) 
    (有可能因为JS页面加载顺序问题,服务器生成的signature,noncestr,timestamp在wx.config中没有获取到)。

    ③ 确认url是页面完整的url,包括GET参数部分 
    需要去掉#后面的部分

    ④ config 中的 appid 与用来获取 jsapi_ticket 的 appid 是否一致

    ⑤ 报错{errmsg:config:ok}是debug的正常返回把调试模式关掉就OK 
    wx.config debug: false, 

    本文已被整理到了《Android微信开发教程汇总》,《java微信开发教程汇总》欢迎大家学习阅读。

     
  • 相关阅读:
    SpringBoot启动里的细节问题
    SpringBoot运行过程从SpringApplication开始解读
    SpringBoot应用启动流程
    SpringBoot上传相关配置
    planUML时序图
    JAVA设计模式--单例模式
    中国云运营商横向对比——IaaS服务对标
    ansible api2.0 多进程执行不同的playbook
    自动化运维平台搭建
    django-将数据库数据转换成JSON格式(ORM和SQL两种情况)
  • 原文地址:https://www.cnblogs.com/jabez1992/p/9184427.html
Copyright © 2011-2022 走看看