H5+.Net Webapi集成微信分享前后端代码 微信JS-SDK wx.onMenuShareTimeline wx.onMenuShareAppMessage

说明:

1/因为赚麻烦这里没有使用数据库或服务器缓存来存储access_token和jsapi_ticket,为了方便这里使用了本地的xml进行持久化这两个值以及这两个值的创建时间和有限期限。

2/每次请求先检查有没有存在并且在有效期内的access_token和jsapi_ticket,存在的话直接进行加密操作,不存在或过期重新请求wechat接口获得再进行加密。

3/每个分享的页面都需要将当页的url发送到服务器进行签名,且一定要encodeURIComponent,因为在微信中打开会自动给当前链接加个各种参数,从而导致url不一致,导致invalid signature签名错误。

4/分享的图标url( imgUrl )必须是绝对路径。

一 封装的微信授权工具类

WechatJsSdk.cs

using SouthRuiHeH5.Models;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web.Script.Serialization;
using System.Xml.Linq;

namespace SouthRuiHeH5.Provider
{
    public class WechatJsSdk
    {
        /// <summary>
        /// 模拟get请求获取AccessToken
        /// </summary>
        /// <param name="appID"></param>
        /// <param name="appSecret"></param>
        /// <returns></returns>
        public static string GetAccessToken(string appID, string appSecret)
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appID, appSecret);
            JavaScriptSerializer js = new JavaScriptSerializer();
            AccessTokenOutput output = js.Deserialize<AccessTokenOutput>(HttpGet(url));
            SaveAccessTokenInXml(output);
            return output.access_token;
        }

        /// <summary>
        /// 将AccessToken保存进xml
        /// </summary>
        /// <param name="input"></param>
        private static void SaveAccessTokenInXml(AccessTokenOutput input)
        {
            if (string.IsNullOrWhiteSpace(input?.access_token)) return;
            var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
            string filePath = mappedPath + "Xml/AccessToken.xml";
            XDocument inputDoc = XDocument.Load(filePath);
            inputDoc.Elements().First().Element("access_token").Value = input.access_token;
            inputDoc.Elements().First().Element("expires_in").Value = input.expires_in;
            inputDoc.Elements().First().Element("create_time").Value = DateTime.Now.ToString();
            inputDoc.Save(filePath);
        }

        /// <summary>
        /// 模拟get请求获取JsapiTicket
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        public static string GetJsapiTicket(string accessToken)
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi", accessToken);
            JavaScriptSerializer js = new JavaScriptSerializer();
            JsapiTicketOutput output = js.Deserialize<JsapiTicketOutput>(HttpGet(url));
            SaveJsapiTicketInXml(output);
            return output.ticket;
        }

        /// <summary>
        /// 将JsapiTicket保存进xml
        /// </summary>
        /// <param name="input"></param>
        private static void SaveJsapiTicketInXml(JsapiTicketOutput input)
        {
            if (string.IsNullOrWhiteSpace(input?.ticket)) return;
            var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
            string filePath = mappedPath + "Xml/JsapiTicket.xml";
            XDocument inputDoc = XDocument.Load(filePath);
            inputDoc.Elements().First().Element("ticket").Value = input.ticket;
            inputDoc.Elements().First().Element("expires_in").Value = input.expires_in;
            inputDoc.Elements().First().Element("create_time").Value = DateTime.Now.ToString();
            inputDoc.Save(filePath);
        }

        /// <summary>
        /// get模拟请求
        /// </summary>
        /// <param name="Url"></param>
        /// <param name="postDataStr"></param>
        /// <returns></returns>
        private static string HttpGet(string Url, string postDataStr = "")
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);
            request.Method = "GET";
            request.ContentType = "text/html;charset=UTF-8";

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream myResponseStream = response.GetResponseStream();
            StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
            string retString = myStreamReader.ReadToEnd();
            myStreamReader.Close();
            myResponseStream.Close();
            return retString;
        }

        private static 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"
                                };

        /// <summary>
        /// 获取随机字符串
        /// </summary>
        /// <returns></returns>
        public static string CreatenNonce_str()
        {
            Random r = new Random();
            var sb = new StringBuilder();
            var length = strs.Length;
            for (int i = 0; i < 15; i++)
            {
                sb.Append(strs[r.Next(length - 1)]);
            }
            return sb.ToString();
        }

        /// <summary>
        /// 获取时间戳
        /// </summary>
        /// <returns></returns>
        public static long CreatenTimestamp()
        {
            return (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000;
        }

        /// <summary>
        /// sha1加密string1 获得Signature
        /// </summary>
        /// <param name="jsapi_ticket"></param>
        /// <param name="noncestr"></param>
        /// <param name="timestamp"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string GetSignature(string jsapi_ticket, string noncestr, long timestamp, string url)
        {
            string string1 = string.Format("jsapi_ticket={0}&noncestr={1}&timestamp={2}&url={3}", jsapi_ticket, noncestr, timestamp, url);
            return Sha1(string1);
        }

        /// <summary>
        /// sha1
        /// </summary>
        /// <param name="orgStr"></param>
        /// <param name="encode"></param>
        /// <returns></returns>
        private static string Sha1(string orgStr, string encode = "UTF-8")
        {
            var sha1 = new SHA1Managed();
            var sha1bytes = System.Text.Encoding.GetEncoding(encode).GetBytes(orgStr);
            byte[] resultHash = sha1.ComputeHash(sha1bytes);
            string sha1String = BitConverter.ToString(resultHash).ToLower();
            sha1String = sha1String.Replace("-", "");
            return sha1String;
        }

    }

}

