服务剖析与工匠网关服务实现
1. 服务管理基础
在不同系统中,服务的管理方式各有不同。在某些系统里,若配置文件处于
launchd
文件的标准位置,可使用以下命令分别启动、重启和停止服务:
launchctl start testdaemon.service
launchctl restart testdaemon.service
launchctl stop testdaemon.service
而在 Linux 系统中,虽然当前管理服务进程的趋势正朝着
systemd/systemctl
发展,但仍可能存在使用 System V 风格初始化脚本的操作系统。以下是一个简单的初始化脚本示例:
#!/bin/sh
# - The action we're concerned with appears as $1 in a standard
# bash-script
case $1 in
start)
echo "Starting $0"
/usr/bin/python /usr/local/bin/testdaemon.py
;;
stop)
echo "Stopping $0"
/usr/bin/pkill -f testdaemon.py
;;
restart)
echo "Restarting $0"
/usr/bin/pkill -f testdaemon.py
/usr/bin/python /usr/local/bin/testdaemon.py
;;
esac
在 System V 管理的环境中,服务自身需确保与调用它的进程(如终端会话或操作系统的启动进程)分离,否则服务进程可能在实际执行任务前就终止。为处理这种情况,有一个
BaseDaemonizable
类,它能将服务类实例守护化,还可将进程 ID(PID)写入已知位置的文件。
2. 工匠网关服务概述
工匠网关在系统中充当工匠与中央办公室之间的通信中心,尤其在产品和订单对象方面。可能涉及的操作如下:
| 操作主体 | 操作内容 |
| ---- | ---- |
| 工匠 | 创建新产品数据、更新现有产品数据、删除产品、更新订单状态、更新自身工匠对象信息 |
| 中央办公室员工 | 标记产品可用、修改产品内容、管理工匠对象、管理订单对象 |
| 客户终端用户 | 间接创建订单对象 |
这些操作大多属于产品和/或订单对象的 CRUD 操作,相关子系统中的
_create
或
_update
方法基本能满足需求。数据变更的传输通常包含以下步骤:
1. 在用户级应用程序中进行本地数据变更(创建、更新或删除)。
2. 验证数据变更,确保数据格式正确且符合数据结构要求。
3. (若适用)本地存储数据变更。
4. 将数据变更序列化并传输到工匠网关服务,执行相应操作。
3. 数据传输机制选择
数据传输可选择消息队列或 Web 服务两种方式。编写 Web 服务可能需要处理认证、授权、特定 HTTP 方法和 CRUD 操作的关联等,复杂度较高,可考虑使用现有框架(如 Flask 或 Django)。相比之下,消息队列方式更简单,可编写七个相关函数,通过队列协议的消息调用。为解决认证和授权问题,可给每个工匠分配独立队列,并对消息签名。
4. 数据流动
无论选择哪种传输机制,工匠/产品操作的数据流动大致如下:
graph LR
A[本地工匠应用程序] --> B[创建消息并传输]
B --> C[工匠网关服务]
C --> D[读取、验证消息]
D --> E[调用相应服务方法]
E --> F[处理数据存储]
中央办公室用户对产品对象的操作、工匠与订单对象的交互等也有类似的数据流动。
5. 迭代故事
迭代故事涵盖了不同角色的需求,具体如下:
1. 工匠:向工匠网关发送数据变更,创建、更新、删除产品对象,更新订单对象,更新自身工匠对象。
2. 中央办公室用户:向工匠网关发送数据变更,管理工匠和订单对象。
3. 产品经理:激活、停用、更新产品对象。
4. 所有消息发送者:对消息进行签名,以便在处理前进行验证。
6. 消息定义
消息需处理结构化数据,内部以字典表示较为合适,因其易于序列化为 JSON 或 YAML 格式。例如,工匠视角下的产品数据字典以 JSON 格式呈现如下:
{
"oid": "a7393e5c-c30c-4ea4-8469-e9cd4287714f",
"modified": "2018-08-19 07:13:43",
"name": "Example Product",
"created": "2018-08-19 07:13:43",
"description": "Description TBD",
"metadata":{
"wood": "Cherry, Oak"
},
"available": false,
"dimensions": "2½\" x 4\" x ¾\"",
"shipping_weight": 0.5,
"summary": "Summary TBD"
}
为完整表示消息,还需添加操作和签名信息,示例如下:
{
"data":{
"oid": "a7393e5c-c30c-4ea4-8469-e9cd4287714f",
"modified": "2018-08-19 07:41:56",
"name": "Example Product",
"created": "2018-08-19 07:13:43",
"description": "Cherry and oak business-card holder",
"metadata": {
"wood": "Cherry, Oak"
},
"available": true,
"dimensions": "2½\" x 4\" x ¾\"",
"shipping_weight": 0.5,
"summary": "Cherry and oak business-card holder"
},
"operation":"update",
"signature":"{Hash hexdigest}"
}
7. DaemonMessage 类实现
为表示发送到或来自工匠网关服务的消息,实现了
DaemonMessage
类,代码如下:
import json
from hashlib import sha512
class DaemonMessage(object):
"""
Represents a *signed* message being sent to or received from a
BaseDaemon instance.
"""
###################################
# Class attributes/constants #
###################################
# - class-constant encoding-type for signature processes
__encoding = 'utf-8'
###################################
# Property-getter methods #
###################################
def _get_signature(self) -> str:
if not self.data:
raise RuntimeError(
'%s.signature cannot be calculated because there is '
'no data to sign' % (self.__class__.__name__)
)
if not self.signing_key:
raise RuntimeError(
'%s.signature cannot be calculated because there is '
'no key to sign data with' % (self.__class__.__name__)
)
return sha512(
bytes(
# - We're using json.dumps to assure a consistent
# key-order here...
json.dumps(self.data, sort_keys=True), self.__encoding
) + self.signing_key
).hexdigest()
###################################
# Instance property definitions #
###################################
data = property(
lambda self: self._data,
lambda self, value: setattr(self, '_data', value),
lambda self: delattr(self, '_data'),
'Gets, sets, or deletes the data/content of the message'
)
operation = property(
lambda self: self._operation,
lambda self, value: setattr(self, '_operation', value),
lambda self: delattr(self, '_operation'),
'Gets, sets, or deletes the operation of the message'
)
signature = property(
_get_signature, None, None,
'Gets the signature of the message'
)
signing_key = property(
lambda self: self._signing_key,
lambda self, value: setattr(self, '_signing_key', value),
lambda self: delattr(self, '_signing_key'),
'Gets, sets, or deletes the signing_key of the message'
)
###################################
# Object initialization #
###################################
def __init__(self,
operation:(str,None)=None, data:(dict,None)=None,
signing_key:(bytes,str,None)=None
):
"""
Object initialization.
self .............. (DaemonMessage instance, required) The instance to
execute against
operation ......... (str, optional, defaults to None) The operation
('create', 'update', 'delete' or 'response') that
the message is requesting
data .............. (dict, optional, defaults to None) The data of the
message
signing_key ....... (bytes|str, optional, defaults to None) The raw
data of the signing-key to be used to generate the
message-signature.
"""
# - Call parent initializers if needed
# - Set default instance property-values using _del_... methods
self._del_data()
self._del_operation()
self._del_signing_key()
# - Set instance property-values from arguments using
# _set_... methods
if operation:
self.operation = operation
if data:
self.data = data
if signing_key:
self.signing_key = signing_key
def to_message_dict(self):
return {
'data': self.data,
'operation': self.operation,
'signature': self.signature
}
def to_message_json(self):
return json.dumps(self.to_message_dict())
@classmethod
def from_message_dict(cls,
message_dict:(dict,), signing_key:(bytes,str)
):
"""
message_dict ...... (dict, required) The incoming message as a dict,
that is expected to have the following structure:
{
'data':dict,
'operation':str, # (create|update|delete|response)
'signature':str # (hash hex-digest)
}
signing_key ....... (bytes|str, optional, defaults to None) The raw
data of the signing-key to be used to generate the
message-signature.
"""
if type(message_dict) != dict:
raise TypeError(
'%s.from_message_dict expects a three-element '
'message_dict value ({"data":dict, "signature":str, '
'"operation":str}), but was passed "%s" (%s)' %
(cls.__name__, message_dict, type(message_dict).__name__)
)
if type(signing_key) not in (bytes, str):
raise TypeError(
'%s.from_message_dict expects a bytes or str signing_key '
'value, but was passed "%s" (%s)' %
(cls.__name__, signing_key, type(signing_key).__name__)
)
if type(signing_key) == str:
signing_key = bytes(signing_key, cls.__encoding)
_data = message_dict.get('data')
if not _data:
raise ValueError(
'%s.from_message_dict expects a three-element dict '
'({"data":dict, "signature":str, "operation":str}), '
'but was passed "%s" (%s) which did not include a '
'"data" key' %
(cls.__name__, message_dict, type(message_dict).__name__)
)
_signature = message_dict.get('signature')
if not _signature:
raise ValueError(
'%s.from_message_dict expects a three-element dict '
'({"data":dict, "signature":str, "operation":str}), '
'but was passed "%s" (%s) which did not include a '
'"signature" key' %
(cls.__name__, message_dict, type(message_dict).__name__)
)
_operation = message_dict.get('operation')
if not _operation:
raise ValueError(
'%s.from_message_dict expects a three-element dict '
'({"data":dict, "operation":str, "operation":str}), '
'but was passed "%s" (%s) which did not include a '
'"operation" key' %
(cls.__name__, message_dict, type(message_dict).__name__)
)
result = cls(_operation, _data, signing_key)
if result.signature == _signature:
return result
raise InvalidMessageError(
'The message %s, with a signature of "%s" did not match '
'the expected signature. Message DENIED' %
(_data, result.signature)
)
@classmethod
def from_message_json(cls, json_in:(str,), signing_key:(bytes,str)):
return cls.from_message_dict(json.loads(json_in), signing_key)
8. 签名密钥管理
DaemonMessage
类的签名过程依赖于签名密钥,创建签名密钥时需考虑以下因素:
-
长度
:密钥越长,破解难度越大。
-
字符多样性
:字符越多样,破解难度越大。
可使用 Python 的
os.urandom
函数生成签名密钥,示例如下:
import os
example_key = os.urandom(1024)
print(example_key)
example_key_hex = example_key.hex()
print(example_key_hex)
example_from_hex = bytes.fromhex(example_key_hex)
print(example_from_hex == example_key)
为确保密钥安全,需关注以下几点:
-
静态加密
:可使用库(如 PyCrypto)或配置 MongoDB 企业版的加密存储引擎实现,但可能增加系统复杂度。
-
传输加密
:传输密钥时必须实现加密,消息队列可使用私有证书,Web 服务可能需要公共证书颁发机构的证书。
-
密钥轮换
:定期更换密钥,降低密钥被破解的风险。
服务剖析与工匠网关服务实现
9. 签名密钥的数学原理与安全考量
签名密钥的安全性基于数学原理。从数学角度看,密钥可能的取值数量可以用公式(每个字符的可能取值数)^(字符串的字符数)来表示。例如,一个 128 字符的签名密钥,每个字符有 255 种可能取值,那么就有大约 1.09 × 10^308 种组合。即使每秒进行 10 亿次计算,每年大约进行 3.15 × 10^16 次计算,要破解这样的签名密钥在实际操作中几乎是不可能的。
在安全方面,静态加密对于当前系统规模可能过于复杂,暂不建议使用。但传输加密是必须的,因为签名密钥属于敏感信息。对于消息队列方式,可以使用私有证书进行加密;而对于 Web 服务,则可能需要从公共证书颁发机构获取证书。此外,定期进行密钥轮换能够降低密钥被破解后造成的风险。
10. 消息传输与加密的决策因素
在决定消息传输方式时,加密是一个重要的考量因素。无论是采用 Web 服务还是本地托管的消息队列,都需要创建加密证书来实现传输中的加密。消息队列方式可能允许使用私有证书,而 Web 服务可能需要公共证书颁发机构的证书。
以下是两种传输方式在加密方面的对比表格:
| 传输方式 | 加密证书要求 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- |
| 消息队列 | 可使用私有证书 | 配置相对灵活,可在内部环境使用 | 对外部访问支持可能有限 |
| Web 服务 | 需要公共证书颁发机构的证书 | 适合外部访问,安全性有公信力 | 证书获取和管理成本较高 |
11. 消息处理流程总结
综合前面的内容,消息处理的完整流程如下:
graph LR
A[本地数据变更] --> B[验证数据]
B --> C{数据存储?}
C -- 是 --> D[本地存储数据]
C -- 否 --> E[序列化数据]
D --> E
E --> F[选择传输方式]
F -- 消息队列 --> G[消息队列传输]
F -- Web 服务 --> H[Web 服务传输]
G --> I[工匠网关接收消息]
H --> I
I --> J[验证消息签名]
J -- 签名有效 --> K[调用相应服务方法]
J -- 签名无效 --> L[拒绝消息]
K --> M[处理数据存储]
12. 代码使用示例
以下是一个简单的代码示例,展示如何使用
DaemonMessage
类创建、序列化和反序列化消息:
import os
import json
from hashlib import sha512
class InvalidMessageError(Exception):
pass
class DaemonMessage(object):
"""
Represents a *signed* message being sent to or received from a
BaseDaemon instance.
"""
###################################
# Class attributes/constants #
###################################
# - class-constant encoding-type for signature processes
__encoding = 'utf-8'
###################################
# Property-getter methods #
###################################
def _get_signature(self) -> str:
if not self.data:
raise RuntimeError(
'%s.signature cannot be calculated because there is '
'no data to sign' % (self.__class__.__name__)
)
if not self.signing_key:
raise RuntimeError(
'%s.signature cannot be calculated because there is '
'no key to sign data with' % (self.__class__.__name__)
)
return sha512(
bytes(
# - We're using json.dumps to assure a consistent
# key-order here...
json.dumps(self.data, sort_keys=True), self.__encoding
) + self.signing_key
).hexdigest()
###################################
# Instance property definitions #
###################################
data = property(
lambda self: self._data,
lambda self, value: setattr(self, '_data', value),
lambda self: delattr(self, '_data'),
'Gets, sets, or deletes the data/content of the message'
)
operation = property(
lambda self: self._operation,
lambda self, value: setattr(self, '_operation', value),
lambda self: delattr(self, '_operation'),
'Gets, sets, or deletes the operation of the message'
)
signature = property(
_get_signature, None, None,
'Gets the signature of the message'
)
signing_key = property(
lambda self: self._signing_key,
lambda self, value: setattr(self, '_signing_key', value),
lambda self: delattr(self, '_signing_key'),
'Gets, sets, or deletes the signing_key of the message'
)
###################################
# Object initialization #
###################################
def __init__(self,
operation:(str,None)=None, data:(dict,None)=None,
signing_key:(bytes,str,None)=None
):
"""
Object initialization.
self .............. (DaemonMessage instance, required) The instance to
execute against
operation ......... (str, optional, defaults to None) The operation
('create', 'update', 'delete' or 'response') that
the message is requesting
data .............. (dict, optional, defaults to None) The data of the
message
signing_key ....... (bytes|str, optional, defaults to None) The raw
data of the signing-key to be used to generate the
message-signature.
"""
# - Call parent initializers if needed
# - Set default instance property-values using _del_... methods
self._del_data()
self._del_operation()
self._del_signing_key()
# - Set instance property-values from arguments using
# _set_... methods
if operation:
self.operation = operation
if data:
self.data = data
if signing_key:
self.signing_key = signing_key
def to_message_dict(self):
return {
'data': self.data,
'operation': self.operation,
'signature': self.signature
}
def to_message_json(self):
return json.dumps(self.to_message_dict())
@classmethod
def from_message_dict(cls,
message_dict:(dict,), signing_key:(bytes,str)
):
"""
message_dict ...... (dict, required) The incoming message as a dict,
that is expected to have the following structure:
{
'data':dict,
'operation':str, # (create|update|delete|response)
'signature':str # (hash hex-digest)
}
signing_key ....... (bytes|str, optional, defaults to None) The raw
data of the signing-key to be used to generate the
message-signature.
"""
if type(message_dict) != dict:
raise TypeError(
'%s.from_message_dict expects a three-element '
'message_dict value ({"data":dict, "signature":str, '
'"operation":str}), but was passed "%s" (%s)' %
(cls.__name__, message_dict, type(message_dict).__name__)
)
if type(signing_key) not in (bytes, str):
raise TypeError(
'%s.from_message_dict expects a bytes or str signing_key '
'value, but was passed "%s" (%s)' %
(cls.__name__, signing_key, type(signing_key).__name__)
)
if type(signing_key) == str:
signing_key = bytes(signing_key, cls.__encoding)
_data = message_dict.get('data')
if not _data:
raise ValueError(
'%s.from_message_dict expects a three-element dict '
'({"data":dict, "signature":str, "operation":str}), '
'but was passed "%s" (%s) which did not include a '
'"data" key' %
(cls.__name__, message_dict, type(message_dict).__name__)
)
_signature = message_dict.get('signature')
if not _signature:
raise ValueError(
'%s.from_message_dict expects a three-element dict '
'({"data":dict, "signature":str, "operation":str}), '
'but was passed "%s" (%s) which did not include a '
'"signature" key' %
(cls.__name__, message_dict, type(message_dict).__name__)
)
_operation = message_dict.get('operation')
if not _operation:
raise ValueError(
'%s.from_message_dict expects a three-element dict '
'({"data":dict, "operation":str, "operation":str}), '
'but was passed "%s" (%s) which did not include a '
'"operation" key' %
(cls.__name__, message_dict, type(message_dict).__name__)
)
result = cls(_operation, _data, signing_key)
if result.signature == _signature:
return result
raise InvalidMessageError(
'The message %s, with a signature of "%s" did not match '
'the expected signature. Message DENIED' %
(_data, result.signature)
)
@classmethod
def from_message_json(cls, json_in:(str,), signing_key:(bytes,str)):
return cls.from_message_dict(json.loads(json_in), signing_key)
# 生成签名密钥
signing_key = os.urandom(1024)
# 创建消息
data = {
"oid": "a7393e5c-c30c-4ea4-8469-e9cd4287714f",
"modified": "2018-08-19 07:41:56",
"name": "Example Product",
"created": "2018-08-19 07:13:43",
"description": "Cherry and oak business-card holder",
"metadata": {
"wood": "Cherry, Oak"
},
"available": true,
"dimensions": "2½\" x 4\" x ¾\"",
"shipping_weight": 0.5,
"summary": "Cherry and oak business-card holder"
}
message = DaemonMessage(operation="update", data=data, signing_key=signing_key)
# 序列化消息
json_message = message.to_message_json()
print("序列化后的消息:", json_message)
# 反序列化消息
received_message = DaemonMessage.from_message_json(json_message, signing_key)
print("反序列化后的消息数据:", received_message.data)
12. 总结
通过对服务管理、数据传输、消息处理和签名密钥管理等方面的详细分析,我们构建了一个完整的工匠网关服务体系。在实际应用中,需要根据具体的业务需求和安全要求,选择合适的消息传输方式和加密策略。同时,要重视签名密钥的管理,确保消息的安全性和完整性。通过对
DaemonMessage
类的使用,可以方便地实现消息的创建、序列化和反序列化,为数据的传输和处理提供了有效的工具。
超级会员免费看
27

被折叠的 条评论
为什么被折叠?



