客户端 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, eC <-> 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'