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.43k stars 4.35k forks source link

ComponentContainer 不能注册多个第三方应用 #543

Closed ytpos closed 7 years ago

ytpos commented 7 years ago

( 此版块专为反馈bug及提交需求服务,不负责解答开发问题,请勿发表开发问题, 如果您需要这方面的帮助,请移步问答社区http://weixin.senparc.com/QA )

问题描述

///

/// 注册应用凭证信息,此操作只是注册,不会马上获取Token,并将清空之前的Token, /// /// /// /// 获取ComponentVerifyTicket的方法 /// 从数据库中获取已存的AuthorizerAccessToken的方法 /// AuthorizerAccessToken更新后的回调 /// 标记Authorizer名称(如微信公众号名称),帮助管理员识别 public static void Register(string componentAppId, string componentAppSecret, Func<string, string> getComponentVerifyTicketFunc, Func<string, string> getAuthorizerRefreshTokenFunc, Action<string, RefreshAuthorizerTokenResult> authorizerTokenRefreshedFunc, string name = null) { //激活消息队列线程

        if (GetComponentVerifyTicketFunc == null)
        {
            GetComponentVerifyTicketFunc = getComponentVerifyTicketFunc;
            GetAuthorizerRefreshTokenFunc = getAuthorizerRefreshTokenFunc;
            AuthorizerTokenRefreshedFunc = authorizerTokenRefreshedFunc;
        }

        RegisterFunc = () =>
        {
            using (FlushCache.CreateInstance())
            {
                var bag = new ComponentBag()
                {
                    Name = name,
                    ComponentAppId = componentAppId,
                    ComponentAppSecret = componentAppSecret,
                };
                Update(componentAppId, bag);
                return bag;
            }
        };
        RegisterFunc();
    }

GetAuthorizerRefreshTokenFunc,AuthorizerTokenRefreshedFunc缺少参数componentAppId,一个应用里不能同时注册多个。

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

注册多个第三方应用是可以的:

            ComponentContainer.Register(
                ConfigurationManager.AppSettings["Component_Appid"],
                ConfigurationManager.AppSettings["Component_Secret"],
                getComponentVerifyTicketFunc,
                getAuthorizerRefreshTokenFunc,
                authorizerTokenRefreshedFunc,
                "【盛派网络】开放平台");

之前的设计是所有的账号公用这几个xxFunc的逻辑,因为每个账号需要做的事情几乎是一样的,你是要每个账号定义完全不同的xxFunc?

ytpos commented 7 years ago

getAuthorizerRefreshTokenFunc,authorizerTokenRefreshedFunc如果不加入参数Component_Appid,就没办法区分,如果定义不周围的xxFunc,也是没用的,这是个静态的,全部共用。所以这两个函数要像getComponentVerifyTicketFunc一样增加一个参数Component_Appid

ytpos commented 7 years ago

if (GetComponentVerifyTicketFunc == null) { GetComponentVerifyTicketFunc = getComponentVerifyTicketFunc; GetAuthorizerRefreshTokenFunc = getAuthorizerRefreshTokenFunc; AuthorizerTokenRefreshedFunc = authorizerTokenRefreshedFunc; } 注册第二个是没用的

ytpos commented 7 years ago

@JeffreySu

JeffreySu commented 7 years ago

“注册第二个是没用的”是指没用上还是程序有问题导致没用?

ytpos commented 7 years ago

你最好看一下代码,代码bug,上面已经说得很清楚了。无论你注册多少个,取authorizerToken的函数都是同一个,又没有Component_Appid区分。只加上这个参数就可以了。这么明显的问题?看不出来,哎

ytpos commented 7 years ago

getComponentVerifyTicketFunc有Component_Appid,但getAuthorizerRefreshTokenFunc和authorizerTokenRefreshedFunc没有。再好加上吧

ytpos commented 7 years ago

当同一个公众号授权给同一网站的多个第三方开放平台,就没办法区分。

JeffreySu commented 7 years ago

越说越糊涂了,到底是设计问题,还是代码问题? 1、原先的设计就是只考虑一个第三方平台,没考虑你说的两个第三方平台同时部署在一个WebSite下面的情况,这种情况非常少的。 2、如果你说是代码有bug,请问具体“注册第二个是没用的”是哪里没起作用?

