微信公衆號開發之access_token的全局共用

最近作微信公衆號開發,涉及到access_token的緩存問題(避免各自的應用都去取access_token,同時解決微信 appid和appsecret的安全問題),在通用權限管理系統底層增長了實現方法:redis

(access_token默認2小時過時,每取一次,上一次的就自動失效,天天取的次數有限制)json

//-----------------------------------------------------------------
// All Rights Reserved , Copyright (C) 2016 , Hairihan TECH, Ltd.  
//-----------------------------------------------------------------

using System;
using System.Net;
using System.Text;
using System.Web.Script.Serialization;

namespace DotNet.Business.HttpUtilities
{
    using DotNet.Utilities;

    /// <summary>
    /// WeChatUtilities 
    /// 微信公共服務,遠程微信調用接口
    ///
    /// 修改記錄
    ///
    ///        2016.11.16 版本:1.0 SongBiao  遠程調用服務。
    ///
    /// <author>
    ///        <name>SongBiao</name>
    ///        <date>2016.11.16</date>
    /// </author>
    /// </summary>
    public class WeChatUtilities
    {
        /// <summary>
        /// 獲取微信AccessToken
        /// Redis全局緩存,過時自動獲取
        /// 對應於公衆號是全局惟一的票據,重複獲取將致使上次獲取的access_token失效
        /// </summary>
        /// <returns></returns>
        public static string GetAccessToken()
        {
            string key = "WXAccessToken";
            string accessToken = string.Empty;
            DateTime expiresAt = DateTime.Now;
            using (var redisClient = PooledRedisHelper.GetTokenClient())
            {
                AccessTokenResult tokenResult = redisClient.Get<AccessTokenResult>(key);
                // 不存在或者已過時
                if (tokenResult == null || (tokenResult != null && DateTime.Now > tokenResult.expiresAt))
                {
                    JavaScriptSerializer js = new JavaScriptSerializer();
                    string appId = BaseSystemInfo.WeiXinAppId;
                    string appSecret = BaseSystemInfo.WeiXinAppSecret;
                    var url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appId, appSecret);
                    using (WebClient wc = new WebClient())
                    {
                        wc.Proxy = null;
                        wc.Encoding = Encoding.UTF8;
                        string returnText = wc.DownloadString(url);
                        if (returnText.Contains("errcode"))
                        {
                            //可能發生錯誤
                            //可能發生錯誤
                            WxJsonResult errorResult = js.Deserialize<WxJsonResult>(returnText);
                            if (errorResult.errcode != 0)
                            {
                                //發生錯誤
                                throw new Exception(string.Format("微信請求發生錯誤!錯誤代碼:{0},說明:{1}",
                                    (int)errorResult.errcode, errorResult.errmsg));
                            }
                        }
                        tokenResult = js.Deserialize<AccessTokenResult>(returnText);
                        // 添加到緩存中 減小10秒 避免一些問題
                        expiresAt = DateTime.Now.AddSeconds(tokenResult.expires_in);
                        tokenResult.expiresAt = expiresAt;
                        redisClient.Set(key, tokenResult, expiresAt);
                        NLogHelper.Trace(DateTime.Now + ",微信accessToken過時,從新獲取,下次過時時間:" + expiresAt);
                    }
                }
                accessToken = tokenResult.access_token;
            }

            return accessToken;
        }

