微信Token验证的——C#

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Amesteur/article/details/80304516
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Xml;

namespace Weixin
{
    /// <summary>
    /// 只适用于微信回调模式的请求
    /// </summary>
    public class WXCallBackService : IWXCallBack, IQYWXHandler
    {
        /// <summary>
        /// 当你开启应用的回调模式时,企业号会要求你填写应用的URL、Token、EncodingAESKey三个参数。
        /// URL是企业应用接收企业号推送请求的访问协议和地址,支持http或https协议。
        /// Token可由企业任意填写,用于生成签名。
        /// EncodingAESKey用于消息体的加密,是AES密钥的Base64编码。
        /// </summary>
        #region 自定义参数
        private string sToken { get { return System.Configuration.ConfigurationManager.AppSettings["Token"]; } }
        private string sCorpID  {get {return System.Configuration.ConfigurationManager.AppSettings["CorpID"];}}
        private string sEncodingAESKey {get{return System.Configuration.ConfigurationManager.AppSettings["EncodingAESKey"];}}
        #endregion
        #region 私有属性
        private HttpRequest Request;
        private string Response = string.Empty;
        public string wxResponse
        {
            get{return this.Response;}
        }
        private Tencent.WXBizMsgCrypt wxcpt { get; set; }
        #endregion 
        #region 构造函数
        /// <summary>
        /// 构造函数 赋予初始值
        /// </summary>
        /// <param name="wxRequest">微信的回调模式的请求</param>
        public WXCallBackService(HttpRequest wxRequest)
        {
            this.Request = wxRequest;
            Utils.LogHelper.Instance.Start(); //日志记录,记录请求的地址、以及解密的参数与返回的解密的密文
            Utils.LogHelper.Instance.WriteLog(this.Request.Url);
            #region 从web.config中获取微信企业号参数,并初始化微信加解密对象
            Utils.LogHelper.Instance.WriteLog("Token:" + this.sToken, Utils.LogType.Notice);
            Utils.LogHelper.Instance.WriteLog("EncodingAESKey:" + this.sEncodingAESKey, Utils.LogType.Notice);
            Utils.LogHelper.Instance.WriteLog("CorpID:" + this.sCorpID, Utils.LogType.Notice);
            this.wxcpt = new Tencent.WXBizMsgCrypt(this.sToken, this.sEncodingAESKey, this.sCorpID);
            #endregion

            if (wxRequest.HttpMethod.ToLower() == "get")
            {
                /*
                 * 微信请求方式为get时,表示需要验证唯一标识的服务器,我们需要按照微信的消息加解密的方法去解密,返回明文
                 * 也可以使用EchoStr参数是否设置,区分
                */
                this.Response = CheckSignature();
            }
            else
            {
                /*
                 * 当微信请求方式为post时.表示微信用户给公众号发送了消息
                 */
                string postString = string.Empty;
                using (Stream stream = this.Request.InputStream)
                {
                    Byte[] postBytes = new Byte[stream.Length];
                    stream.Read(postBytes, 0, (Int32)stream.Length);
                    postString = System.Text.Encoding.UTF8.GetString(postBytes);
                }

                if (!string.IsNullOrEmpty(postString))
                {
                    this.Response = responseMsg(postString);
                }
            }
        }
        #endregion    
        #region 微信回调模式的接口方法
        /// <summary>
        /// 验证微信Token的函数,一般新建一个企业应用只需验证一次
        /// </summary>
        /// <returns>echostr的明文字符串</returns>
        public string CheckSignature()
        {
            /*
------------验证回调URL---------------
*企业开启回调模式时,企业号会向验证url发送一个get请求 
假设点击验证时,企业收到类似请求:
* GET /cgi-bin/wxpush?msg_signature=5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3&timestamp=1409659589&nonce=263014780&echostr=P9nAzCzyDtyTWESHep1vC5X9xho%2FqYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp%2B4RPcs8TgAE7OaBO%2BFZXvnaqQ%3D%3D 
* HTTP/1.1 Host: qy.weixin.qq.com

* 接收到该请求时,企业应 1.解析出Get请求的参数,包括消息体签名(msg_signature),时间戳(timestamp),随机数字串(nonce)以及公众平台推送过来的随机加密字符串(echostr),
这一步注意作URL解码。
2.验证消息体签名的正确性 
3.解密出echostr原文,将原文当作Get请求的response,返回给公众平台
第2,3步可以用公众平台提供的库函数VerifyURL来实现。
*/
            #region 获取URL上面的参数msg_signature、timestamp、nonce、echostr
            string sVerifyMsgSig = this.Request.QueryString["msg_signature"];
            Utils.LogHelper.Instance.WriteLog("MsgSig:" + sVerifyMsgSig, Utils.LogType.Notice);
            string sVerifyTimeStamp = this.Request.QueryString["timestamp"];
            Utils.LogHelper.Instance.WriteLog("TimeStamp:" + sVerifyTimeStamp, Utils.LogType.Notice);
            string sVerifyNonce = this.Request.QueryString["nonce"];
            Utils.LogHelper.Instance.WriteLog("Nonce:" + sVerifyNonce, Utils.LogType.Notice);
            string sVerifyEchoStr = this.Request.QueryString["echostr"];
            Utils.LogHelper.Instance.WriteLog("Echostr:" + sVerifyEchoStr, Utils.LogType.Notice);
            #endregion
            int ret = 0;
            string sEchoStr = string.Empty;
            ret = wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, sVerifyEchoStr, ref sEchoStr);
            if (ret != 0)
            {
                Utils.LogHelper.Instance.WriteLog("output:" + "ERR: VerifyURL fail, ret: " + ret.ToString(), Utils.LogType.Error);
                return "ERR: VerifyURL fail, ret: " + ret.ToString();
            }
            Utils.LogHelper.Instance.WriteLog("output:" + this.Response, Utils.LogType.Notice);
            return sEchoStr;
        }
        /// <summary>
        /// 被动回复的消息处理函数
        /// </summary>
        /// <param name="postString">微信消息的密文</param>
        /// <returns>响应被动回复的处理消息</returns>
        public string responseMsg(string postString)
        {
            string sMsg = this.decodeMsg(postString);
            // ret==0表示解密成功,sMsg表示解密之后的明文xml串
            // TODO: 对明文的处理
            string sRegData = dispath(sMsg);
            return encodeMsg(sRegData);

        }
        #endregion
        #region 公共方法
        /// <summary>
        /// 获取当前时间的时间戳
        /// </summary>
        /// <returns>时间戳的string格式</returns>
        string getTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }
        /// <summary>
        /// 获取一定范围内的任意随机数
        /// </summary>
        /// <param name="minValue">最小范围</param>
        /// <param name="MaxValue">最大范围</param>
        /// <returns>随机数</returns>
        string GetRand(int minValue, int MaxValue)
        {
            Random rd = new Random();
            return rd.Next(minValue, MaxValue).ToString();
        }
        #endregion
        #region 企业微信的接口方法
        #region 企业号的消息加解密方法
        /// <summary>
        /// 企业号解密方法
        /// </summary>
        /// <param name="postString">微信post中的密文</param>
        /// <returns>解密得到的明文</returns>
        public string decodeMsg(string postString)
        {
            /*
            ------------对用户回复的消息解密---------------
            用户回复消息或者点击事件响应时,企业会收到回调消息,此消息是经过公众平台加密之后的密文以post形式发送给企业,密文格式请参考官方文档
            假设企业收到公众平台的回调消息如下:
            POST /cgi-bin/wxpush? msg_signature=477715d11cdb4164915debcba66cb864d751f3e6&timestamp=1409659813&nonce=1372623149 HTTP/1.1
            Host: qy.weixin.qq.com
            Content-Length: 613
            <xml> <ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName><Encrypt><![CDATA[RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==]]></Encrypt>
            <AgentID><![CDATA[218]]></AgentID>
            </xml>

            企业收到post请求之后应该 1.解析出url上的参数,包括消息体签名(msg_signature),时间戳(timestamp)以及随机数字串(nonce)
            2.验证消息体签名的正确性。
            3.将post请求的数据进行xml解析,并将<Encrypt>标签的内容进行解密,解密出来的明文即是用户回复消息的明文,明文格式请参考官方文档
            第2,3步可以用公众平台提供的库函数DecryptMsg来实现。
            */
            #region 获取URL上面的参数
            string sReqMsgSig = this.Request.QueryString["msg_signature"];
            Utils.LogHelper.Instance.WriteLog("MsgSig:" + sReqMsgSig, Utils.LogType.Notice);
            string sReqTimeStamp = this.Request.QueryString["timestamp"];
            Utils.LogHelper.Instance.WriteLog("TimeStamp:" + sReqTimeStamp, Utils.LogType.Notice);
            string sReqNonce = this.Request.QueryString["nonce"];
            Utils.LogHelper.Instance.WriteLog("Nonce:" + sReqNonce, Utils.LogType.Notice);
            #endregion

            // Post请求的密文数据
            string sReqData = postString;
            string sMsg = string.Empty;  // 解析之后的明文
            int ret = 0;
            ret = this.wxcpt.DecryptMsg(sReqMsgSig, sReqTimeStamp, sReqNonce, sReqData, ref sMsg);
            if (ret != 0)
            {
                Utils.LogHelper.Instance.WriteLog("output:" + "ERR: Decrypt Fail, ret: " + ret.ToString(), Utils.LogType.Error);
                return "ERR: Decrypt Fail, ret: " + ret;
            }
            Utils.LogHelper.Instance.WriteLog("output:" + sMsg, Utils.LogType.Notice);
            return sMsg;
        }
        /// <summary>
        /// 响应的XML格式数据加密成微信的密文
        /// </summary>
        /// <param name="sRespData">XML格式的字符串</param>
        /// <returns></returns>
        public string encodeMsg(string sRespData)
        {
            /*
------------使用示例三:企业回复用户消息的加密---------------
企业被动回复用户的消息也需要进行加密,并且拼接成密文格式的xml串。
假设企业需要回复用户的明文如下:
<xml>
<ToUserName><![CDATA[mycreate]]></ToUserName>
<FromUserName><![CDATA[wx5823bf96d3bd56c7]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
<AgentID>128</AgentID>
</xml>

为了将此段明文回复给用户,企业应: 1.自己生成时间时间戳(timestamp),随机数字串(nonce)以便生成消息体签名,也可以直接用从公众平台的post url上解析出的对应值。
2.将明文加密得到密文。 3.用密文,步骤1生成的timestamp,nonce和企业在公众平台设定的token生成消息体签名。 4.将密文,消息体签名,时间戳,随机数字串拼接成xml格式的字符串,发送给企业。
以上2,3,4步可以用公众平台提供的库函数EncryptMsg来实现。
*/
            // 需要发送的明文
            //string sRespData = "<xml><ToUserName><![CDATA[mycreate]]></ToUserName><FromUserName><![CDATA[wx582396d3bd56c7]]></FromUserName><CreateTime>1348831860</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[this is a test]]></Content><MsgId>1234567890123456</MsgId><AgentID>128</AgentID></xml>";
            string sEncryptMsg = ""; //xml格式的密文
            string sReqTimeStamp = getTimeStamp();
            string sReqNonce = GetRand(1000000000, 1999999999);
            int ret = 0;
            ret = wxcpt.EncryptMsg(sRespData, sReqTimeStamp, sReqNonce, ref sEncryptMsg);
            if (ret != 0)
            {
                Utils.LogHelper.Instance.WriteLog("output:" + "ERR: EncryptMsg Fail, ret: " + ret.ToString(), Utils.LogType.Error);
                return "ERR: EncryptMsg Fail, ret: " + ret;
            }
            // TODO:
            // 加密成功,企业需要将加密之后的sEncryptMsg返回
            Utils.LogHelper.Instance.WriteLog("output:" + sEncryptMsg, Utils.LogType.Notice);
            return sEncryptMsg;
        }
        #endregion
        /// <summary>
        /// 微信消息分发函数
        /// </summary>
        /// <param name="sMsg">通过微信解密出来的明文</param>
        /// <returns></returns>
        public string dispath(string sMsg)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(sMsg);
            XmlNode root = doc.FirstChild;
            string R_Type = root["MsgType"].InnerText;
            string result = string.Empty;
            switch (R_Type)
            {
                    //事件类型
                case "event":
                    result = receiveEvent(root);
                    break;
                case "text":
                    result = receiveText(root);
                    break;
                case "image":
                    result = receiveImage(root);
                    break;
                case "location":
                    result = receiveLocation(root);
                    break;
                case "voice":
                    result = receiveVoice(root);
                    break;
                case "video":
                    result = receiveVideo(root);
                    break;
                case "link":
                    result = receiveLink(root);
                    break;
                    //小视频类型
                case "shortvideo":
                    result = receiveShortVideo(root);
                    break;
                default:
                    break;
            }
            return result;
        }
        /// <summary>
        /// 获取微信服务器的IP端,为了信息安全,可以提高安全性能
        /// </summary>
        /// <param name="AccessToken"></param>
        /// <returns></returns>
        /// TODO 实现这个功能
        public string getIPSegment(string AccessToken)
        {
            return "";
        }
        #endregion
        #region 接收消息处理的方法————需要自己定义消息内容
        #region 微信被动回复API的接口方法
        /// <summary>
        /// 接收Event类型的消息处理方法
        /// </summary>
        /// <param name="xmlString">明文消息</param>
        /// <returns>处理后响应消息</returns>
        public string receiveEvent(XmlNode xmlString)
        {
            string EventType = xmlString["Event"].InnerText;
            string Content = string.Empty;
            switch (EventType) 
            {
                    //成员关注、取消事件
                case "subscribe":
                    TextClass TextMsg = new TextClass(xmlString);
                    TextMsg.Content = "欢迎加入我们!";
                    Content = transmitText(TextMsg);
                    break;
                //上报地理位置事件
                case "LOCATION":
                    break;
                //点击菜单拉取消息的事件推送
                case "click":
                    break;
                    //TODO 添加其他事件
                default:
                    break;
            }
            return Content;
        }
        /// <summary>
        /// 接收Text类型的消息处理方法
        /// </summary>
        /// <param name="XmlString"></param>
        /// <returns>处理后响应消息</returns>
        /// 
        public string receiveText(XmlNode XmlString)
        {
            string keyword = XmlString["Content"].InnerText.Trim();
            string Content = string.Empty;
            switch (keyword)
            {
                case "新闻消息":
                    NewClass News = new NewClass(XmlString);
                    News.Articles.Add(new ArticleEntity("https://www.baidu.com"));
                    Content = tramsmitNew(News);
                    break;
                default:
                    TextClass TextMsg = new TextClass(XmlString);
                    TextMsg.Content = System.DateTime.Now.ToString() + "别瞎逼逼!";
                    Content = transmitText(TextMsg);
                    break;
            }
            return Content;
        }
        /// <summary>
        /// 接收shortvedio类型的消息处理方法
        /// </summary>
        /// <param name="xmlString">明文消息</param>
        /// <returns>处理后响应消息</returns>
        public string receiveShortVideo(XmlNode XmlString)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 接收link类型的消息处理方法
        /// </summary>
        /// <param name="xmlString">明文消息</param>
        /// <returns>处理后响应消息</returns>
        public string receiveLink(XmlNode XmlString)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 接收vedio类型的消息处理方法
        /// </summary>
        /// <param name="xmlString">明文消息</param>
        /// <returns>处理后响应消息</returns>
        public string receiveVideo(XmlNode XmlString)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 接收voice类型的消息处理方法
        /// </summary>
        /// <param name="xmlString">明文消息</param>
        /// <returns>处理后响应消息</returns>
        public string receiveVoice(XmlNode XmlString)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 接收location类型的消息处理方法
        /// </summary>
        /// <param name="xmlString">明文消息</param>
        /// <returns>处理后响应消息</returns>
        public string receiveLocation(XmlNode XmlString)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 接收image类型的消息处理方法
        /// </summary>
        /// <param name="xmlString">明文消息</param>
        /// <returns>处理后响应消息</returns>
        public string receiveImage(XmlNode XmlString)
        {
            throw new NotImplementedException();
        }
        #endregion
        #region 打包消息方法
        public string transmitText(TextClass Content)
        {
            if (Content == null)
                return "";
            return Content.ToXml();
        }
        public string tramsmitNew(NewClass Content)
        {
            if (Content == null)
                return "";
            return Content.ToXml();
        }
        #endregion
        #endregion

    }
  
}
展开阅读全文

没有更多推荐了,返回首页