ytpos commented 7 years ago

1、sdk的bug,如果只考虑一个第三方平台,那就不说了。但getComponentVerifyTicketFunc这个函数有传入Component_Appid,原先的设计应是考虑了注册多个第三方平台的。 2、具体“注册第二个是没用的”是哪里没起作用,您看一下ComponentContainer.Register,多次注册,那三个Func都是以第一个为准,是不起作用的,代码如下: if (GetComponentVerifyTicketFunc == null) { GetComponentVerifyTicketFunc = getComponentVerifyTicketFunc; GetAuthorizerRefreshTokenFunc = getAuthorizerRefreshTokenFunc; AuthorizerTokenRefreshedFunc = authorizerTokenRefreshedFunc; }。 后两个函数没有传入Component_Appid。

提交个bug真心累。不说了

JeffreySu commented 7 years ago

总算知道你说的“注册第二个是没用的”是什么意思了,我也好累 :smile: 不过真心感谢您提供的信息!

我详细说一下,交流一下设计思路:那个if判断的作用,最重要的不是为了判断FuncX本身是否存在,只要用了Open,这三个FuncX必定是要配套都有的。那么这个if是不是干脆完全没必要存在,而是每次都直接赋值是呢?其实不是的。

1、考虑到用户可能使用Redis之类外部生命周期的缓存,必须要有一个方法可以让用户随时刷新AppId和Secret(主要),并且不影响其他任何数据,这个方法就是Register(),其最主要的作用是激活消息队列线程,储存ContainerBag。由于FuncX一般是静态地存在于某个代码片段中,所以重新注册的时候最好不要重新传入FuncX,尤其在分布式中这是非常不可靠的做法,那么也就是说必须支持传入3个(funcX)null。

2、基于上面这一点,在Register方法里面就需要排除干扰,所以必须要有一个判断,来判断是否是第一次注册,或是后面由管理员发起的修改Secret。于是才有了您看到的这只有一个if条件的判断,其作用只是为了避免FunxX被执行期望以外的赋值。3个FunxX通常是同时出现,所以这里只做了一个判断。当然每个都做判断也是没错的,只是我们认为没有必要了。

3、那么引发了另外一个问题,如果funcX真的需要在运行过程中更换怎么办呢?您可以注意到我们特地加了public的访问器,而不是默认的private,可以直接在外部赋值进行修改。

不知道这样说是否表达清楚了?

关于讨论的支持多个ComponentId的做法,一开始getComponentVerifyTicketFunc里面只是习惯性这么做了(日志文件名识别需要,不想引用外部变量,就加了一个),Open本身就是一个平台了,做到后面越来越感觉到不太可能有这种场景,所以就没有再往那个方面去规划了。所以您看Demo里面AuthorizerInfo的bin文件我们也都是放一块的,没有分文件夹。

如果您确实碰到了需要支持多ComponentId的实际需求,我们会考虑加上的,这个SDK就是希望服务开发者,如果这个问题只是作为讨论,我觉得就没必要画蛇添足了。再次感谢!

ytpos commented 7 years ago

非常感谢您对待每一个问题!

支持多个ComponentId的实际需求还是很广的,第三方平台可以建5个应用,其实处理方法都是一样的,完全可以共用一套代码。为什么要分5个应用呢,细分对客户的体验比较好,看起来专业,像做餐饮的,授权给餐饮类的应用,做服务装的,授权给服装类应用是很友好的。

再次感谢,加上的成本很小,望考虑。

JeffreySu commented 7 years ago

嗯,下个版本我们会计划加入。

另外还有一个问题补充一下,就是我前面说的“支持多个ComponentId”的情况,之前可能说得不够清除,这个不是指同一个WebSite下面支持多个,而是多个WebSite可以共用同一个分布式缓存(如Redis)数据库,使用和AccessTokenContainer一样的设计结构可以有非常好的兼容性(设想的场景是多个WebSite可以在一个内网使用同一的AccessToken,并且也支持多个不同的ComponentId对应的AppId、Secret等储存在同一个缓存下),由于FuncX委托属于应用程序池管理的范畴,所有才没有独立给ComponentId。