微信分享功能开发

一、服务器端程序

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 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是写死的
  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微信开发教程汇总》欢迎大家学习阅读。

 

猜你喜欢

转载自www.cnblogs.com/jabez1992/p/9184427.html