        #region 微信公用
        /// <summary>
        /// 微信接口
        /// </summary>
        interface IJsonResult
        {
            string errmsg { get; set; }
            object P2PData { get; set; }
        }
        /// <summary>
        /// 公衆號返回碼(JSON)
        /// 應該改名爲ReturnCode_MP,但爲減小項目中的修改,此處依舊用ReturnCode命名
        /// </summary>
        enum ReturnCode
        {
            系統繁忙此時請開發者稍候再試 = -1,
            請求成功 = 0,
            獲取access_token時AppSecret錯誤或者access_token無效 = 40001,
            不合法的憑證類型 = 40002,
            不合法的OpenID = 40003,
            不合法的媒體文件類型 = 40004,
            不合法的文件類型 = 40005,
            不合法的文件大小 = 40006,
            不合法的媒體文件id = 40007,
            不合法的消息類型 = 40008,
            不合法的圖片文件大小 = 40009,
            不合法的語音文件大小 = 40010,
            不合法的視頻文件大小 = 40011,
            不合法的縮略圖文件大小 = 40012,
            不合法的APPID = 40013,
            不合法的access_token = 40014,
            不合法的菜單類型 = 40015,
            不合法的按鈕個數1 = 40016,
            不合法的按鈕個數2 = 40017,
            不合法的按鈕名字長度 = 40018,
            不合法的按鈕KEY長度 = 40019,
            不合法的按鈕URL長度 = 40020,
            不合法的菜單版本號 = 40021,
            不合法的子菜單級數 = 40022,
            不合法的子菜單按鈕個數 = 40023,
            不合法的子菜單按鈕類型 = 40024,
            不合法的子菜單按鈕名字長度 = 40025,
            不合法的子菜單按鈕KEY長度 = 40026,
            不合法的子菜單按鈕URL長度 = 40027,
            不合法的自定義菜單使用用戶 = 40028,
            不合法的oauth_code = 40029,
            不合法的refresh_token = 40030,
            不合法的openid列表 = 40031,
            不合法的openid列表長度 = 40032,
            不合法的請求字符不能包含uxxxx格式的字符 = 40033,
            不合法的參數 = 40035,
            不合法的請求格式 = 40038,
            不合法的URL長度 = 40039,
            不合法的分組id = 40050,
            分組名字不合法 = 40051,
            缺乏access_token參數 = 41001,
            缺乏appid參數 = 41002,
            缺乏refresh_token參數 = 41003,
            缺乏secret參數 = 41004,
            缺乏多媒體文件數據 = 41005,
            缺乏media_id參數 = 41006,
            缺乏子菜單數據 = 41007,
            缺乏oauth_code = 41008,
            缺乏openid = 41009,
            access_token超時 = 42001,
            refresh_token超時 = 42002,
            oauth_code超時 = 42003,
            須要GET請求 = 43001,
            須要POST請求 = 43002,
            須要HTTPS請求 = 43003,
            須要接收者關注 = 43004,
            須要好友關係 = 43005,
            多媒體文件爲空 = 44001,
            POST的數據包爲空 = 44002,
            圖文消息內容爲空 = 44003,
            文本消息內容爲空 = 44004,
            多媒體文件大小超過限制 = 45001,
            消息內容超過限制 = 45002,
            標題字段超過限制 = 45003,
            描述字段超過限制 = 45004,
            連接字段超過限制 = 45005,
            圖片連接字段超過限制 = 45006,
            語音播放時間超過限制 = 45007,
            圖文消息超過限制 = 45008,
            接口調用超過限制 = 45009,
            建立菜單個數超過限制 = 45010,
            回覆時間超過限制 = 45015,
            系統分組不容許修改 = 45016,
            分組名字過長 = 45017,
            分組數量超過上限 = 45018,
            不存在媒體數據 = 46001,
            不存在的菜單版本 = 46002,
            不存在的菜單數據 = 46003,
            解析JSON_XML內容錯誤 = 47001,
            api功能未受權 = 48001,
            用戶未受權該api = 50001,
            參數錯誤invalid_parameter = 61451,
            無效客服帳號invalid_kf_account = 61452,
            客服賬號已存在kf_account_exsited = 61453,
            /// <summary>
            /// 客服賬號名長度超過限制(僅容許10個英文字符,不包括@及@後的公衆號的微信號)(invalid kf_acount length)
            /// </summary>
            客服賬號名長度超過限制 = 61454,
            /// <summary>
            /// 客服賬號名包含非法字符(僅容許英文+數字)(illegal character in kf_account)
            /// </summary>
            客服賬號名包含非法字符 = 61455,
            /// <summary>
            ///      客服賬號個數超過限制(10個客服帳號)(kf_account count exceeded)
            /// </summary>
            客服賬號個數超過限制 = 61456,
            無效頭像文件類型invalid_file_type = 61457,
            系統錯誤system_error = 61450,
            日期格式錯誤 = 61500,
            日期範圍錯誤 = 61501,

            //新加入的一些類型,如下文字根據P2P項目格式組織,非官方文字
            發送消息失敗_48小時內用戶未互動 = 10706,
            發送消息失敗_該用戶已被加入黑名單_沒法向此發送消息 = 62751,
            發送消息失敗_對方關閉了接收消息 = 10703,
            對方不是粉絲 = 10700
        }

