JeffreySu / WeiXinMPSDK

微信全平台 .NET SDK, Senparc.Weixin for C#,支持 .NET Framework 及 .NET Core、.NET 8.0。已支持微信公众号、小程序、小游戏、微信支付、企业微信/企业号、开放平台、JSSDK、微信周边等全平台。 WeChat SDK for C#.
https://weixin.senparc.com
Apache License 2.0
8.42k stars 4.35k forks source link

微信开放平台的被授权的服务号Access Token过期自动更新机制失效,仍然会返回过期的Access Token #409

Closed CooperLiu closed 5 years ago

CooperLiu commented 7 years ago
问题描述

项目背景: 使用微信公众平台授权给微信开放平台,进行多公众号授权运营。 我尝试使用以下方法获取授权的公众号的AccessToken,发现不会自动管理AccessToken的过期时间

获取授权公众号Access Token方法

发现在此出更新时,不会更新局部变量authorizerBag

是不是应该吧局部变量authorizerBag 传入TryUpdateAuthorizationInfo?

发现问题的模块
模块对应的.net版本
开发环境
缓存环境
JeffreySu commented 7 years ago

谢谢反馈!

在TryUpdateAuthorizationInfo方法内,authorizerBag会被重新获取一次:

https://github.com/JeffreySu/WeiXinMPSDK/blob/1f71194a4ca6bb6bb8717f6cf54500bd355e05cc/src/Senparc.Weixin.Open/Senparc.Weixin.Open/Containers/AuthorizerContainer.cs#L382

最终在这里是有更新的: https://github.com/JeffreySu/WeiXinMPSDK/blob/1f71194a4ca6bb6bb8717f6cf54500bd355e05cc/src/Senparc.Weixin.Open/Senparc.Weixin.Open/Containers/AuthorizerContainer.cs#L389

并且这里也设置了过期时间: https://github.com/JeffreySu/WeiXinMPSDK/blob/1f71194a4ca6bb6bb8717f6cf54500bd355e05cc/src/Senparc.Weixin.Open/Senparc.Weixin.Open/Containers/AuthorizerContainer.cs#L392

代码上来看应该是对的,不知道您那边是否方便输出一下日志?比如实际每次变更的过期时间等等?

CooperLiu commented 7 years ago

日志:

2017-02-16 14:00:00.5566 Zmind.Scrm.WeChat.WechatMessageService 1.进入开放平台:[wx2a10323fe09421ab],获取授权公众号:[wx9dc86883b02dc326],Access Token。

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

