• 微信公众号开发之access_token的全局共用


    最近做微信公众号开发,涉及到access_token的缓存问题(避免各自的应用都去取access_token,同时解决微信 appid和appsecret的安全问题),在通用权限管理系统底层增加了实现方法:

    (access_token默认2小时过期,每取一次,上一次的就自动失效,每天取的次数有限制)

    //-----------------------------------------------------------------
    // 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方式实现以后,同步微信后台用户数据正常了。

    ==============

    上面第一个方法可以作为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;
                }
            }
        }
    }
  • 相关阅读:
    UVa 658 (Dijkstra) It's not a Bug, it's a Feature!
    CodeForces Round #288 Div.2
    UVa 540 (团体队列) Team Queue
    UVa 442 (栈) Matrix Chain Multiplication
    CodeForces Round #287 Div.2
    CodeForces Round #286 Div.2
    CodeForces Round #285 Div.2
    UVa 12096 (STL) The SetStack Computer
    UVa 101 (模拟) The Blocks Problem
    UVa 12171 (离散化 floodfill) Sculpture
  • 原文地址:https://www.cnblogs.com/hnsongbiao/p/6069963.html
Copyright © 2020-2023  润新知