Skip to content

客户端 0 信任

在客户端发上来的包中,如果自带 client=clientApi.GetLocalPlayerId() 参数,这是脚本的自声明的,懂我什么意思吗?它可以被内存篡改,如果攻击者足够有心。

建议使用引擎底层在每个包附带的 __id__ 参数,这个参数是在服务器侧 c++ 层的 network 服务根据连接 id 区分的,客户端无法伪造,我们在开发一些敏感业务时应对客户端发来的数据保持 0 信任 ( Client Zero Trust ),应当使用这个安全的参数,而不是脚本自声明的 client (或类似的东西) ,当然单机就可以为了项目效率随意点。

使用安全的 clientId

那么,如何使用 __id__ 参数?

我们知道,Call 封装默认行为是直接调用函数的,你拿不到客户端 id

python
# CallServer('A')
    def A(self):
        print '我被调用啦!'

如果你想拿到 __id__ ,使用

python
from ..framework.core.app.mixin.network.common import Call
	@Call.NeedClientId
    def A(self, clientId):
        print '哈哈,我知道你是谁了'

有时候,你不想被自动解包,我就想要完整的 payload ,咋办?

python
from ..framework.core.app.mixin.network.common import Call
	@Call.NoUnpack
    def A(self, e):
        print e['xxx']
        print e['__id__']

你还可以组合使用:

python
from ..framework.core.app.mixin.network.common import Call
    @Call.NeedClientId
    @Call.NoUnpack
    def A(self, clientId, e):
        print clientId, e

C <-> S 的 mapping 关系

世间万物都有个来由,为什么 AServer->AClient,BClient->BServer?

我就乐意命名 A -> B 行不行?为什么非得加后缀,我看后缀不顺眼行不行?

行!

首先,你可以用sys_target,它会让包自动送达此名称的系统

你如果觉得每次都主动传参太麻烦了,你也可以覆写系统的

python
# ClientBase.py
    def _get_call_channel(self):
        """
        如果发消息没指定目标系统,就用这个默认推断。
        比如 FooClient 默认发给 FooServer
        """
        name = self.__class__.__name__
        if name.lower().endswith('client'):
            return name[:-6] + 'Server'
        elif name.lower().endswith('server'):
            return name[:-6] + 'Client'
        raise Exception('无法推断 network call 默认发送 sys_target (系统目标名),请手动覆写 _get_call_channel 实现')

# A.py
    def _get_call_channel(self):
        return 'B'

如你所见,A 的 Call 调用的 sys_target 在不传参的情况下会自动获取 _get_call_channel 的结果。如果你覆写返回值,那么所有 A 的 Call 调用就会发往 B 系统。

使用同名前缀只是默认的推断方式,这是一种懒得动脑的约定俗成,但不是铁规律,你完全可以覆写这个推断函数。

你也可以截获所有 Call 事件,根据业务需求判断具体如何处理:

python
# XXServer.py
	def _handle_call_from_client(self, e):
        # print e['__id__'] # 我只是看看
        ServerBase._handle_call_from_client(self, e) # 继续处理

RPC 也支持 NeedClientId

完全可以成功调用,而且 clientId 是安全的 __id__

python
# self.Server().B().send()
    @Call.NeedClientId
    def B(self, clientId):
        print clientId, 'bbb'