2017-02-16 14:00:00.6191 Zmind.Scrm.WeChat.WechatMessageService 2.获取缓存授权信息:{"AuthorizerAppId":"wx9dc86883b02dc326","ComponentAppId":"wx2a10323fe09421ab","FullAuthorizerInfoResult":{"authorizer_info":{"nick_name":"耐洗","head_img":"http://wx.qlogo.cn/mmopen/ChfjRafShpzZE8GhePOL8OLlqYEl4FZRE5l6NDib2MDQqTszxp4LEgwls2RcSpkoXQQtkG0Cw233KzpDLfmWnF733znW7Uicjd/0","service_type_info":{"id":2},"verify_type_info":{"id":0},"user_name":"gh_dbd09d529d05","alias":"naixi_001","qrcode_url":"http://mmbiz.qpic.cn/mmbiz_jpg/Wh112kEEqDib7MTSxv9YofVxbIiaVlCvBZonMf7icNkSlhHYU6uRCLK04n7mWnw5nEJMSY5fbeT5Igl9UEgZNCAbQ/0","business_info":{"open_pay":0,"open_shake":0,"open_card":0,"open_store":1}},"authorization_info":{"authorizer_appid":"wx9dc86883b02dc326","authorizer_access_token":"tw4v875q--D9d3e042n4I4UY7PMNwsYWThlDq9AxjRsNIl03g9koTvkmzQtMroaYwS3WWZN-7g2J-7mhoU0zdyC-r3iWu_Tt22cO5V3Dp3bgxAYWtUwqe9KzbYMYXx2kFQNgAGDTEB","expires_in":7200,"authorizer_refresh_token":"refreshtoken@@@zWIqOBbkdaUOgXXVo5c-A_iLcVMMk8-gtVR8d6Teqsg","func_info":[{"funcscope_category":{"id":1}},{"funcscope_category":{"id":15}},{"funcscope_category":{"id":4}},{"funcscope_category":{"id":7}},{"funcscope_category":{"id":2}},{"funcscope_category":{"id":3}},{"funcscope_category":{"id":11}},{"funcscope_category":{"id":6}},{"funcscope_category":{"id":5}},{"funcscope_category":{"id":8}},{"funcscope_category":{"id":13}},{"funcscope_category":{"id":9}},{"funcscope_category":{"id":10}},{"funcscope_category":{"id":12}}]},"errcode":0,"errmsg":null,"P2PData":null},"JsApiTicketResult":{"ticket":null,"expires_in":0,"errcode":0,"errmsg":null,"P2PData":null},"JsApiTicketExpireTime":"0001-01-01T00:00:00+08:00","AuthorizationInfo":{"authorizer_appid":"wx9dc86883b02dc326","authorizer_access_token":"tw4v875q--D9d3e042n4I4UY7PMNwsYWThlDq9AxjRsNIl03g9koTvkmzQtMroaYwS3WWZN-7g2J-7mhoU0zdyC-r3iWu_Tt22cO5V3Dp3bgxAYWtUwqe9KzbYMYXx2kFQNgAGDTEB","expires_in":7200,"authorizer_refresh_token":"refreshtoken@@@zWIqOBbkdaUOgXXVo5c-A_iLcVMMk8-gtVR8d6Teqsg","func_info":[{"funcscope_category":{"id":1}},{"funcscope_category":{"id":15}},{"funcscope_category":{"id":4}},{"funcscope_category":{"id":7}},{"funcscope_category":{"id":2}},{"funcscope_category":{"id":3}},{"funcscope_category":{"id":11}},{"funcscope_category":{"id":6}},{"funcscope_category":{"id":5}},{"funcscope_category":{"id":8}},{"funcscope_category":{"id":13}},{"funcscope_category":{"id":9}},{"funcscope_category":{"id":10}},{"funcscope_category":{"id":12}}]},"AuthorizationInfoExpireTime":"2017-02-16T11:47:45.1191573+08:00","AuthorizerInfo":{"nick_name":"耐洗","head_img":"http://wx.qlogo.cn/mmopen/ChfjRafShpzZE8GhePOL8OLlqYEl4FZRE5l6NDib2MDQqTszxp4LEgwls2RcSpkoXQQtkG0Cw233KzpDLfmWnF733znW7Uicjd/0","service_type_info":{"id":2},"verify_type_info":{"id":0},"user_name":"gh_dbd09d529d05","alias":"naixi_001","qrcode_url":"http://mmbiz.qpic.cn/mmbiz_jpg/Wh112kEEqDib7MTSxv9YofVxbIiaVlCvBZonMf7icNkSlhHYU6uRCLK04n7mWnw5nEJMSY5fbeT5Igl9UEgZNCAbQ/0","business_info":{"open_pay":0,"open_shake":0,"open_card":0,"open_store":1}},"Name":null,"Key":"wx9dc86883b02dc326","CacheTime":"2017-02-16T09:57:45.5566323+08:00"}

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

2017-02-16 14:00:00.6191 Zmind.Scrm.WeChat.WechatMessageService 3.发现授权信息已经过期,过期时间:2017/2/16 11:47:45。

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

