目錄python
實現用戶中心信息自查,不帶主鍵的get請求,走單查邏輯算法
urls.py數據庫
# 用路由組件配置,造成的映射關係是 /user/center/ => list | user/center/(pk)/ => retrieve # router.register('user/center', views.UserCenterViewSet, 'center') urlpatterns = [ # ... # /user/center/ => 單查,不能走路由組件,只能自定義配置映射關係 url('^user/center/$', views.UserCenterViewSet.as_view({'get': 'user_center'})), ]
views.pydjango
from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response class UserCenterViewSet(GenericViewSet): permission_classes = [IsAuthenticated, ] queryset = models.User.objects.filter(is_active=True).all() serializer_class = serializers.UserCenterSerializer def user_center(self, request, *args, **kwargs): # request.user就是前臺帶token,在通過認證組件解析出來的, # 再通過權限組件IsAuthenticated的校驗,因此request.user必定有值,就是當前登陸用戶 serializer = self.get_serializer(request.user) return Response(serializer.data)
drf-jwt直接提供刷新功能後端
""" 1)運用在像12306這樣極少數安全性要求高的網站 2)第一個token由登陸簽發 3)以後的全部正常邏輯,都須要發送兩次請求,第一次是刷新token的請求,第二次是正常邏輯的請求 """
settings.pyapi
import datetime JWT_AUTH = { # 配置過時時間 'JWT_EXPIRATION_DELTA': datetime.timedelta(minutes=5), # 是否可刷新 'JWT_ALLOW_REFRESH': True, # 刷新過時時間 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7), }
urls.py安全
from rest_framework_jwt.views import ObtainJSONWebToken, RefreshJSONWebToken urlpatterns = [ url('^login/$', ObtainJSONWebToken.as_view()), # 登陸簽發token接口 url('^refresh/$', RefreshJSONWebToken.as_view()), # 刷新toekn接口 ]
Postman服務器
# 接口:/api/refresh/ # 方法:post # 數據:{"token": "登陸簽發的token"}
注意:登陸是post請求框架
# 自定義登陸(重點):post請求 => 查操做(簽發token返回給前臺) - 自定義路由映射 url('^user/login/$', views.LoginViewSet.as_view({'post': 'login'})),
# 重點:自定義login,完成多方式登陸 from rest_framework.viewsets import ViewSet from rest_framework.response import Response class LoginViewSet(ViewSet): # 登陸接口,要取消全部的認證與權限規則,也就是要作局部禁用操做(空配置) authentication_classes = [] permission_classes = [] # 須要和mixins結合使用,繼承GenericViewSet,不須要則繼承ViewSet # 爲何繼承視圖集,不去繼承工具視圖或視圖基類,由於視圖集能夠自定義路由映射: # 能夠作到get映射get,get映射list,還能夠作到自定義(靈活) def login(self, request, *args, **kwargs): serializer = serializers.LoginSerializer(data=request.data, context={'request': request}) # 自定義序列化 serializer.is_valid(raise_exception=True) # 序列化校驗 token = serializer.context.get('token') # 獲取token return Response({"token": token})
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler # 重點:自定義login,完成多方式登陸 class LoginSerializer(serializers.ModelSerializer): # 登陸請求,走的是post方法,默認post方法完成的是create入庫校驗,因此惟一約束的字段,會進行數據庫惟一校驗,致使邏輯相悖 # 須要覆蓋系統字段,自定義校驗規則,就能夠避免完成多餘的沒必要要校驗,如惟一字段校驗 username = serializers.CharField() class Meta: model = models.User # 結合前臺登陸佈局:採用帳號密碼登陸,或手機密碼登陸,佈局一致,因此無論帳號仍是手機號,都用username字段提交的 fields = ('username', 'password') def validate(self, attrs): # 在全局鉤子中,才能提供提供的所需數據,總體校驗獲得user # 再就能夠調用簽發token算法(drf-jwt框架提供的),將user信息轉換爲token # 將token存放到context屬性中,傳給外鍵視圖類使用 user = self._get_user(attrs) payload = jwt_payload_handler(user) # 內部提供的方法,生成tokrn token = jwt_encode_handler(payload) # 內部提供的方法,生成token self.context['token'] = token # 吧token放進context中,方便取 return attrs # 多方式登陸 def _get_user(self, attrs): username = attrs.get('username') password = attrs.get('password') import re if re.match(r'^1[3-9][0-9]{9}$', username): # 手機登陸 user = models.User.objects.filter(mobile=username, is_active=True).first() elif re.match(r'^.+@.+$', username): # 郵箱登陸 user = models.User.objects.filter(email=username, is_active=True).first() else: # 帳號登陸 user = models.User.objects.filter(username=username, is_active=True).first() if user and user.check_password(password): return user raise ValidationError({'user': 'user error'})
from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): # settings.py中要配置 mobile = models.CharField(max_length=11, unique=True, verbose_name='移動電話') icon = models.ImageField(upload_to='icon', default='icon/default.png', verbose_name='頭像') class Meta: db_table = 'o_user' verbose_name_plural = '用戶表' def __str__(self): return self.username class Book(models.Model): name = models.CharField(max_length=64, verbose_name='書名') class Meta: db_table = 'o_book' verbose_name_plural = '書表' def __str__(self): return self.name class Car(models.Model): name = models.CharField(max_length=64, verbose_name='車名') class Meta: db_table = 'o_car' verbose_name_plural = '車表' def __str__(self): return self.name
""" 1)User表建立兩條數據 2)Group分組表建立一條數據,name叫vip 3)操做User和Group的關係表,讓1號用戶屬於1號vip組 """
from rest_framework.permissions import BasePermission from django.contrib.auth.models import Group class IsVipUser(BasePermission): def has_permission(self, request, view): if request.user and request.user.is_authenticated: # 必須是合法用戶 try: vip_group = Group.objects.get(name='vip') if vip_group in request.user.groups.all(): # 用戶可能不屬於任何分組 return True # 必須是vip分組用戶 except: pass return False
from .permissions import IsVipUser class CarViewSet(ModelViewSet): permission_classes = [IsVipUser] # 自定義校驗 queryset = models.Car.objects.all() serializer_class = serializers.CarSerializer
class CarSerializer(serializers.ModelSerializer): class Meta: model = models.Car fields = ('name', )
router.register('cars', views.CarViewSet, 'car')
from rest_framework import settings
# drf配置(把配置放在最下方) REST_FRAMEWORK = { # 自定義三大認證配置類們 'DEFAULT_AUTHENTICATION_CLASSES':['rest_framework_jwt.authentication.JSONWebTokenAuthentication'], # 認證 # 'DEFAULT_PERMISSION_CLASSES': [], # 權限 # 'DEFAULT_THROTTLE_CLASSES': [], # 頻率 }
""" 1)如何自定義頻率類 2)頻率校驗的規則 3)自定義頻率類是最多見的:短信接口一分鐘只能發生一條短信 """
#1)UserRateThrottle: 限制全部用戶訪問頻率 #2)AnonRateThrottle:只限制匿名用戶訪問頻率
from rest_framework.throttling import SimpleRateThrottle """ 1)自定義類繼承SimpleRateThrottle 2)設置類實現scope,值就是一個字符串,與settings中的DEFAULT_THROTTLE_RATES進行對應 DEFAULT_THROTTLE_RATES就是設置scope綁定的類的頻率規則:1/min 就表明一分鐘只能訪問一次 3)重寫 get_cache_key(self, request, view) 方法,指定限制條件 不知足限制條件,返回None:表明對這類請求不進行頻率限制 知足限制條件,返回一個字符串(是動態的):表明對這類請求進行頻率限制 短信頻率限制類,返回 "throttling_%(mobile)s" % {"mobile": 實際請求來的電話} """
from rest_framework.throttling import SimpleRateThrottle # 只限制查接口的頻率,不限制增刪改的頻率 class MethodRateThrottle(SimpleRateThrottle): scope = 'method' # 系統的配置就放在配置文件中 def get_cache_key(self, request, view): # 只有對get請求進行頻率限制 if request.method.lower() not in ('get', 'head', 'option'): return None # 區別不一樣的訪問用戶,之間的限制是不衝突的 if request.user.is_authenticated: ident = request.user.pk else: # get_ident是BaseThrottle提供的方法,會根據請求頭,區別匿名用戶, # 保證不一樣客戶端的請求都是表明一個獨立的匿名用戶 ident = self.get_ident(request) return self.cache_format % {'scope': self.scope, 'ident': ident}
REST_FRAMEWORK = { # ... # 頻率規則配置 'DEFAULT_THROTTLE_RATES': { # 只能設置 s,m,h,d,且只須要第一個字母匹配就ok,m = min = maaa 就表明分鐘 'user': '3/min', # 配合drf提供的 UserRateThrottle 使用,限制全部用戶訪問頻率 'anon': '3/min', # 配合drf提供的 AnonRateThrottle 使用,只限制匿名用戶訪問頻率 'method': '3/min', # 限制請求方式頻率 }, }
from .permissions import IsVipUser from .throttles import MethodRateThrottle class CarViewSet(ModelViewSet): permission_classes = [IsVipUser] # 自定義是不是VIP校驗 throttle_classes = [MethodRateThrottle] # 自定義頻率限制 queryset = models.Car.objects.all() serializer_class = serializers.CarSerializer
主要:後端異常以後必定要添加日誌記錄。配合日誌一塊兒使用ide
from rest_framework.views import exception_handler as drf_exception_handler from rest_framework.response import Response def exception_handler(exc, context): # 只處理客戶端異常,不處理服務器異常, # 若是是客戶端異常,response就是能夠直接返回給前臺的Response對象 response = drf_exception_handler(exc, context) if response is None: # 沒有處理的服務器異常,處理一下 # 其實給前臺返回 服務器異常 幾個字就好了 # 那咱們處理異常模塊的目的是 無論任何錯誤,都有必要進行日誌記錄(線上項目只能經過記錄的日誌查看出現過的錯誤) response = Response({'detail': '%s' % exc}) # 須要結合日誌模塊進行日誌記錄的:項目中講 return response
REST_FRAMEWORK = { # ... # 異常模塊 # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler', # 原來的,只處理客戶端異常 'EXCEPTION_HANDLER': 'api.exception.exception_handler', }