二  webapi部分

ConfigController.cs

using SouthRuiHeH5.Models;
using SouthRuiHeH5.Provider;
using System;
using System.Configuration;
using System.Linq;
using System.Web.Http;
using System.Xml.Linq;

namespace SouthRuiHeH5.Controllers
{
    public class ConfigController : ApiController
    {
        public ConfigOutput Get([FromUri]string url)
        {
            string appId = ConfigurationManager.AppSettings.Get("AppId");
            string appSecret = ConfigurationManager.AppSettings.Get("AppSecret");

            if (string.IsNullOrWhiteSpace(appId)) throw new Exception("AppSeeting:AppId Missed");
            if (string.IsNullOrWhiteSpace(appSecret)) throw new Exception("AppSeeting:AppSecret Missed");


            string jsapiTicket = getJsapiTicketFromXml();
            if (string.IsNullOrEmpty(jsapiTicket))
            {
                string accessToken = GetAccessTokenFromXmlFirst();
                if (string.IsNullOrEmpty(accessToken)) accessToken = WechatJsSdk.GetAccessToken(appId, appSecret);
                if (string.IsNullOrEmpty(accessToken)) throw new Exception("Get AccessToken Error");
                jsapiTicket = WechatJsSdk.GetJsapiTicket(accessToken);
                if (string.IsNullOrEmpty(jsapiTicket)) throw new Exception("Get JsapiTicket Error");
            }

            ConfigOutput output = new ConfigOutput
            {
                appId = appId,
                nonceStr = WechatJsSdk.CreatenNonce_str(),
                timestamp = WechatJsSdk.CreatenTimestamp()
            };
            output.signature = WechatJsSdk.GetSignature(jsapiTicket, output.nonceStr, output.timestamp, url);
            return output;
        }

