概述
一、引言
最近在开发一个网站,使用的是Python2.7+Django
,在向BAE发布的时候,数据库出现了异常,原因是BAE
基础版提供的免费数据库中,不能插入含有数据库关键字的字段,所以想出了通过使用ROT13
算法对字段的内容进行转换后在添加进数据库的办法。
二、正文
-
实现Rot13算法
ROT13
算法比较特殊,加密和解密都是同一个算法。如果你要换成其他加密解密算法,请注意区分加密和解密。ROT13 = string.maketrans( "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm") def rot13_encode(text): return string.translate(text, ROT13)
点击此处阅读详细内容
-
object(新式类)
实现自动加密解密的功能需要对新式类有一个最基本的了解。
新式类是python2.2
版本中引入的,所有新式类全部继承自object类。
为什么要引入新式类呢?官方的解释是:为了统一类
(class)
和类型(type)
。这个和我们今天的内容没有太大关系,如果有兴趣可以自己问度娘。
我们只需要知道新式类中添加了__getattribute__
和__setattr__
方法。每次访问新式类的实例的属性时,都会调用
__getattribute__
方法。
而通过__setattr__
方法,我们可以实现在程序运行时通过字符串动态的设置实例属性的功能,大大的方便我们的开发。getattribute
#coding=utf-8 class A(object): #类A继承自object,是一个新式类 def __init__(self, id=100): self.id = id a = A() print a.id output: 100 print a.__getattribute__('id') #等价于a.id #output: 100
我们尝试着对__getattribute__重写
#coding=utf-8 class A(object): #类A继承自object,是一个新式类 def __init__(self, id=100): self.id = id def __getattribute__(self, attr): #注意:我们使用A父类的__getattribute__方法获取属性 #如果直接使用getattr(self, attr)获取attr属性会造成死循环 #有兴趣可以尝试一下 return object.__getattribute__(self, attr) + 10 a = A() print a.id #output: 110 print object.__getattribute__(a, 'id') #output: 100
我们可以看出访问a的属性
id
时,调用了a
的__getattribute__
方法,得到的值是经过处理的,而object.__getattribute__
方法可以获取到真正的内容,请记住这两个方法和它们的特性,一会儿我们会用到(这两个方法就是这篇文章最核心的内容)。
setattr
虽说__getattribute__
是主角,但是少了__setattr__
,我们的功能虽然也能完成,但是会很麻烦。#coding=utf-8 class A(object): def __init__(self, id = 100): self.id = id a = A() print a.id #output: 100 a.__setattr__('id', 120) print a.id #output: 120
好了,如果你学会了如何使用__getattribute__和__setattr__方法,那我们的预备工作就做好了,下面进入正戏。
字段插入数据库时自动加密
通过对models.py中模型的save函数进行了重写,我们可以实现在保存时自动调用ROT13
算法进行转换的功能。
以Article
模型为例:#coding=utf-8 from django.db import models #实现ROT13算法 #当然,真正开发时该算法肯定不会写在models.py里 #这里为了简化代码,直接写在了这里 ROT13 = string.maketrans( "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm") def rot13_encode(text): return string.translate(text, ROT13) class Article(models.Model, object): #以下为最初的字段(仅挑选出了相关字段) title = models.CharField(max_length=200) content = models.TextField() hot = models.IntegerField(default=0) #文章的热度,此字段无需加密 #使用以下字段记录相关字段是否是加密状态 #使用"{name}_en"的方式命名,便于操作 title_en = models.BooleanField(default=False) content_en = models.BooleanField(default=False) #encrypt_items记录哪些字段需要加密 encrypt_items = ['content', 'title'] #重写Model的save函数,实现当向数据库插入字段时自动加密的功能 def save(self, *args, **kwargs): for attr in self.encrypt_items: if not getattr(self, '%s_en' % attr): self.__setattr__(attr, rot13_encode(getattr(self, attr))) self.__setattr__('%s_en' % attr, True) #调用父类Model的save函数,进行真正的数据库插入操作 super(Article, self).save(*args, **kwargs)
从数据库读取时自动进行解密
重写save函数的不足
一般我们用读取数据库内容时会这样写:art = Article.object.get(id=1) title = art.title content = art.content
但是在上一小节,重写过
save
后,读取数据库内容时我们需要这样写:art = Article.object.get(id=1) title = rot13_encode(art.title) content = rot13_encode(art.content)
这样的话写代码的工作量会剧增,而且如果其他代码已经写好,那就需要一行一行的改代码——非常痛苦的工作。所以我们需要对
Article
进行改进。
实现读取数据库时自动解密#coding=utf-8 from django.db import models #实现ROT13算法 ROT13 = string.maketrans( "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm") def rot13_encode(text): return string.translate(text, ROT13) #简化函数名称,便于调用 OGA = object.__getattribute__ REP = rot13_encode class Article(models.Model, object): title = models.CharField(max_length=200) content = models.TextField() hot = models.IntegerField(default=0) #文章的热度,此字段无需加密 title_en = models.BooleanField(default=False) content_en = models.BooleanField(default=False) #encrypt_items记录哪些字段需要加密 encrypt_items = ['content', 'title'] def __getattribute__(self, attr): try: if (attr in OGA(self, 'encrypt_items')) and OGA(self, '%s_en' % attr): #若所查询属性(字段)需要加密且已经被加密,解密后再返回 return REP(OGA(self, attr)) else: #所查询属性无需加密或未被加密,直接返回 return OGA(self, attr) except AttributeError as err: #可能出现AttributeError异常,捕获后直接上抛即可 raise err #重写Model的save函数,实现当向数据库插入字段时自动加密的功能 def save(self, *args, **kwargs): for attr in self.encrypt_items: if not OGA(self, '%s_en' % attr): #注意:这里注释掉了进行加密的这行代码。只有这样才能正常加密!!! #请往下阅读,我会说明原因 #self.__setattr__(attr, REP(OGA(self, attr))) self.__setattr__('%s_en' % attr, True) #调用Model的save函数 super(Article, self).save(*args, **kwargs)
有的读者可能会疑惑,为何去掉了那行真正进行加密的代码,才能正常加密呢?(没有疑惑的话,这篇文章就已经结束了,点个赞再走呗~)
最初我自己实现这个功能的时候并没有去掉那行代码(废话!),然而当我测试时发现数据库中需要加密的字段并没有被加密,但是title_en
和content_en
的值都是1(数据库中1代表True
,0代表False
)。
有问题自然要进行调试,我修改了一下代码的最后几行:print OGA(self, 'title') super(Article, self).save(*args, **kwargs) print OGA(self, 'title')
即在调用
Model
的save
函数前分别打印了一次title真正的值,发现在调用save
前,title
是被正常加密了的(注意,我这时没有注释掉那一行代码)。但是调用save后就很有趣了,title真正的值又变成了未加密的值。
而当注释掉那行代码后,数据库中的值才是被加密过后的。对此,我的猜想(没错,是猜想)是在调用save
函数时,Django
使用了类似这样的代码:self.title = self.title
对,你没有看错,就是这样,你要知道,我们对
Article
的__getattribute__
方法进行了重写,本来title是加密了的,但是一旦使用一次self.title = self.title
,title就会被解密,这样一来,插入到数据库中的字段就是被解密了的。
作者:LucianoSimon
链接:https://www.jianshu.com/p/193fd76e7a96
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
最后
以上就是搞怪火车为你收集整理的Django实现读取数据库时自动加密解密的全部内容,希望文章能够帮你解决Django实现读取数据库时自动加密解密所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复