Django Model字段加密的優雅實現

早前的一篇文章Django開發密碼管理表實例有寫我們寫了個密碼管理工具來實現對密碼的管理,當時加密解密的功能在view層實現,一直運行穩定所以也沒有過多關注實現是否優雅的問題。最近要多加幾個密碼錶再次回頭看之前的代碼發現加解密在view層實現較爲繁瑣,尤其是使用了Sadmin公共庫之後view的代碼簡潔了很多,若再在view層處理顯然不夠優雅,是時候用更優雅的方式來實現了

Sadmin增刪改查

對數據庫表的增刪改查是開發過程中最常用到的功能,之前的文章也介紹過我們通過封裝Sadmin公共庫用最爲簡潔的代碼實現了對錶的增刪改查操作,具體有多簡潔,看下邊的代碼

class TableList(ListCreateView):
    model = Table
    template = 'password/table.html'
    permission = {'get': 'password.select_table', 'post': 'password.create_table'}


class TableDetail(RetrieveUpdateDestroyView):
    model = Table
    permission = {'get': 'password.select_table', 'put': 'password.update_table',
                  'delete': 'password.delete_table'}

TableList類可以實現對錶的查詢以及新建表數據,TableDetail可以實現對錶內單條數據的查詢、修改和刪除,對應了兩條URL

path('table/', views.TableList.as_view(), name='table-list-url'),
path('table/<int:pk>/', views.TableDetail.as_view(), name='table-detail-url'),

如果在view層實現表字段的加密的話,那就要重寫TableList的post方法,以及TableDetail類的put方法,非常麻煩,那有什麼更爲優雅的方法呢?對錶字段的處理最好能在表發生變化的時候來處理,直接在model層來實現顯然要比view更合適,model層來實現的話通過Django的signals或是重寫model的save方法都是不錯的選擇

至於究竟是用signals還是重寫save方法,兩者都可以實現,個人覺得對於簡單的處理邏輯採用重寫save的方式比較好,而對於複雜的處理邏輯採用signals更清晰,而對於我們這個對字段進行加密的需求,邏輯簡單代碼也不需要太多,直接採用重寫save的方式就好了

重寫model的save方法

對於加密解密的核心代碼可以參考文章Django開發密碼管理表實例給出的源碼,重寫model的save方法代碼如下

class Table(models.Model):
    username = models.CharField(max_length=64, verbose_name='用戶名')
    password = models.CharField(max_length=512, verbose_name='密碼')

    def __str__(self):
        return self.application_name

    def save(self, *args, **kwargs):
        _encrypt = True

        if self.pk:
            old_password = Table.objects.get(id=self.id)
            _encrypt = False if old_password.password == self.password else True

        if _encrypt:
            _m = RsaCrypto().encrypt(self.password)
            if _m.get('state'):
                self.password = _m.get('message')
            else:
                raise Exception('加密失敗:' + _m.get('message'))

        super(Table, self).save(*args, **kwargs)

對於密碼加密,通常會在首次新加記錄,以及更新記錄密碼發生變化的情況下進行

每當save時如何判斷是insert還是update呢?可以通過是否存在self.pk來判斷,Django的model必須有一個字段爲主鍵,如果用戶沒有設置主鍵字段,那麼Django會默認創建一個名爲id的字段作主鍵,主鍵也用pk別名來表示,所以可以通過self.pk是否存在來判斷本次save究竟是insert還是update

當本次save爲update時,我們也需要判斷密碼字段是否發生了變化,如果沒有變化則不需要調用加密方法,判斷字段是否變化就需要獲取字段提交前的值,提交前的值可以通過Table.objects.get(id=self.id)來獲取

有了以上這些信息,那加密就水到渠成了。我們優雅的實現了字段的加密,那對於解密呢?個人覺得同樣放在model裏比寫在veiw裏更合適,可以在mdel里加個decode_password的方法

class Table(models.Model):
    ...
    
    def decode_password(self):
        _m = RsaCrypto().decrypt(self.password)
        if _m.get('state'):
            return {
                'state': 1,
                'message': _m.get('message'),
                'username': self.username
            }
        else:
            return {'state': 0, 'message': 'Error: ' + _m.get('message')}

需要解密時調用Model的decode_password方法就可以了

def decode_password(request, pk):
    try:
        _t = Table.objects.get(id=pk)
        return JsonResponse(_t.decode_password())
    except Exception as e:
        return JsonResponse({'state': 0, 'message': 'Error: ' + str(e)})

寫在最後

個人對代碼有一點潔癖,實現功能也以簡單實用爲主,能2行搞定的絕對不會寫3行,能有更優的解法就會毫不猶豫去重構,同時也堅決反對“又不是不能用”的說法。對於以上實現是否優雅,或是有更好的解決方法,歡迎討論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章