        /// <summary>
        /// 检查xml是否有JsapiTicket,并且JsapiTicket在有效期内
        /// </summary>
        /// <returns></returns>
        private string getJsapiTicketFromXml()
        {
            var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
            string filePath = mappedPath + "Xml/JsapiTicket.xml";
            XDocument inputDoc = XDocument.Load(filePath);

            string ticket = inputDoc.Elements().First().Element("ticket").Value;
            bool expiresInTry = int.TryParse(inputDoc.Elements().First().Element("expires_in").Value, out int expiresIn);
            bool createTimeTry = DateTime.TryParse(inputDoc.Elements().First().Element("create_time").Value, out DateTime createTime);

            if (!string.IsNullOrWhiteSpace(ticket) || !expiresInTry || !createTimeTry)
            {
                TimeSpan timeSpan = DateTime.Now.Subtract(createTime);
                if (timeSpan.TotalSeconds < expiresIn)
                    return ticket;
            }
            return string.Empty;
        }

        /// <summary>
        /// 检查xml是否有AccessToken,并且AccessToken在有效期内
        /// </summary>
        /// <returns></returns>
        private string GetAccessTokenFromXmlFirst()
        {
            var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
            string filePath = mappedPath + "Xml/AccessToken.xml";
            XDocument inputDoc = XDocument.Load(filePath);

            string accessToken = inputDoc.Elements().First().Element("access_token").Value;
            bool expiresInTry = int.TryParse(inputDoc.Elements().First().Element("expires_in").Value, out int expiresIn);
            bool createTimeTry = DateTime.TryParse(inputDoc.Elements().First().Element("create_time").Value, out DateTime createTime);

            if (!string.IsNullOrWhiteSpace(accessToken) || !expiresInTry || !createTimeTry)
            {
                TimeSpan timeSpan = DateTime.Now.Subtract(createTime);
                if (timeSpan.TotalSeconds < expiresIn)
                    return accessToken;
            }
            return string.Empty;

        }
    }
}

三 JS部分

wechat.share.js

var url = window.location.href.split('#')[0];

$.get("/api/Config?url=" + encodeURIComponent(url), function (res) {
    if (!res) return;
    var input = res;
    //input.debug = true;
    input.jsApiList = ["onMenuShareTimeline", "onMenuShareAppMessage"];
    wx.config(input);
});

wx.ready(function () {
    onMenuShareTimeline();
    onMenuShareAppMessage();
});

function onMenuShareTimeline() {
    wx.onMenuShareTimeline({
        title: '为态度喝彩!',
        desc: '唯有创造价值,才能共享价值。南方瑞合三年定开基金(LOF)盛大发行中。',
        link: url,
        imgUrl: 'http://southruihe.huiz.cn/image/sharelogo.jpg',
        success: function () { }
    });
}

function onMenuShareAppMessage() {
    wx.onMenuShareAppMessage({
        title: '为态度喝彩!',
        desc: '唯有创造价值,才能共享价值。南方瑞合三年定开基金(LOF)盛大发行中。',
        link: url,
        imgUrl: 'http://southruihe.huiz.cn/image/sharelogo.jpg',
        success: function () { }
    });
}

四 其中使用的3个数据传输类

AccessTokenOutput.cs

namespace SouthRuiHeH5.Models
{
    public class AccessTokenOutput
    {
        public string access_token { get; set; }
        public string expires_in { get; set; }
        public string errcode { get; set; }
        public string errmsg { get; set; }
    }
}

ConfigOutput.cs

namespace SouthRuiHeH5.Models
{
    public class ConfigOutput
    {
        /// <summary>
        /// 必填,公众号的唯一标识
        /// </summary>
        public string appId { get; set; }
        /// <summary>
        /// 必填,生成签名的时间戳
        /// </summary>
        public long timestamp { get; set; }
        /// <summary>
        /// 必填,生成签名的随机串
        /// </summary>
        public string nonceStr { get; set; }
        /// <summary>
        /// 必填,签名
        /// </summary>
        public string signature { get; set; }
    }
}

JsapiTicketOutput.cs

namespace SouthRuiHeH5.Models
{
    public class JsapiTicketOutput
    {
        public string errcode { get; set; }
        public string errmsg { get; set; }
        public string ticket { get; set; }
        public string expires_in { get; set; }
    }
}
原文地址:https://www.cnblogs.com/eedc/p/9521297.html