        /// <summary>
        /// 返回接口
        /// </summary>
        interface IWxJsonResult : IJsonResult
        {
            ReturnCode errcode { get; set; }
        }
        /// <summary>
        /// 公衆號JSON返回結果(用於菜單接口等)
        /// </summary>
        [Serializable]
        class WxJsonResult : IWxJsonResult
        {
            public ReturnCode errcode { get; set; }
            public string errmsg { get; set; }
            /// <summary>
            /// 爲P2P返回結果作準備
            /// </summary>
            public virtual object P2PData { get; set; }
        }
        #endregion

    }
    /// <summary>
    /// access_token請求後的JSON返回格式
    /// </summary>
    [Serializable]
    public class AccessTokenResult
    {
        /// <summary>
        /// 獲取到的憑證
        /// </summary>
        public string access_token { get; set; }
        /// <summary>
        /// 憑證有效時間,單位:秒
        /// </summary>
        public int expires_in { get; set; }

        /// <summary>
        /// 憑證過時有效時間
        /// </summary>
        public DateTime expiresAt { get; set; }
    }

}

 

 

 

經過緩存access_token方式實現之後,同步微信後臺用戶數據正常了。api

 

==============緩存

上面第一個方法能夠做爲C#開發的同窗的獲取AccessToken的公共方法,由於還有其餘語言開發的同窗,因此在這裏又增長了一個獲取AccessToken的對外接口安全

//-----------------------------------------------------------------------
// <copyright file="WeChatService.ashx" company="Hairihan">
//     Copyright (C) 2016 , All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace DotNet.UserCenter
{
    using DotNet.Business.HttpUtilities;
    using DotNet.Utilities;

    /// <summary>
    /// WeChatService
    /// 
    /// 修改記錄
    /// 
    /// 
    /// 2016-11-17 版本:1.0 SongBiao 建立
    /// 
    /// <author>
    ///     <name>SongBiao</name>
    ///     <date>2016-11-17</date>
    /// </author>
    /// </summary>
    public class WeChatService : IHttpHandler
    {
        /// <summary>
        /// 獲取服務器時間
        /// </summary>
        /// <param name="context"></param>
        private void GetServerDateTime(HttpContext context)
        {
            JsonResult<string> jsonResult = new JsonResult<string>()
            {
                Status = true,
                StatusMessage = "成功獲取服務器時間",
                Data = DateTime.Now.ToString(BaseSystemInfo.DateTimeFormat)
            };
            context.Response.Write(jsonResult.ToJson());
        }
        /// <summary>
        /// 獲取用戶中心庫時間
        /// </summary>
        /// <param name="context"></param>
        private void GetDbDateTime(HttpContext context)
        {
            JsonResult<string> jsonResult = new JsonResult<string>();
            try
            {
                using (IDbHelper dbHelper = DbHelperFactory.GetHelper(BaseSystemInfo.UserCenterDbType, BaseSystemInfo.UserCenterDbConnection))
                {
                    string result = DateTime.Parse(dbHelper.GetDbDateTime()).ToString(BaseSystemInfo.DateTimeFormat);
                    jsonResult.Status = true;
                    jsonResult.StatusMessage = "成功獲取用戶中心庫時間";
                    jsonResult.Data = result;
                }
            }
            catch (Exception ex)
            {
                jsonResult.Status = true;
                jsonResult.StatusMessage = "獲取用戶中心庫時間異常:" + ex.Message;
                NLogHelper.Trace(ex, "UserService GetDbDateTime 異常");
            }
            context.Response.Write(jsonResult.ToJson());
        }

        /// <summary>
        /// 獲取AccessToken
        /// </summary>
        /// <param name="context"></param>
        private void GetAccessToken(HttpContext context)
        {
            BaseResult baseResult = new BaseResult();
            try
            {
                string accessToken = WeChatUtilities.GetAccessToken();
                if (!string.IsNullOrWhiteSpace(accessToken))
                {
                    baseResult.Status = true;
                    baseResult.ResultValue = accessToken;
                    baseResult.StatusMessage = Status.OK.GetDescription();
                }
                else
                {
                    baseResult.Status = false;
                    baseResult.StatusMessage = "AccessToken獲取失敗。";
                }
            }
            catch (Exception ex)
            {
                baseResult.Status = false;
                baseResult.StatusMessage = "AccessToken獲取異常:"+ex.Message;
            }

            context.Response.Write(baseResult.ToJson());
        }

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            if (context.Request["function"] == null)
            {
                this.GetServerDateTime(context);
            }
            else
            {
                string function = context.Request["function"];
                if (function.Equals("GetServerDateTime", StringComparison.OrdinalIgnoreCase))
                {
                    this.GetServerDateTime(context);
                }
                else if (function.Equals("GetAccessToken", StringComparison.OrdinalIgnoreCase))
                {
                    this.GetAccessToken(context);
                }
                else
                {
                    context.Response.Write(BaseResult.Error("function對應方法不存在。").ToJson());
                }

                context.Response.End();
            }
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}
相關文章
相關標籤/搜索