前言
由于业务需求,并且保证数据安全行,某些数据字段需要加密,比如用户身份等隐私数据。
数据加密情况下,假设被脱库,也不会丢失用户隐私数据
实现
AES的优点是比较快,缺点就是密钥的保密很关键,性能问题所致,本次是通过 AES加密库实现
import base64
import hashlib
from Cryptodome import Random
from Cryptodome.Cipher import AES
class AESCipher(object):
def __init__(self, key):
self.key = hashlib.sha256(key.encode()).digest()
def encrypt(self, raw):
raw = self._pack_data(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpack_data(cipher.decrypt(enc[AES.block_size:]))
@staticmethod
def _pack_data(s):
return s + ((AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)).encode(
'utf-8')
@staticmethod
def _unpack_data(s):
data = s[:-ord(s[len(s) - 1:])]
if isinstance(data, bytes):
data = data.decode('utf-8')
return data
aes核心代码即上面代码,主要实现了aes的加密与解密,接下来,就需要封装Django自定义字段
封装加密字段
from django.conf import settings
from django.db import models
class AESCharField(models.CharField):
def __init__(self, *args, **kwargs):
if 'prefix' in kwargs:
self.prefix = kwargs['prefix']
del kwargs['prefix']
else:
self.prefix = "aes:::"
self.cipher = AESCipher(settings.SECRET_KEY)
super(AESCharField, self).__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super(AESCharField, self).deconstruct()
if self.prefix != "aes:::":
kwargs['prefix'] = self.prefix
return name, path, args, kwargs
def from_db_value(self, value, *args, **kwargs):
if value is None:
return value
if value.startswith(self.prefix):
value = value[len(self.prefix):]
if isinstance(value, str):
value = value.encode('utf-8')
value = self.cipher.decrypt(value)
return value
def to_python(self, value):
if value is None:
return value
elif value.startswith(self.prefix):
value = value[len(self.prefix):]
if isinstance(value, str):
value = value.encode('utf-8')
value = self.cipher.decrypt(value)
return value
def get_prep_value(self, value):
if isinstance(value, str):
value = value.encode('utf-8')
if isinstance(value, bytes):
value = self.cipher.encrypt(value)
value = self.prefix + value.decode('utf-8')
elif value is not None:
raise TypeError(str(value) + " is not a valid value for AESCharField")
return value
如何使用该加密字段
model如下
from django.db import models
# Create your models here.
class AliyunDrive(models.Model):
owner_id = models.ForeignKey(to=User, on_delete=models.CASCADE, verbose_name="所属用户ID")
user_name = models.CharField(max_length=128, verbose_name="用户名")
nick_name = models.CharField(max_length=128, verbose_name="昵称")
user_id = models.CharField(max_length=32, verbose_name="用户ID")
default_drive_id = models.CharField(max_length=16, verbose_name="存储ID")
default_sbox_drive_id = models.CharField(max_length=16, verbose_name="保险箱ID")
access_token = AESCharField(max_length=1536, verbose_name="访问token")
refresh_token = AESCharField(max_length=512, verbose_name="刷新token")
类似这种,直接引用
access_token = AESCharField(max_length=1536, verbose_name="访问token")
然后我们保存数据进行查看
1.通过manage shell 查看,是原始字段
2.直接查询数据库,是加密字段
Django 自定义加密字段就是如此使用,简单方便,并且很安全