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

@@ -1,3 +1,6 @@
## 如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
# 项目简介
本项目为基于 Django5 + Vue3vben-admin全栈开发的企业级中后台管理系统支持动态菜单、按钮权限、自动化代码生成、前后端权限联动等功能适用于多角色、多权限场景的管理后台。

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支持所有序列化器

View File

@@ -0,0 +1,167 @@
"""
脱敏功能使用示例
这个文件展示了如何在不同的序列化器中使用 DesensitizationMixin 来实现字段脱敏
"""
from utils.serializers import CustomModelSerializer, DesensitizationMixin
# 示例1基本用法 - 脱敏单个字段
class BasicDesensitizationExample(DesensitizationMixin, CustomModelSerializer):
"""
基本脱敏示例
"""
# 指定需要脱敏的字段
desensitize_fields = ['password', 'api_key', 'secret_token']
class Meta:
model = None # 这里应该是您的模型
fields = '__all__'
# 示例2自定义脱敏格式
class CustomDesensitizationExample(DesensitizationMixin, CustomModelSerializer):
"""
自定义脱敏格式示例
"""
# 指定需要脱敏的字段
desensitize_fields = ['credit_card', 'phone_number', 'id_card']
# 自定义脱敏参数
desensitize_prefix_length = 2 # 保留前2位
desensitize_suffix_length = 2 # 保留后2位
desensitize_threshold = 6 # 长度小于等于6时全部脱敏
desensitize_char = '#' # 使用#作为脱敏字符
class Meta:
model = None
fields = '__all__'
# 示例3脱敏关联字段
class RelatedFieldDesensitizationExample(DesensitizationMixin, CustomModelSerializer):
"""
脱敏关联字段示例
"""
# 脱敏关联字段,使用点号分隔
desensitize_fields = [
'user.password', # 用户密码
'user.email', # 用户邮箱
'config.api_key', # 配置中的API密钥
'payment.credit_card', # 支付信息中的信用卡
'profile.phone_number' # 个人资料中的电话号码
]
class Meta:
model = None
fields = '__all__'
# 示例4混合脱敏和普通字段
class MixedDesensitizationExample(DesensitizationMixin, CustomModelSerializer):
"""
混合脱敏和普通字段示例
"""
from rest_framework import serializers
# 脱敏字段
desensitize_fields = ['secret_key', 'user.password']
# 普通字段
name = serializers.CharField()
description = serializers.CharField()
# 计算字段
full_name = serializers.SerializerMethodField()
def get_full_name(self, obj):
return f"{obj.first_name} {obj.last_name}"
class Meta:
model = None
fields = '__all__'
# 示例5条件脱敏
class ConditionalDesensitizationExample(DesensitizationMixin, CustomModelSerializer):
"""
条件脱敏示例
"""
desensitize_fields = ['api_key', 'secret_token']
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
# 示例6不同字段使用不同脱敏规则
class MultiRuleDesensitizationExample(DesensitizationMixin, CustomModelSerializer):
"""
不同字段使用不同脱敏规则的示例
注意:这个示例需要自定义实现,因为 DesensitizationMixin 使用统一的规则
"""
desensitize_fields = ['api_key', 'phone_number', 'credit_card']
def _apply_desensitization(self, value):
"""
重写脱敏方法,为不同字段应用不同规则
"""
# 这里可以根据字段名或其他逻辑来应用不同的脱敏规则
# 由于 DesensitizationMixin 的设计,这个示例需要额外的自定义逻辑
return super()._apply_desensitization(value)
# 使用说明
"""
使用 DesensitizationMixin 的步骤:
1. 在序列化器中继承 DesensitizationMixin
class MySerializer(DesensitizationMixin, CustomModelSerializer):
pass
2. 设置需要脱敏的字段
desensitize_fields = ['field1', 'field2', 'related.field3']
3. 可选:自定义脱敏参数
desensitize_prefix_length = 3 # 保留前3位
desensitize_suffix_length = 3 # 保留后3位
desensitize_threshold = 10 # 长度小于等于10时全部脱敏
desensitize_char = 'X' # 使用X作为脱敏字符
4. 可选:重写权限检查方法
def _can_view_full_value(self):
# 自定义权限逻辑
pass
5. 可选:重写脱敏方法
def _apply_desensitization(self, value):
# 自定义脱敏规则
pass
注意事项:
- 脱敏字段会自动转换为 SerializerMethodField
- 关联字段使用点号分隔,如 'user.password'
- 脱敏只影响显示,不影响数据库存储
- 超级用户和管理员默认可以查看完整值
- 可以通过重写方法来扩展功能
"""