微信分享比较简单,麻烦点的地方就是生成签名, 同时要特别记住签名的url, 我做的时候遇到一个情况,就是第一次分享正常,分享后再转发就不行啦,我就是因为url不对造成的,因为分享后微信会在链接后加上标识,比如分享给朋友会加上?from=singlemessage, 开始我是直接使用rails生成的url: mobile_article_path, 这个url就固定死了, 导致分享后的链接于这个url不一样,所以签名生成错误,再次转发也不行了。
url = request.url
if url.nil?
url = mobile_article_path(params[:id])
@url = 'https://' + ENV.fetch('DEFAULT_HOST') + url
else
@url = url
end
wechat_api = WechatApi::Wechat.new
@jsapi_config = wechat_api.get_jsapi_signature(@url)
微信js:
<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<script type="text/javascript">
wx.config({
debug: false,
appId: '<%= @jsapi_config[:appid] %>', // 必填,公众号的唯一标识
timestamp: <%= @jsapi_config[:timestamp] %>, // 必填,生成签名的时间戳
nonceStr: '<%= @jsapi_config[:noncestr] %>', // 必填,生成签名的随机串
signature: '<%= @jsapi_config[:signature] %>',// 必填,签名
jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage'] // 必填,需要使用的JS接口列表
});
var share_data = {
title: '家具头条|<%= @article.title %>', // 分享标题
desc: '<%= @article.meta_description %>', // 分享描述
link: '<%= @url %>', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: '<%= show_pic(@article.image, w: 300, h: 300) %>', // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
// alert('<%= @article.title %>');
},
cancel: function () {
// 用户取消分享后执行的回调函数
// alert('<%= @article.title %>');
}
}
wx.ready(function(){
wx.onMenuShareTimeline(share_data);
wx.onMenuShareAppMessage(share_data);
});
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
// alert(res);
});
</script>
签名:
# 获取
def get_jsapi_ticket
now = Time.now
now = now.to_i
client = HttpClientWechat.new(@access_token_url, @timeout, @skip_verify_ssl)
data = client.get_json('jsapi_ticket.json')
expire_seconds = data.blank? ? '' : data['expire']
jsapi_ticket = nil
# 过期时间为空或小于记录的时间
if expire_seconds.blank? || expire_seconds < now
access_token = get_access_token
client = HttpClientWechat.new(@jsapi_ticket_url, @timeout, @skip_verify_ssl)
get_info = client.get('getticket', params: { access_token: access_token , type: 'jsapi' })
unless get_info['ticket'].nil?
jsapi_ticket = get_info['ticket']
expire_seconds = now + 7000
data = {
jsapi_ticket: jsapi_ticket,
expire: expire_seconds
}
data_json = JSON.generate(data)
client.set_json('jsapi_ticket.json', data_json)
end
else
jsapi_ticket = data['jsapi_ticket']
end
jsapi_ticket
end
# jsapi 签名算法
def get_jsapi_signature(url)
jsapi_ticket = get_jsapi_ticket
res = nil
if jsapi_ticket
noncestr = create_noncestr
timestamp = Time.now.to_i
jsapi_config = {
noncestr: noncestr,
jsapi_ticket: jsapi_ticket,
timestamp: timestamp,
url: url
}
signature = sign_param(jsapi_config)
res = {
appid: @app_id,
timestamp: timestamp,
noncestr: noncestr,
signature: signature,
}
end
res
end
# 自定义菜单
def create_menu
end
private
#随机数生成算法
def create_noncestr
len = 30
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
newpass = ""
1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
newpass
end
#生成签名[排序,url参数格式,拼接key,机密]
def sign_param(param)
param = param.sort{ |a,b| a.to_s <=> b.to_s }.to_h
format = []
param.each_with_index do |value|
format << "#{value[0]}=#{value[1]}"
end
Digest::SHA1.hexdigest("#{format.join('&')}").upcase
end