java开发微信分享到朋友圈功能
微信分享功能开发
用了一天时间,把微信发送给朋友和分享到朋友圈功能开发出来,在这里给大家分享一下,避免大家走弯路。
一、服务器端程序
packagecom.wiimedia.controller; importjava.io.IOException; importjava.security.MessageDigest; importjava.security.NoSuchAlgorithmException; importjava.text.ParseException; importjava.text.SimpleDateFormat; importjava.util.Arrays; importjava.util.Date; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.stereotype.Controller; importorg.springframework.web.bind.annotation.RequestMapping; importcom.google.gson.Gson; importcom.wiimedia.model.Ticket; importcom.wiimedia.service.ArticleSolrService; importcom.wiimedia.service.TicketRepository; importcom.wiimedia.service.TicketRepositorySolr; importcom.wiimedia.utils.GetRandomStr; importcom.wiimedia.utils.SignatureBean; importcom.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") publicclassWeixinshareController{ @Autowired privateTicketRepositorySolrticketRepositorySolr; @RequestMapping("/getSignature") publicStringgetSignature(HttpServletRequestrequest, HttpServletResponseresponse)throwsIOException,ParseException{ //获取签名页面链接 Stringurl=request.getParameter("url"); SimpleDateFormatformat=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss"); //从数据库中获取标签,并检查标签是否过期 Ticketoldticket=ticketRepositorySolr.getTicketById("20160114wiimediamrylsong1152"); if(oldticket==null){//第一次访问,标签不存在。 executeTicket(response,"1",url,format); returnnull; }else{//标签存在,判断标签是否超时 StringoldAcquiretime=oldticket.getAcquiretime(); longdifference=format.parse(format.format(newDate())).getTime()-format.parse(oldAcquiretime).getTime(); if(difference>7100000){//标签超时,重新到微信服务器请求标签超时时间为7200秒(7200000毫秒) executeTicket(response,"2",url,format); returnnull; }else{//标签未超时 /** *注意事项 *1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。 *2.签名用的url必须是调用JS接口页面的完整URL。 *3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。 * ****根据第1点要求signature配置的时候很容易出错,需要把生成Ticket的noncestr和timestamp传给客户端*** */ Stringsignature=signature(oldticket.getTicket(),oldticket.getTimestamp(),oldticket.getNoncestr(),url); SignatureBeansignatureBean=newSignatureBean(); signatureBean.setNoncestr(oldticket.getNoncestr()); signatureBean.setSignature(signature); signatureBean.setTimestamp(oldticket.getTimestamp()); signatureBean.setUrl(url); response.setContentType("text/html;charset=UTF-8"); response.getWriter().print(newGson().toJson(signatureBean)); returnnull; } } } /** * *<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 * */ publicvoidexecuteTicket(HttpServletResponseresponse,Stringflag,Stringurl,SimpleDateFormatformat)throwsIOException{ //获取签名随即字符串 GetRandomStrrandomStr=newGetRandomStr(); Stringnoncestr=randomStr.getRandomString(15); //获取签名时间戳 Stringtimestamp=Long.toString(System.currentTimeMillis()); //请求accessToken StringaccessTokenUrl="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=您的APPID&secret=您的密匙"; StringtokenJson=WeixinUtil.httpRequest(accessTokenUrl,"GET",null); Gsongson=newGson(); ShareAccess_Tokentoken=gson.fromJson(tokenJson,ShareAccess_Token.class); Stringto=token.getAccess_token(); //获取标签 StringurlTicket="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+to+"&type=jsapi"; StringticketJson=WeixinUtil.httpRequest(urlTicket,"GET",null); Ticketticket=gson.fromJson(ticketJson,Ticket.class); Stringt=ticket.getTicket(); //Stringuuid=UUID.randomUUID().toString().trim().replaceAll("-",""); //我的TicketID是写死的 Stringacquiretime=format.format(newDate()); 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传给客户端* */ Stringsignature=signature(t,timestamp,noncestr,url); SignatureBeansignatureBean=newSignatureBean(); signatureBean.setNoncestr(noncestr); signatureBean.setSignature(signature); signatureBean.setTimestamp(timestamp); signatureBean.setUrl(url); response.setContentType("text/html;charset=UTF-8"); response.getWriter().print(newGson().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 * */ privateStringsignature(Stringjsapi_ticket,Stringtimestamp,Stringnoncestr,Stringurl){ jsapi_ticket="jsapi_ticket="+jsapi_ticket; timestamp="timestamp="+timestamp; noncestr="noncestr="+noncestr; url="url="+url; String[]arr=newString[]{jsapi_ticket,timestamp,noncestr,url}; //将token、timestamp、nonce,url参数进行字典序排序 Arrays.sort(arr); StringBuildercontent=newStringBuilder(); for(inti=0;i<arr.length;i++){ content.append(arr[i]); if(i!=arr.length-1){ content.append("&"); } } MessageDigestmd=null; StringtmpStr=null; try{ md=MessageDigest.getInstance("SHA-1"); //将三个参数字符串拼接成一个字符串进行sha1加密 byte[]digest=md.digest(content.toString().getBytes()); tmpStr=byteToStr(digest); }catch(NoSuchAlgorithmExceptione){ e.printStackTrace(); } content=null; returntmpStr; } /** *将字节转换为十六进制字符串 * *@parammByte *@return */ privatestaticStringbyteToHexStr(bytemByte){ char[]Digit={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; char[]tempArr=newchar[2]; tempArr[0]=Digit[(mByte>>>4)&0X0F]; tempArr[1]=Digit[mByte&0X0F]; Strings=newString(tempArr); returns; } /** *将字节数组转换为十六进制字符串 * *@parambyteArray *@return */ privatestaticStringbyteToStr(byte[]byteArray){ StringstrDigest=""; for(inti=0;i<byteArray.length;i++){ strDigest+=byteToHexStr(byteArray[i]); } returnstrDigest; } classShareAccess_Token{ privateStringaccess_token; privateStringexpires_in; publicStringgetAccess_token(){ returnaccess_token; } publicvoidsetAccess_token(StringaccessToken){ access_token=accessToken; } publicStringgetExpires_in(){ returnexpires_in; } publicvoidsetExpires_in(StringexpiresIn){ expires_in=expiresIn; } } }
二、客户端代码.
<scripttype="text/javascript"> varurl=window.location.href; vararticleId=""; varshareTitle="明日医疗资讯"; varshareImgUrl=""; varuserinfo=localStorage.getItem("_userinfo"); vartimestamp; varnoncestr; varsignature; //获取签名 $.ajax({ type:"GET", url:"WeixinshareController/Api/Inteface/getSignature", //data:{timestamp:timestamp,noncestr:noncestr,url:url}, data:{url:url}, success:function(data){ varobjData=JSON.parse(data); timestamp=objData.timestamp; noncestr=objData.noncestr; signature=objData.signature; console.log(objData); wxShare(); } }); functionwxShare(){ 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
①Ticket
packagecom.wiimedia.model; publicclassTicket{ privateStringtid; privateStringticket; privateStringerrcode; privateStringerrmsg; privateStringexpires_in; privateStringacquiretime; privateStringnoncestr; privateStringtimestamp; publicTicket(Stringtid,Stringticket,Stringerrcode,Stringerrmsg, StringexpiresIn,Stringacquiretime,Stringnoncestr, Stringtimestamp){ 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; } publicStringgetTid(){ returntid; } publicvoidsetTid(Stringtid){ this.tid=tid; } publicStringgetTicket(){ returnticket; } publicvoidsetTicket(Stringticket){ this.ticket=ticket; } publicStringgetErrcode(){ returnerrcode; } publicvoidsetErrcode(Stringerrcode){ this.errcode=errcode; } publicStringgetErrmsg(){ returnerrmsg; } publicvoidsetErrmsg(Stringerrmsg){ this.errmsg=errmsg; } publicStringgetExpires_in(){ returnexpires_in; } publicvoidsetExpires_in(StringexpiresIn){ expires_in=expiresIn; } publicStringgetAcquiretime(){ returnacquiretime; } publicvoidsetAcquiretime(Stringacquiretime){ this.acquiretime=acquiretime; } publicStringgetNoncestr(){ returnnoncestr; } publicvoidsetNoncestr(Stringnoncestr){ this.noncestr=noncestr; } publicStringgetTimestamp(){ returntimestamp; } publicvoidsetTimestamp(Stringtimestamp){ this.timestamp=timestamp; } }
②添加到数据库的业务根据自己需要进行实现.
③GetRandomStr
packagecom.wiimedia.utils; importjava.util.Random; publicclassGetRandomStr{ /** * *<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 * */ publicStringgetRandomString(intlength){ Stringbase="abcdefghijklmnopqrstuvwxyz0123456789"; Randomrandom=newRandom(); StringBuffersb=newStringBuffer(); for(inti=0;i<length;i++){ intnumber=random.nextInt(base.length()); sb.append(base.charAt(number)); } returnsb.toString(); } }
④SignatureBean
packagecom.wiimedia.utils; publicclassSignatureBean{ privateStringnoncestr; privateStringurl; privateStringtimestamp; privateStringsignature; publicStringgetNoncestr(){ returnnoncestr; } publicvoidsetNoncestr(Stringnoncestr){ this.noncestr=noncestr; } publicStringgetUrl(){ returnurl; } publicvoidsetUrl(Stringurl){ this.url=url; } publicStringgetTimestamp(){ returntimestamp; } publicvoidsetTimestamp(Stringtimestamp){ this.timestamp=timestamp; } publicStringgetSignature(){ returnsignature; } publicvoidsetSignature(Stringsignature){ this.signature=signature; } }
⑤WeixinUtil
packagecom.wiimedia.utils.weixin; importjava.io.BufferedReader; importjava.io.InputStream; importjava.io.InputStreamReader; importjava.io.OutputStream; importjava.net.ConnectException; importjava.net.URL; importjavax.net.ssl.HttpsURLConnection; importjavax.net.ssl.SSLContext; importjavax.net.ssl.SSLSocketFactory; importjavax.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 * */ publicclassWeixinUtil{ /** *发起https请求并获取结果 * *@paramrequestUrl请求地址 *@paramrequestMethod请求方式(GET、POST) *@paramoutputStr提交的数据 *@returnJSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) */ publicstaticStringhttpRequest(StringrequestUrl,StringrequestMethod,StringoutputStr){ StringBufferbuffer=newStringBuffer(); try{ //创建SSLContext对象,并使用我们指定的信任管理器初始化 TrustManager[]tm={newMyX509TrustManager()}; SSLContextsslContext=SSLContext.getInstance("SSL","SunJSSE"); sslContext.init(null,tm,newjava.security.SecureRandom()); //从上述SSLContext对象中得到SSLSocketFactory对象 SSLSocketFactoryssf=sslContext.getSocketFactory(); URLurl=newURL(requestUrl); HttpsURLConnectionhttpUrlConn=(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){ OutputStreamoutputStream=httpUrlConn.getOutputStream(); //注意编码格式,防止中文乱码 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } //将返回的输入流转换成字符串 InputStreaminputStream=httpUrlConn.getInputStream(); InputStreamReaderinputStreamReader=newInputStreamReader(inputStream,"utf-8"); BufferedReaderbufferedReader=newBufferedReader(inputStreamReader); Stringstr=null; while((str=bufferedReader.readLine())!=null){ buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); //释放资源 inputStream.close(); inputStream=null; httpUrlConn.disconnect(); returnbuffer.toString(); }catch(ConnectExceptionce){ ce.printStackTrace(); }catch(Exceptione){ 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.configdebug:false,
本文已被整理到了《Android微信开发教程汇总》,《java微信开发教程汇总》欢迎大家学习阅读。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。