Files
django-vue3-admin-gd/backend/examples/README_DESENSITIZATION.md
2025-09-01 10:34:10 +08:00

333 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 通用脱敏解决方案
## 概述
为了保护敏感信息,系统提供了一个通用的脱敏 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支持所有序列化器