django restframework使用redis實現token認證
一、前言
restframework有自己很方便的一套認證、權限體系:官方文檔(tokenauthentication)
官方文檔的token 是基於數據庫中的authtoken_token
表來做的
有時候在後續接口中需要使用的用戶信息過多時,頻繁、高並發下的查詢數據庫會帶來比較大的性能消耗。這個時候我們就需要通過redis來做用戶認證,並存儲一些用戶信息在其中。下面就為你講解如何基於redis來使用DRF做用戶認證。
二、詳解
1. 前期準備
1.1 安裝redis並啟動
自行安裝!這個都裝不好後面的教程也不用看瞭!看瞭也理解不瞭!
1.2 安裝django-redis庫
pip install django-redis -i https://pypi.tuna.tsinghua.edu.cn/simple
2. 配置redis
2.1 配置redis連接
在settings.py
輸入下面的代碼 (我的redis沒有設置密碼,所以配置代碼中無密碼相關配置)
# redis緩存配置 CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor", "IGNORE_EXCEPTIONS": True, } } }
2.2 初始化redis連接
在項目初始化文件中(__init__.py
)加入下面代碼進行redis的初始化
加入下列代碼
from django_redis import get_redis_connection redis_connect = get_redis_connection()
3. 將token寫入redis
在之前的登錄接口是將token寫入數據庫的,現在需要重寫它讓其寫入redis
3.1 原來的登錄代碼
@api_view(['POST']) def login(request): """ 登錄接口 """ user = authenticate(username=request.data['username'], password=request.data['password']) if user: Token.objects.filter(user_id=user.id).delete() token = Token.objects.create(user=user) _dict = {'id': user.id, 'username': user.username, 'first_name': user.first_name, 'last_name': user.last_name, 'email': user.email} redis_connect.set(token.key, json.dumps(_dict), 259200) # 存redis 259200秒=72個小時 return Response(data={'status_code': 200, 'msg': '登錄成功!', 'token': token.key}) return Response(data={'status_code': 403, 'msg': '密碼錯誤!'})
3.2 重寫後的登錄代碼
@api_view(['POST']) def login(request): """ 登錄接口 """ user = authenticate(username=request.data['username'], password=request.data['password']) if user: token = binascii.hexlify(os.urandom(20)).decode() # 生成token 的方式 _dict = {'id': user.id, 'username': user.username, 'first_name': user.first_name, 'last_name': user.last_name, 'email': user.email} redis_connect.set(token, json.dumps(_dict), 259200) # 存redis 259200秒=72個小時 return Response(data={'status_code': 200, 'msg': '登錄成功!', 'token': token}) return Response(data={'status_code': 403, 'msg': '密碼錯誤!'})
3.3 登錄後redis存儲的用戶記錄
4. 重寫認證token方法
4.1 源碼分析
我們可以全局搜索TokenAuthentication
找到【restframework】源碼中的Token認證類
這個類中我們隻需要關註authenticate_credentials
這個方法就可以瞭。
def authenticate_credentials(self, key): model = self.get_model() try: token = model.objects.select_related('user').get(key=key) except model.DoesNotExist: raise exceptions.AuthenticationFailed(_('Invalid token.')) if not token.user.is_active: raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) return (token.user, token)
源碼首先通過接口請求的token (源碼中的key
) 去數據庫中尋找是否有該對應的記錄
如果有則認證成功返回user
和token
這兩個模型對象
如果沒有對應的記錄,則拋出【invalid token】異常
try: token = model.objects.select_related('user').get(key=key) except model.DoesNotExist: raise exceptions.AuthenticationFailed(_('Invalid token.'))
如果有對應的記錄,但用戶是未激活的 (is_active=0) 則拋出【User inactive or deleted】異常
if not token.user.is_active: raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
然後restframework會在視圖層的dispatch
方法中進行異常的封裝並返回響應結果。
4.2 進行重寫
經過源碼分析,我們需要重寫的有兩部分:
1.驗證token (源碼中的key
) 是否有效,之前是從數據庫進行驗證的現在需要通過redis去驗證
2.重新封裝user
模型對象,但有個問題需要註意的是:
如果你重寫瞭django的user
對象,讓它關聯瞭其他表的屬性,那麼這裡則不能將其封裝進user
這個對象的,因為redis不能存儲一個對象!,當然如果非要這麼做可以將外鍵id值在登錄 (寫入token) 的時候存入redis,然後在這裡通過該外鍵id去查詢關聯的外鍵表獲取屬性,再封裝到user
模型對象中!
重寫後的代碼
class RedisTokenAuthentication(TokenAuthentication): def authenticate_credentials(self, key): null = None # json的None為null,所以需要定義一下 user_data = redis_connect.get(key) if user_data: user_dict = json.loads(user_data) user_obj = User() for key_name in user_dict.keys(): setattr(user_obj, key_name, user_dict[key_name]) return user_obj, key raise exceptions.AuthenticationFailed(_('無效的token.'))
4.3 加入認證配置
在settings.py
配置文件中,加入如下配置
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( #如果有REST_FRAMEWORK配置項瞭單獨加入該項即可 'Demo.RedisAuthentication.RedisTokenAuthentication', # 項目名稱.重新認證類所在的文件.類名 ), }
4.4 效果展示
增加一個接口
path('test-token', views.test_token),
接口方法代碼
@api_view(['GET']) @permission_classes((permissions.IsAuthenticated,)) def test_token(request): """ 測試token """ print('登錄的用戶名是:', request.user) res_data = {'data': {'status_code': 200}} return Response(**res_data)
輸出結果
登錄的用戶名是: admin
三、總結
無論是django還是restframework,他們的源碼和官方文檔在我看來是非常清晰的 (在我看的那麼多官方文檔中算很清晰的瞭) 這裡給DRF的團隊點個贊!!!
到此這篇關於django restframework使用redis實現token認證的文章就介紹到這瞭,更多相關django restframework token認證內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- python-jwt用戶認證食用教學的實現方法
- drf-router和authenticate認證源碼分析
- Django中的JWT身份驗證的實現
- Django學習筆記之View操作指南
- Django框架CBV裝飾器中間件auth模塊CSRF跨站請求問題