一,认证组件
restframework中的认证组件是使用token实现的,之前我们用到过cookie和session,cookie保存在客户端浏览器中,用来保存不太敏感的数据,session保存在服务器中,用在存储重要的敏感的信息
不过我们在开发数据接口类的web应用中,使用token还是比较多的
token的认证步骤如下
- 用户登录,获取用户名密码,查询用户表,如果存在该用户,生成token,否则返回错误信息
- 更新token
(1),局部认证组件:
我们如果使用到token的话,需要再建立一张user表用来存储用户的用户名,密码以及用户状态等信息,另外还需要一张token表来存储用户的token值,user表跟token表时一对一的关系,表详情如下:
class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) user_type_entry = ( (1,'vip'), (2,'svip'), (3,'vvip') ) user_type = models.IntegerField(choices=user_type_entry) class UserToken(models.Model): user = models.OneToOneField('User',on_delete=models.CASCADE) token = models.CharField(max_length=128)
然后我们可以另外创建一个py文件,用来放我们获取随机字符串的类,这个随机字符串就当做我们用到的token值,然后需要用到的时候,直接调用就可以了
#get_token.py import uuid def get_token(): random_str = str(uuid.uuid4()).replace('-', '') return random_str
因为是用户登录认证,所以只需要post接口就可以了,下面是视图的写法:
1 2 class UserView(APIView): 3 def post(self,request): 4 # 定义返回消息类型 5 response = dict() 6 # 定义需要的用户信息 7 fields = {'username','password'} 8 # 定义一个用户信息字典 9 user_info = dict() 10 11 # 如果我们需要的用户信息在前端传过来的字典的key中 12 if fields.issubset(set(request.data.keys())): 13 for key in fields: 14 user_info[key] = request.data[key] 15 16 # 查询数据库中相关信息 17 user_instance = User.objects.filter(**user_info).first() 18 19 if user_instance is not None: 20 # 获取随机字符串 21 access_token = get_token() 22 # 去token表中验证有没有该用户的token,有的话更新,没有的话生成 23 UserToken.objects.update_or_create(user=user_instance,defaults={'token':access_token}) 24 25 response['status_code'] = 200 26 response['status_message'] = '登陆成功' 27 response['access_token'] = access_token 28 # 数据库中存的choice,所以用get_display取对应值 29 response['user_role'] = user_instance.get_user_type_display() 30 31 else: 32 response['status_code'] = 201 33 response['status_message'] = '登录失败,用户名或密码错误' 34 35 return JsonResponse(response)
定义认证类,另建一个py文件,用于存放认证类
1 from rest_framework.authentication import BaseAuthentication 2 from rest_framework.exceptions import APIException 3 4 from app01.models import UserToken 5 6 7 # 定义认证类 8 class UserAuth(BaseAuthentication): 9 10 # 认证逻辑都放在authenticate 11 def authenticate(self, request): 12 # request.query_params 中可以获取?param1=32¶m2=23形式的参数.类型为querydict 13 user_token = request.query_params.get('token') 14 try: 15 # 这个地方用get获取数据查不到会报错,所以用try 16 token = UserToken.objects.get(token=user_token) 17 # 以下返回的数据在权限中会用到 18 return token.user,token.token 19 except Exception: 20 raise APIException('没有认证')
然后在views中BookView中使用认证类
1 class BookView(ModelViewSet): 2 authentication_classes = [UserAuth] 3 queryset = Book.objects.all() 4 serializer_class = BookSerializer
多个认证类的实现
并且,我们还可以指定多个认证类,只是需要注意的是,如果需要返回什么数据,请在最后一个认证类中返回,因为如果在前面返回,在self._authentication()方法中会对返回值进行判断,如果不为空,认证的过程就会中止
DRF认证源码剖析
1 前面的步骤都差不多,我们来看有差别的地方,我们说,request对象是APIView重写的,这个是在dispatch方法里面实现的,继续往后看dispatch方法,我们会看到self.initial方法,就是在这个方法里面,我们会看到认证、权限、频率几个组件的实现: 2 3 执行self.initial()方法 4 执行self.perform_authentication(request),方法,注意,新的request对象被传递进去了 5 该方法只有一行request.user,根据之前的经验,解析器(request.data),我们知道这个user肯定也是request对的一个属性方法 6 所料不错,该方法继续执行self._authenticate(),注意此时的self是request对象 7 该方法会循环self.authenticators,而这个变量是在重新实例化request对象时通过参数传递的 8 传递该参数是通过get_authenticatos()的返回值来确定的,它的返回值是 9 [ auth for auth in self.authentication_classes ] 10 也就是我们的BookView里面定义的那个类变量,也就是认证类 11 一切都明朗了,循环取到认证类,实例化,并且执行它的authenticate方法 12 这就是为什么认证类里面需要有该方法 13 如果没有该方法,认证的逻辑就没办法执行 14 至于类里面的header方法,照着写就行,有兴趣的可以研究源码,这里就不细究了 15 该方法如果执行成功就返回一个元组,执行完毕 16 如果失败,它会捕捉一个APIException 17 如果我们不希望认证通过,可以raise一个APIException 18 19 这就是认证组件的实现方式,非常简单。
(2),全局认证组件
如果我们需要对所有的接口都进行认证的话,我们需要用到全局认证组件,此时我们需要在setting中进行配置:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'app01.classes.authentication_classes.UserAuth', ), }
二,权限组件
上面我们已经了解了认证组件的使用方式,下面是权限组件的使用方式,跟认证组件是差不多的,所以下面直接上用法:
1 #permission_class.py 2 3 # 定义权限类 4 class UserPerms(): 5 message = '您没有权限访问该数据' 6 7 def has_permission(self,request,view): 8 if request.user.user_type == 3: 9 return True 10 else: 11 return False
在视图中加入权限类:
1 class BookView(ModelViewSet): 2 authentication_classes = [UserAuth] 3 permission_class = [UserPerms] 4 queryset = Book.objects.all() 5 serializer_class = BookSerializer
这就是权限组件的使用方式,同理,如果要用到全局权限的话,在settings中配置
三,频率组件
request.MATA : 获取所有请求头
REMOTE_ADDR: 获取客户端的访问ip地址
HTTP_HOST : 获取服务端ip地址
(1),局部访问频率控制
1 #ratethrottle.py 2 3 from rest_framework.throttling import SimpleRateThrottle 4 5 6 class RateThrottle(SimpleRateThrottle): 7 rate = '5/m' 8 9 def get_cache_key(self, request, view): 10 return self.get_ident(request)
rate代表访问评率,上面表示每分钟五次,get_cache_key是必须存在的,它的返回值告诉当前频率控制组件要使用什么方式区分访问者(比如ip地址)。
视图中:
1 from .utils.throttles import RateThrottle 2 3 # Create your views here. 4 5 6 class BookView(ModelViewSet): 7 throttle_classes = [ RateThrottle ] 8 queryset = Book.objects.all() 9 serializer_class = BookSerializer
(2),全局访问频率控制组件
首先定义一个频率控制类,必须继承SimpleRateThrottle类:
1 class RateThrottle(SimpleRateThrottle): 2 scope = "visit_rate" 3 4 def get_cache_key(self, request, view): 5 return self.get_ident(request)
然后我们需要在settings中配置频率控制组件参数:
1 REST_FRAMEWORK = { 2 "DEFAULT_THROTTLE_CLASSES": ('app01.chasses.ratethrottle.RateThrottle',), 3 "DEFAULT_THROTTLE_RATES": { 4 "visit_rate": "5/m" 5 } 6 }
浙公网安备 33010602011771号