想在微站里面实现分享帖子给朋友和朋友圈,显示图片和简介,就这么简单的功能折腾了1星期。。。主要是微信官方文档没看清楚,怪自己了。
遇到invalid signature签名错误。找了半天,各种调试,终于找到问题了,每个新闻的id是变动的,
url需要传入完整的地址,在微信官方手册里面查到的。
比如页面是/wx.aspx?id=111,需要完整传入,不能仅仅在url里面传入/wx.aspx
下面是stoneniqiu 的具体做法,大家可以参考一下。
内嵌在微信中的网页,右上角都会有一个默认的分享功能。如下图所示,第一个为自定义的效果,第二个为默认的效果。实现了自定义的分享链接是不是更让人有点击的欲望?下面讲解下开发的过程。
一、准备,设置js接口安全域名
这需要使用微信的jssdk,先需要在微信公众号后台进行设置:公众号设置-->功能设置-->JS接口安全域名。打开这个页面之后你会看到下面的提示。需要先下载这个文件并上传到指定域名的根目录。
这个文件里面是一个字符串,从名称看是用来校验用的。先上传了这个文件,你才能保存成功。这样你就可以使用jssdk了。
二、前端配置
首先要说明的是分享功能是一个配置功能,绑定在按钮的click事件中是没有效果的。也就是说只有点击右上角的分享才有效果(有的文字内容分享不知道是怎么实现的)。官方的js有四个步骤,首先是引入jssdk:
根据官方的配置参数,我们可以定义一个WXShareModel对象:
public class WXShareModel
{
public string appId { get; set; }
public string nonceStr { get; set; }
public long timestamp { get; set; }
public string signature { get; set; }
public string ticket { get; set; }
public string url { get; set; }
public void MakeSign()
{
var string1Builder = new StringBuilder();
string1Builder.Append("jsapi_ticket=").Append(ticket).Append("&")
.Append("noncestr=").Append(nonceStr).Append("&")
.Append("timestamp=").Append(timestamp).Append("&")
.Append("url=").Append(url.IndexOf("#") >= 0 ? url.Substring(0, url.IndexOf("#")) : url);
var string1 = string1Builder.ToString();
signature = Util.Sha1(string1, Encoding.Default);
}
}
然后是进行配置:
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '@Model.appId', // 必填,公众号的唯一标识
timestamp: '@Model.timestamp', // 必填,生成签名的时间戳
nonceStr: '@Model.nonceStr', // 必填,生成签名的随机串
signature: '@Model.signature',// 必填,签名,见附录1
jsApiList: ["checkJsApi", "onMenuShareTimeline", "onMenuShareAppMessage", "onMenuShareQQ", "onMenuShareQZone"] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.ready(function () {
document.querySelector('#checkJsApi').onclick = function () {
wx.checkJsApi({
jsApiList: [
'getNetworkType',
'previewImage'
],
success: function (res) {
alert(JSON.stringify(res));
}
});
};
//朋友圈
wx.onMenuShareTimeline({
title: '暖木科技', // 分享标题
link: '/home/lampindex', // 分享链接
imgUrl: '/images/s1.jpg',
success: function (res) {
alert('已分享');
},
cancel: function (res) {
alert('已取消');
},
fail: function (res) {
alert(JSON.stringify(res));
}
});
//朋友
wx.onMenuShareAppMessage({
title: '暖木科技', // 分享标题
desc: '宝宝的睡眠很重要,你的睡眠也很重要', // 分享描述
link: '/home/lampindex', // 分享链接
imgUrl: '/images/s1.jpg', // 分享图标
type: '', // 分享类型,music、video或link,不填默认为link
dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
success: function () {
// 用户确认分享后执行的回调函数
alert("分享");
},
cancel: function () {
// 用户取消分享后执行的回调函数
alert("取消分享");
}
});
});
然后剩下就是后端的事情了。后端的关键是获取access_token和jsapi_ticket以及生成正确的签名。另外如果要统计分享的数量,最好就是在success方法中进行统计了。
三、生成签名
1.access_token
获取access_token方法全平台都是一致的。
public const string AccessTokenUrl = "https://api./cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
public TokenResult GetAccessToken()
{
var url = string.Format(WxDeviceConfig.AccessTokenUrl, WxDeviceConfig.AppId, WxDeviceConfig.APPSECRET);
var res = SendHelp.Send(null, url, null, CommonJsonSendType.GET);
return res;
}
access_token的超时时间是7200秒,所以先可以缓存起来。SendHelp文章末尾可下载
2.获取jsapi_ticket
access_token的作用就是为了获取jsapi_ticket。用get方式获取,url:https://api./cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi,返回的JSON对象如下。
{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}
所以可以定义一个模型:
public class jsapiTicketModel
{
public string errcode { get; set; }
public string errmsg { get; set; }
public string ticket { get; set; }
public string expires_in { get; set; }
}
再完成获取ticket的方法:
public jsapiTicketModel GetJsApiTicket(string accessToken)
{
var url = string.Format(WxPayConfig.Jsapi_ticketUrl, accessToken);
return SendHelp.Send(accessToken, url, "", CommonJsonSendType.GET);
}
ticket过期时间也是7200秒,并且不能频繁的请求,所以也需要再服务端缓存起来。
private void setCacheTicket(string cache)
{
_cacheManager.Set(tokenKey, cache, 7200);
}
MemoryCacheManager:
View Code
3.签名
终于到这一步了,然后你在文档中看到让你失望的一幕:
么有C#的demo,支付那边都提供了,为啥jssdk没有提供,好吧先不吐槽了。官方也说明白签名的规则。一开始我使用的是/night-king/weixinSDK中的签名:
public static string Sha1(string orgStr, string encode = "UTF-8")
{
var sha1 = new SHA1Managed();
var sha1bytes = System.Text.Encoding.GetEncoding(encode).GetBytes(orgStr);
byte[] resultHash = puteHash(sha1bytes);
string sha1String = BitConverter.ToString(resultHash).ToLower();
sha1String = sha1String.Replace("-", "");
return sha1String;
}//错误示例
得出的结果和官方校验的不一致,一直提示签名错误。
正确的写法是:
public static string Sha1(string orgStr, Encoding encode)
{
SHA1 sha1 = new SHA1CryptoServiceProvider();
byte[] bytes_in = encode.GetBytes(orgStr);
byte[] bytes_out = puteHash(bytes_in);
sha1.Dispose();
string result = BitConverter.ToString(bytes_out);
result = result.Replace("-", "");
return result;
}
和官方校验的结果一直后,就ok了(忽略大小写)。另外一个需要注意的地方是签名中的url。如果页面有参数,model中的url也需要带参数,#号后面的不要。不然也是会报签名错误。
public ActionResult H5Share()
{
var model = new WXShareModel();
model.appId = WxPayConfig.APPID;
model.nonceStr = WxPayApi.GenerateNonceStr();
model.timestamp = Util.CreateTimestamp();
model.ticket = GetTicket();
model.url = "/AuthWeiXin/share";// domain + Request.Url.PathAndQuery;
model.MakeSign();
Logger.Debug("获取到ticket:" + model.ticket);
Logger.Debug("获取到签名:" + model.signature);
return View(model);
}
四、小结
wx.config中的debug为true会alert各种操作结果。参数正确之后界面会提示:
至此,分享的功能就ok了。也就打开了调用其他jssdk的大门。另外文中的SendHelp对象是用的Senparc(基于.net4.5)的dll。
参考资料:
我的核心代码
[System.Web.Services.WebMethod]public static WXShareModel GetKey(stringstr)
{
WXShareModel aModel= newWXShareModel();
WXToolsHelper tb= newWXToolsHelper();string AppId = "你的APPID";string secret = "你的secret";string access_token =tb.GetAccess_Token(AppId, secret);
aModel.appId=AppId;
aModel.nonceStr=tb.CreatenNonce_str();
aModel.timestamp=tb.CreatenTimestamp();
aModel.ticket=tb.GetTicket(access_token);
aModel.url=str;
aModel.MakeSign();returnaModel;
}public classWXShareModel
{public string appId { get; set; }public string nonceStr { get; set; }public long timestamp { get; set; }public string ticket { get; set; }public string url { get; set; }public string signature { get; set; }public voidMakeSign()
{var string1Builder = newStringBuilder();
string1Builder.Append("jsapi_ticket=").Append(ticket).Append("&")
.Append("noncestr=").Append(nonceStr).Append("&")
.Append("timestamp=").Append(timestamp).Append("&")
.Append("url=").Append(url.IndexOf("#") >= 0 ? url.Substring(0, url.IndexOf("#")) : url);var string1 =string1Builder.ToString();
signature=Sha1(string1, Encoding.Default);
}public static string Sha1(stringorgStr, Encoding encode)
{
SHA1CryptoServiceProvider sha1= newSHA1CryptoServiceProvider();byte[] bytes_in =encode.GetBytes(orgStr);byte[] bytes_out =puteHash(bytes_in);
sha1.Dispose();string result =BitConverter.ToString(bytes_out);
result= result.Replace("-", "");returnresult;
}public classWXToolsHelper
{///
///获取全局的access_token,程序缓存///
/// 第三方用户唯一凭证
/// 第三方用户唯一凭证密钥,即appsecret
/// 得到的全局access_token
public string GetAccess_Token(string AppId, stringAppSecret)
{try{//先查缓存数据
if (HttpContext.Current.Cache["access_token"] != null)
{return HttpContext.Current.Cache["access_token"].ToString();
}else{returnGetToken(AppId, AppSecret);
}
}catch{returnGetToken(AppId, AppSecret);
}
}///
///获取全局的access_token///
/// 第三方用户唯一凭证
/// 第三方用户唯一凭证密钥,即appsecret
/// 得到的全局access_token
public string GetToken(string AppId, stringAppSecret)
{var client = .WebClient();
client.Encoding=System.Text.Encoding.UTF8;var url = string.Format("https://api./cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", AppId, AppSecret);var data =client.DownloadString(url);var jss = newJavaScriptSerializer();var access_tokenMsg = jss.Deserialize>(data);//放入缓存中
HttpContext.Current.Cache.Insert("access_token", access_tokenMsg["access_token"], null, DateTime.Now.AddSeconds(7100), TimeSpan.Zero, CacheItemPriority.Normal, null);//清除jsapi_ticket缓存
HttpContext.Current.Cache.Remove("ticket");//获取jsapi_ticket,为了同步
GetTicket(access_tokenMsg["access_token"].ToString());return access_tokenMsg["access_token"].ToString();
}///
///获取jsapi_ticket,程序缓存///
/// 全局的access_token
/// 得到的jsapi_ticket
public string GetJsapi_Ticket(stringaccess_token)
{try{//先查缓存数据
if (HttpContext.Current.Cache["ticket"] != null)
{return HttpContext.Current.Cache["ticket"].ToString();
}else{returnGetTicket(access_token);
}
}catch{returnGetTicket(access_token);
}
}///
///获取jsapi_ticket///
/// 全局的access_token
/// 得到的jsapi_ticket
public string GetTicket(stringaccess_token)
{var client = .WebClient();
client.Encoding=System.Text.Encoding.UTF8;var url = string.Format("https://api./cgi-bin/ticket/getticket?access_token={0}&type=jsapi", access_token);var data =client.DownloadString(url);var jss = newJavaScriptSerializer();var ticketMsg = jss.Deserialize>(data);try{//放入缓存中
HttpContext.Current.Cache.Insert("ticket", ticketMsg["ticket"], null, DateTime.Now.AddSeconds(7100), TimeSpan.Zero, CacheItemPriority.Normal, null);return ticketMsg["ticket"].ToString();
}catch(Exception ex)
{returnex.Message;
}
}///
///微信权限签名的 sha1 算法///签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同///
/// 获取到的jsapi_ticket
/// 生成签名的随机串
/// 生成签名的时间戳
/// 签名用的url必须是调用JS接口页面的完整URL
///
public string GetShal(string jsapi_ticket, string noncestr, long timestamp, stringurl)
{string strSha1 = string.Format("jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}", jsapi_ticket, noncestr, timestamp, url);return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(strSha1, "sha1").ToLower();
}///
///微信权限签名( sha1 算法 )///签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同///
/// 第三方用户唯一凭证
/// /// 第三方用户唯一凭证密钥,即appsecret
/// 生成签名的随机串
/// 生成签名的时间戳
/// 签名用的url必须是调用JS接口页面的完整URL
///
public string Get_Signature(string AppId, string AppSecret, string noncestr, long timestamp, stringurl)
{string access_token = GetAccess_Token(AppId, AppSecret); //获取全局的access_token
string jsapi_ticket = GetJsapi_Ticket(access_token); //获取jsapi_ticket
string strSha1 = string.Format("jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}", jsapi_ticket, noncestr, timestamp, url);return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(strSha1, "sha1").ToLower();
}///
///微信权限签名( sha1 算法 )///签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同///
/// 第三方用户唯一凭证
/// /// 第三方用户唯一凭证密钥,即appsecret
/// 生成签名的随机串
/// 生成签名的时间戳
/// 签名用的url必须是调用JS接口页面的完整URL
///
public void signatureOut(string AppId, string AppSecret, string noncestr, long timestamp, string url, out string access_token, out string jsapi_ticket, out stringsignature)
{
access_token= GetAccess_Token(AppId, AppSecret); //获取全局的access_token
jsapi_ticket= GetJsapi_Ticket(access_token); //获取jsapi_ticket
string strSha1 = string.Format("jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}", jsapi_ticket, noncestr, timestamp, url);
signature= System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(strSha1, "sha1").ToLower();
}private string[] strs = new string[]
{"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};///
///创建随机字符串///
///
public stringCreatenNonce_str()
{
Random r= newRandom();var sb = newStringBuilder();var length =strs.Length;for (int i = 0; i < 15; i++)
{
sb.Append(strs[r.Next(length- 1)]);
}returnsb.ToString();
}///
///创建时间戳///
///
public longCreatenTimestamp()
{return (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000;
}
}
前段调用
var strUrl = location.href.split('#')[0];
$.ajax({
type:"Post",
url:"config.aspx/GetKey",//方法传参的写法一定要对,strUrl为形参的名字
data: "{'str':'" + strUrl + "'}",
contentType:"application/json; charset=utf-8",
dataType:"json",
success:function(data) {//返回的数据用data.d获取内容
$("#wx-share-sign").val(data.d.signature);
wxconifg(data.d);
},
error:function(err) {
alert('55');
}
});functionwxconifg(WXDate) {
wx.config({
debug:false,
appId:'你的APPID',
timestamp: WXDate.timestamp,
nonceStr: WXDate.nonceStr,
signature: WXDate.signature,
jsApiList: ["checkJsApi", "onMenuShareTimeline", "onMenuShareAppMessage", "onMenuShareQQ", "onMenuShareQZone"]
});
wx.ready(function() {
wx.onMenuShareAppMessage({
title: $("#wx-share-title").val(),
desc: $("#wx-share-desc").val(),
link: strUrl,
imgUrl: $("#wx-share-img").val(),
trigger:function(res) {
},
success:function(res) {
},
cancel:function(res) {
},
fail:function(res) {
alert(JSON.stringify(res));
}
});//分享到朋友圈
wx.onMenuShareTimeline({
title:'XX新闻|'+$("#wx-share-desc").val(),
desc: $("#wx-share-desc").val(),
link: $("#wx-share-link").val(),
imgUrl: $("#wx-share-img").val(),
type:'link',
dataUrl: strUrl,
trigger:function(res) {
},
success:function(res) {
},
cancel:function(res) {
},
fail:function(res) {
alert(JSON.stringify(res));
}
});
});
}
java 微信分享朋友圈 链接显示图片_【微信开发】-- 微信分享功能(分享到朋友和朋友圈显示图片和简介)...