feat: 添加脱敏Mixin docs。update README

This commit is contained in:
XIE7654
2025-09-01 10:34:10 +08:00
parent fddbd8b6a7
commit 6aaaf6c0c0
3 changed files with 503 additions and 0 deletions

View File

@@ -0,0 +1,333 @@
# 通用脱敏解决方案
## 概述
为了保护敏感信息,系统提供了一个通用的脱敏 Mixin (`DesensitizationMixin`),可以轻松应用到任何序列化器中,实现字段的自动脱敏。
## 核心特性
- **通用性**:一个 Mixin 解决所有脱敏需求
- **灵活性**:支持直接字段和关联字段脱敏
- **可配置**:可自定义脱敏格式和权限规则
- **易使用**:只需几行代码即可启用脱敏
- **权限控制**:超级用户和管理员可查看完整值
## 快速开始
### 1. 基本用法
```python
from utils.serializers import CustomModelSerializer, DesensitizationMixin
class MySerializer(DesensitizationMixin, CustomModelSerializer):
# 指定需要脱敏的字段
desensitize_fields = ['password', 'api_key', 'secret_token']
class Meta:
model = MyModel
fields = '__all__'
```
### 2. 脱敏关联字段
```python
class UserProfileSerializer(DesensitizationMixin, CustomModelSerializer):
# 脱敏关联字段,使用点号分隔
desensitize_fields = [
'user.password', # 用户密码
'user.email', # 用户邮箱
'config.api_key', # 配置中的API密钥
'payment.credit_card' # 支付信息中的信用卡
]
class Meta:
model = UserProfile
fields = '__all__'
```
### 3. 自定义脱敏格式
```python
class CustomSerializer(DesensitizationMixin, CustomModelSerializer):
desensitize_fields = ['credit_card', 'phone_number']
# 自定义脱敏参数
desensitize_prefix_length = 2 # 保留前2位
desensitize_suffix_length = 2 # 保留后2位
desensitize_threshold = 6 # 长度小于等于6时全部脱敏
desensitize_char = '#' # 使用#作为脱敏字符
class Meta:
model = MyModel
fields = '__all__'
```
## 脱敏规则
### 默认脱敏格式
- **长度 ≤ 8 的字段**:全部用 `*` 替换
- 例如:`sk-123``*****`
- **长度 > 8 的字段**显示前4位和后4位中间用 `*` 替换
- 例如:`sk-1234567890abcdefghijklmnopqrstuvwxyz``sk-12****************************wxyz`
### 权限控制
- **超级用户 (`is_superuser=True`)**:可以查看完整的值
- **管理员用户 (`is_staff=True`)**:可以查看完整的值
- **普通用户**:只能查看脱敏后的值
## 配置选项
### 基本配置
```python
class MySerializer(DesensitizationMixin, CustomModelSerializer):
# 需要脱敏的字段列表
desensitize_fields = ['field1', 'field2', 'related.field3']
# 脱敏时保留的前缀长度默认4
desensitize_prefix_length = 4
# 脱敏时保留的后缀长度默认4
desensitize_suffix_length = 4
# 脱敏阈值字段长度小于等于此值时全部脱敏默认8
desensitize_threshold = 8
# 脱敏字符,默认使用*
desensitize_char = '*'
```
### 高级配置
```python
class AdvancedSerializer(DesensitizationMixin, CustomModelSerializer):
desensitize_fields = ['secret_key', 'user.password']
def _can_view_full_value(self):
"""重写权限检查方法,实现自定义权限逻辑"""
request = self.context.get('request')
if not request or not request.user:
return False
# 检查特定权限
if request.user.has_perm('app.view_sensitive_data'):
return True
# 检查角色
if request.user.role.filter(name='数据管理员').exists():
return True
# 检查用户组
if request.user.groups.filter(name='高级用户').exists():
return True
# 默认只有超级用户和管理员可以查看
return request.user.is_superuser or request.user.is_staff
def _apply_desensitization(self, value):
"""重写脱敏方法,实现自定义脱敏规则"""
# 自定义脱敏逻辑
if len(value) <= 6:
return '#' * len(value)
else:
return value[:2] + '#' * (len(value) - 4) + value[-2:]
```
## 实际应用示例
### 示例1用户信息脱敏
```python
class UserSerializer(DesensitizationMixin, CustomModelSerializer):
desensitize_fields = [
'password', # 密码
'email', # 邮箱
'phone_number', # 电话号码
'id_card_number' # 身份证号
]
class Meta:
model = User
fields = '__all__'
```
### 示例2支付信息脱敏
```python
class PaymentSerializer(DesensitizationMixin, CustomModelSerializer):
desensitize_fields = [
'credit_card_number', # 信用卡号
'cvv', # CVV码
'user.password', # 用户密码
'user.email' # 用户邮箱
]
# 为信用卡号使用更严格的脱敏
desensitize_prefix_length = 2
desensitize_suffix_length = 2
desensitize_threshold = 10
class Meta:
model = Payment
fields = '__all__'
```
### 示例3系统配置脱敏
```python
class SystemConfigSerializer(DesensitizationMixin, CustomModelSerializer):
desensitize_fields = [
'database_password', # 数据库密码
'redis_password', # Redis密码
'api_keys.openai_key', # OpenAI API密钥
'api_keys.aws_secret' # AWS密钥
]
class Meta:
model = SystemConfig
fields = '__all__'
```
## 字段映射规则
### 直接字段
- 字段名:`password` → 序列化后字段名:`password`
### 关联字段
- 字段名:`user.password` → 序列化后字段名:`user_password`
- 字段名:`config.api_key` → 序列化后字段名:`config_api_key`
### 注意事项
- 脱敏字段会自动转换为 `SerializerMethodField`
- 原字段会被移除,避免重复
- 关联字段的脱敏会创建新的方法字段
## 测试
### 运行测试
```bash
cd backend
python manage.py test ai.tests.DesensitizationMixinTest
python manage.py test ai.tests.AIApiKeyDesensitizationTest
```
### 测试用例
```python
def test_desensitization_mixin_inheritance(self):
"""测试脱敏 Mixin 是否正确继承"""
serializer = AIApiKeySerializer()
# 验证序列化器有脱敏相关的方法
self.assertTrue(hasattr(serializer, '_desensitize_field'))
self.assertTrue(hasattr(serializer, '_can_view_full_value'))
self.assertTrue(hasattr(serializer, '_apply_desensitization'))
def test_desensitization_fields_configuration(self):
"""测试脱敏字段配置"""
serializer = AIApiKeySerializer()
# 验证脱敏字段配置
self.assertIn('api_key', serializer.desensitize_fields)
self.assertEqual(serializer.desensitize_prefix_length, 4)
self.assertEqual(serializer.desensitize_suffix_length, 4)
```
## 故障排除
### 常见问题
1. **脱敏不生效**
- 检查是否正确继承了 `DesensitizationMixin`
- 确认 `desensitize_fields` 配置正确
- 验证序列化器是否正确设置了 `context`
2. **关联字段脱敏失败**
- 检查关联字段路径是否正确(使用点号分隔)
- 确认关联对象存在且可访问
- 验证字段名拼写正确
3. **权限检查失败**
- 检查用户是否已登录
- 验证 `is_superuser``is_staff` 字段
- 确认 `_can_view_full_value` 方法实现正确
### 调试方法
```python
# 在序列化器中添加调试信息
def _desensitize_field(self, obj, field_name):
"""脱敏指定字段"""
print(f"脱敏字段: {field_name}")
print(f"对象: {obj}")
# 继续原有的脱敏逻辑
return super()._desensitize_field(obj, field_name)
def _can_view_full_value(self):
"""检查当前用户是否可以查看完整值"""
request = self.context.get('request')
print(f"Request: {request}")
print(f"User: {request.user if request else 'None'}")
# 继续原有的权限检查逻辑
return super()._can_view_full_value()
```
## 性能考虑
- 脱敏字段会转换为 `SerializerMethodField`,可能影响性能
- 对于大量数据的场景,建议只对必要的敏感字段启用脱敏
- 可以考虑使用缓存来优化重复的脱敏计算
## 扩展功能
### 自定义脱敏规则
```python
class CustomDesensitizationMixin(DesensitizationMixin):
"""自定义脱敏 Mixin"""
def _apply_desensitization(self, value):
"""实现自定义脱敏规则"""
# 根据字段类型应用不同的脱敏规则
if self._is_credit_card(value):
return self._desensitize_credit_card(value)
elif self._is_phone_number(value):
return self._desensitize_phone_number(value)
else:
return super()._apply_desensitization(value)
def _is_credit_card(self, value):
"""判断是否为信用卡号"""
return len(value) == 16 and value.isdigit()
def _desensitize_credit_card(self, value):
"""信用卡号脱敏前4位 + 8个* + 后4位"""
return value[:4] + '*' * 8 + value[-4:]
```
### 批量脱敏
```python
class BatchDesensitizationMixin(DesensitizationMixin):
"""批量脱敏 Mixin"""
def _batch_desensitize(self, obj_list):
"""批量脱敏对象列表"""
for obj in obj_list:
for field_name in self.desensitize_fields:
if hasattr(obj, field_name):
setattr(obj, f'_{field_name}_desensitized',
self._desensitize_field(obj, field_name))
return obj_list
```
## 更新日志
- **v1.0.0**:初始实现,支持基本字段脱敏
- **v1.1.0**:添加关联字段脱敏支持
- **v1.2.0**:支持自定义脱敏参数和权限规则
- **v1.3.0**:添加完整的测试用例和文档
- **v2.0.0**:重构为通用 Mixin支持所有序列化器