2017-02-16 14:00:00.6659 Zmind.Scrm.WeChat.WechatMessageService 4.重新(TryGetAuthorizerAccessToken)获取授权的Token:tw4v875q--D9d3e042n4I4UY7PMNwsYWThlDq9AxjRsNIl03g9koTvkmzQtMroaYwS3WWZN-7g2J-7mhoU0zdyC-r3iWu_Tt22cO5V3Dp3bgxAYWtUwqe9KzbYMYXx2kFQNgAGDTEB,缓存中的Token:tw4v875q--D9d3e042n4I4UY7PMNwsYWThlDq9AxjRsNIl03g9koTvkmzQtMroaYwS3WWZN-7g2J-7mhoU0zdyC-r3iWu_Tt22cO5V3Dp3bgxAYWtUwqe9KzbYMYXx2kFQNgAGDTEB,是否一致:True

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

2017-02-16 14:00:03.9160 Zmind.Scrm.WeChat.WechatMessageService 5.重新调用RefreshAPI获取AccessToken:4fx5SPFpvuaQaU77h2nOZcUW6lGGfC-hnjZ-14AFy1emPxj_rkEtaTo11o_T3s9z3AEE5odh5JVeYR2pfqcJkkWJ619CMmdd14wenQE8KGvpxEz5IBNADsGVqVOObPWpRJCjALDKCV

================================================= 【WechatOpenAccount.AppId】开放平台ComponentAppId ,【appId】授权公众号AuthorizerAppId

`Logger.Info($"1.进入开放平台:[{WechatOpenAccount.AppId}],获取授权公众号:[{appId}],Access Token。");

            var cacheObj = AuthorizerContainer.TryGetItem(appId);

            Logger.Info($"2.获取缓存授权信息:{cacheObj.ToJsonString()}");

            if (cacheObj.AuthorizationInfoExpireTime <= DateTime.Now)
            {
                Logger.Info($"3.发现授权信息已经过期,过期时间:{cacheObj.AuthorizationInfoExpireTime}。");

                var token = AuthorizerContainer.TryGetAuthorizerAccessToken(WechatOpenAccount.AppId, appId);

                var isSame = token == cacheObj.AuthorizationInfo.authorizer_access_token;

                Logger.Info($"4.重新(TryGetAuthorizerAccessToken)获取授权的Token:{token}" +
                            $",缓存中的Token:{cacheObj.AuthorizationInfo.authorizer_access_token}" +
                            $",是否一致:{isSame}");

                if (isSame)
                {

                    var componentAppId = WechatOpenAccount.AppId;
                    var componentVerifyTicket = ComponentContainer.TryGetComponentVerifyTicket(componentAppId);
                    var componentAccessToken = ComponentContainer.GetComponentAccessToken(componentAppId, componentVerifyTicket);

                    //获取新的AuthorizerAccessToken
                    var refreshToken = ComponentContainer.GetAuthorizerRefreshTokenFunc(appId);
                    var refreshResult = AuthorizerContainer.RefreshAuthorizerToken(componentAccessToken, componentAppId, appId,
                        refreshToken);

                    cacheObj.AuthorizationInfo.authorizer_access_token = refreshResult.authorizer_access_token;
                    cacheObj.AuthorizationInfo.authorizer_refresh_token = refreshResult.authorizer_refresh_token;
                    cacheObj.AuthorizationInfo.expires_in = refreshResult.expires_in;
                    cacheObj.AuthorizationInfoExpireTime = ApiUtility.GetExpireTime(refreshResult.expires_in);

                    AuthorizerContainer.TryUpdateAuthorizationInfo(componentAppId, appId,
                        refreshResult.authorizer_access_token, refreshResult.authorizer_refresh_token,
                        refreshResult.expires_in);

                    Logger.Info($"5.重新调用RefreshAPI获取AccessToken:{refreshResult.authorizer_access_token}");

                }
            }

            Logger.Info($"授权服务号:{cacheObj.FullAuthorizerInfoResult.authorizer_info.nick_name}" +
                        $",AppId:[{appId}]" +
                        $",获取APIToken:{cacheObj.AuthorizationInfo.authorizer_access_token}" +
                        $",有效期:{cacheObj.AuthorizationInfoExpireTime}。");

            return cacheObj.AuthorizationInfo.authorizer_access_token;`
