很多时候我们要在微信中分享h5网页,这个时候就得用微信的分享接口来自定义分享的地址、标题、描述、缩略图了。
分享到微信的时候遇到一个问题,就是第一次分享到微信里,是正确的,但是在微信打开分享的链接,再次分享的时候,发现小图片没了。
原因就是微信在你分享的时候,会自动给你分享的地址后面加入参数,导致你分享的地址改变了,这时候再去用原来的地址获取签名,就不能用了。
解决方法,动态获取地址:
var link = encodeURIComponent(location.href.split('#')[0]);//编码动态获取地址
var link = location.href.split('#')[0];//动态获取地址
代码:
<script src="js/jquery.min.js"></script> <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> <script src="js/sha1.js"></script> <script src="js/WeiXinShare.js"></script> <script type="text/javascript"> //var link = "http://adv.xxx.com/Index.html";//原网页地址 //var link = encodeURIComponent(location.href.split('#')[0]);//编码动态获取地址 //var link = "http://adv.xxx.com/Index.html?from=groupmessage";//微信加入参数后的地址 var link = location.href.split('#')[0];//动态获取当前地址,防止微信在原地址后加入参数 var imgUrl = "http://adv.xxx.com/img/fm.png";//缩略图地址 var apiUrl = "https://api.xxx.com/Common/GetSignature";//服务器端签名 var title = "这里是分享标题"; var desc = "这里是分享描述"; Share(link, imgUrl, title, desc, apiUrl); </script>
Share函数代码:
function Share(link, imgUrl, title, desc, apiUrl) { var randNum = Math.floor(Math.random(1000, 9999) * 10000); $.ajax({ url: apiUrl, type: 'GET', dataType: 'jsonp', async: false, data: { linkUrl: link, randNum: randNum } }).done(function (d) { //debugger; ShareCallBack(d, link, imgUrl, title, desc); }).fail(function () { console.log("error"); }).always(function () { }); } function ShareCallBack(d, link, imgUrl, title, desc) { //debugger; var appid = ""; var timestamp = ""; var noncestr = ""; var signature = ""; if (d.Data != undefined && d.Data != null) { var result = $.parseJSON(d.Data); timestamp = result.timestamp; noncestr = result.noncestr; signature = result.signature; appid = result.appid; //weixin begin wx.config({ debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: appid, // 必填,公众号的唯一标识 timestamp: timestamp, // 必填,生成签名的时间戳 nonceStr: noncestr, // 必填,生成签名的随机串 signature: signature, // 必填,签名,见附录1 jsApiList: ["onMenuShareTimeline", "onMenuShareAppMessage", "onMenuShareQQ", "onMenuShareWeibo", "onMenuShareQZone"] //jsApiList 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function () { //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。 wx.onMenuShareTimeline({ title: title, // 分享标题 desc: desc, // 分享描述 link: link, // 分享链接 imgUrl: imgUrl, // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: title, // 分享标题 desc: desc, // 分享描述 link: link, // 分享链接 imgUrl: imgUrl, // 分享图标 type: 'link', // 分享类型,music、video或link,不填默认为link dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: title, // 分享标题 desc: desc, // 分享描述 link: link, // 分享链接 imgUrl: imgUrl, // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: title, // 分享标题 desc: desc, // 分享描述 link: link, // 分享链接 imgUrl: imgUrl, // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: title, // 分享标题 desc: desc, // 分享描述 link: link, // 分享链接 imgUrl: imgUrl, // 分享图标 success: function () { // 用户确认分享后执行的回调函数 }, cancel: function () { // 用户取消分享后执行的回调函数 } }); wx.error(function (res) { // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 }); }); //weixin end } }
服务器端签名:
/// <summary> /// 获取微信 signature /// </summary> /// <param name="callback"></param> [AcceptVerbs("GET")] public void GetSignature(string callback, string linkUrl) { MessagesDataCodeModel json = new MessagesDataCodeModel(false, "无效参数", 401); try { string AppId = "xxxxxxxx";//微信公众号官网签发的 appid和appsecret string AppSecret = "xxxxxxx"; DateTime endDate = DateTime.Now.AddSeconds(7200); string token = "", ticket = ""; List<Models.WeiXinTokens> list = WeiXinTokensBLL.GetList(); if (list != null && list.Count > 0) { if (list[0].EndDate > DateTime.Now) { token = list[0].Token.Trim(); ticket = list[0].Ticket.Trim(); } } if (string.IsNullOrEmpty(token)) { string result = Common.WeiXinHelper.GetJsApiTicket(AppId, AppSecret); JavaScriptSerializer jss = new JavaScriptSerializer(); WeiXinTokenModel model = jss.Deserialize<WeiXinTokenModel>(result); if (model != null) { Models.WeiXinTokens wxt = new Models.WeiXinTokens(); wxt.Token = model.token; wxt.Ticket = model.ticket; wxt.EndDate = endDate; if (list.Count > 0) { wxt.ID = list[0].ID; WeiXinTokensBLL.ModifyEntity(wxt); } else { WeiXinTokensBLL.Append(wxt); } token = model.token; ticket = model.ticket; } } else { Models.WeiXinTokens wxt = list[0]; wxt.Token = token; wxt.Ticket = ticket; WeiXinTokensBLL.ModifyEntity(wxt); } string noncestr = Guid.NewGuid().ToString().Replace("-", ""); int timestamp = Utils.ConvertDateTimeInt(DateTime.Now); string key_str = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + linkUrl; string signature = Utils.EncryptToSHA1(key_str); string jsonStr = "{\"ticket\":\"" + ticket + "\",\"token\":\"" + token + "\",\"noncestr\":\"" + noncestr + "\",\"timestamp\":\"" + timestamp + "\",\"appid\":\"" + AppId + "\",\"signature\":\"" + signature + "\",\"url\":\"" + linkUrl + "\"}"; json.Success = true; json.Msg = "操作成功"; json.Code = 200; json.Data = jsonStr; } catch (Exception ex) { json.Success = false; json.Msg = "服务器无响应"; json.Code = 500; json.Data = -1; } string str = ToJsonTran.ToJson2(json); if (!string.IsNullOrEmpty(callback)) { str = callback + "(" + str + ")"; } System.Web.HttpContext.Current.Response.Clear(); System.Web.HttpContext.Current.Response.Write(str); System.Web.HttpContext.Current.Response.End(); }
辅助方法:
/// <summary> /// C# SHA1散列算法 /// </summary> /// <param name="str"></param> /// <returns></returns> public static string EncryptToSHA1(string str) { byte[] cleanBytes = Encoding.Default.GetBytes(str); byte[] hashedBytes = System.Security.Cryptography.SHA1.Create().ComputeHash(cleanBytes); return BitConverter.ToString(hashedBytes).Replace("-", ""); } /// <summary> /// DateTime时间格式转换为Unix时间戳格式 /// </summary> /// <param name="time"> DateTime时间格式</param> /// <returns>Unix时间戳格式</returns> public static int ConvertDateTimeInt(System.DateTime time) { System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); return (int)(time - startTime).TotalSeconds; }
签名程序:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using System.Web.Script.Serialization; namespace Common { public class WeiXinHelper { public static string GetJsApiTicket(string AppId, string AppSecret) { JavaScriptSerializer jss = new JavaScriptSerializer(); string result_token = GetAccessToken(AppId, AppSecret); string result_jsapi_ticket = ""; if (!string.IsNullOrEmpty(result_token)) { result_jsapi_ticket = GetJsApiTicketByToken(result_token); } return "{\"token\":\"" + result_token + "\",\"ticket\":\"" + result_jsapi_ticket + "\"}"; } /// <summary> /// 获得微信AccessToken /// 正常返回:{"access_token":"ACCESS_TOKEN","expires_in":7200} /// 错误返回:{"errcode":40013,"errmsg":"invalid appid"} /// </summary> /// <param name="AppId"></param> /// <param name="AppSecret"></param> /// <returns></returns> public static string GetAccessToken(string AppId, string AppSecret) { string url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + AppId + "&secret=" + AppSecret; string result_token = ""; WebRequest webRequest = System.Net.HttpWebRequest.Create(url); WebResponse webResponse = webRequest.GetResponse(); Stream stream = webResponse.GetResponseStream(); using (StreamReader sr = new StreamReader(stream)) { result_token = sr.ReadToEnd(); } JavaScriptSerializer jss = new JavaScriptSerializer(); WeiXinAccessTokenResult atr = jss.Deserialize<WeiXinAccessTokenResult>(result_token); if (!string.IsNullOrEmpty(atr.access_token)) { return atr.access_token; } return ""; } public static string GetJsApiTicketByToken(string AccessToken) { string url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + AccessToken + "&type=jsapi"; string result = Utils.ClientRequest(url, "", "GET"); JavaScriptSerializer jss = new JavaScriptSerializer(); WeiXinJsApiTicketResult ticket_model = jss.Deserialize<WeiXinJsApiTicketResult>(result); if (ticket_model.errcode == "0") { return ticket_model.ticket; } return ""; } } public class WeiXinJsApiTicketResult { public string errcode { get; set; } public string errmsg { get; set; } public string ticket { get; set; } public string expires_in { get; set; } } public class WeiXinAccessTokenResult { //正常返回:{"access_token":"ACCESS_TOKEN","expires_in":7200} //错误返回:{"errcode":40013,"errmsg":"invalid appid"} public string access_token { get; set; } public string expires_in { get; set; } public string errcode { get; set; } public string errmsg { get; set; } public string error { get; set; } } }
微信分享SDK:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115