JeffreySu commented 7 years ago

不好意思,看了两遍,没有明白这个和您说的不能更新缓存有什么联系?从哪一条日志可以看出来呢?

CooperLiu commented 7 years ago

我理解的AuthorizerContainer.TryGetAuthorizerAccessToken方法应该是:如果缓存的AuthorizerAccessToken过期,会自动调用RefreshToken接口来更新AccessToken,然后更新缓存的AuthorizerAccessToken的过期时间。

我原来实现的只是调用了 AuthorizerContainer.TryGetAuthorizerAccessToken(WechatOpenAccount.AppId, appId); 此方法来获取AuthorizerAccessToken,但是每次都还是获取到已经过期的AccessToken,后来,加上日志查看,发现缓存里的AuthorizerAccessToken就算过期也未更新,不知道是不是issues?还是我对此理解的不够?

CooperLiu commented 7 years ago

贴出来的Code中的IsSame 是比较缓存中的Token,和调用AuthorizerContainer.TryGetAuthorizerAccessToken(WechatOpenAccount.AppId, appId); 之后,获取到Token是否一致,结果是一致的,而且都是过期的

第四条日志:

2017-02-16 14:00:00.6659 Zmind.Scrm.WeChat.WechatMessageService 4.重新(TryGetAuthorizerAccessToken)获取授权的Token:tw4v875q--D9d3e042n4I4UY7PMNwsYWThlDq9AxjRsNIl03g9koTvkmzQtMroaYwS3WWZN-7g2J-7mhoU0zdyC-r3iWu_Tt22cO5V3Dp3bgxAYWtUwqe9KzbYMYXx2kFQNgAGDTEB,缓存中的Token:tw4v875q--D9d3e042n4I4UY7PMNwsYWThlDq9AxjRsNIl03g9koTvkmzQtMroaYwS3WWZN-7g2J-7mhoU0zdyC-r3iWu_Tt22cO5V3Dp3bgxAYWtUwqe9KzbYMYXx2kFQNgAGDTEB,是否一致:True

JeffreySu commented 7 years ago

我是不是能这样理解:你的用意是判断更新之后的Token,和更新之前的是否相同,结果是相同的,所以就可以确定缓存没有被更新?

这里面有几种可能我大致分析一下:

缓存更新队列原理

由于提高分布式缓存读写(主要是写)效率,我们提供了一个队列来处理缓存自动更新,因此实际上是有一个微小的时间差的(平均1秒)。当然我们也提供了强制立即更新的方法。

因此如果要测试是否真的更新,建议在更新之后让线程停止2秒。

假设已经这么做,那么:

对于本地缓存

上述更改是有效的

对于Redis

cacheObj 在判断isSame之前需要重新获取一次,因为Reids中的对象是序列化后储存的,更新之后的实例是不一样的。

CooperLiu commented 7 years ago

谢谢回复。 我使用的Redis的缓存组件。简单说我的初衷是使用TryGetAuthorizerAccessToken来获取授权公众公众号的apiAccessToken,但是实际应用下来(在Token过期时),获取到的Token仍然为过期的Token。所以,问题是如何简单的获取到授权公众号apiAccessToken,让Sdk来管理过期机制?

zixinw commented 6 years ago

@JeffreySu 我的理解: https://github.com/JeffreySu/WeiXinMPSDK/blob/1f71194a4ca6bb6bb8717f6cf54500bd355e05cc/src/Senparc.Weixin.Open/Senparc.Weixin.Open/Containers/AuthorizerContainer.cs#L272 这里的 AuthorizationInfo属性未被更新,所以拿到的authorizer_access_token仍是过期的。

JeffreySu commented 6 years ago

嗯,有道理,我跟进看一下,谢谢!

JeffreySu commented 6 years ago

@wangzixin1113 @CooperLiu 已经更新到 Open v2.8.3(master分支下) 请查看是否有解决问题。欢迎反馈,谢谢!