赞
踩
在没有使用 drf 之前,如何判断用户是否登录,一般是给前端提供一个获取用户信息的接口,如果未登录返回未授权等信息,权限的话一般是在 model 层通过字段来设置,这样只能完成简单的权限限制。
@require_http_methods(['GET'])
@ensure_csrf_cookie
def api_userinfo(request):
"""
获取当前用户信息
"""
if not request.user.is_authenticated:
return api.not_authorized()
return JsonResponse()
认证一般分三种:
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request._request.GET.get("token")
try:
token_obj = ReviewUserToken.objects.get(token=token)
except ReviewUserToken.DoesNotExist:
raise exceptions.AuthenticationFailed("认证失败")
return (token_obj.user, token_obj)
def authenticate_header(self, request):
pass
当用户进行登录的时候,运行了登录类的as_view()方法,进入了APIView类的dispatch方法,在原始的 django 中 dispatch 只做方法的分发与执行,并不做认证、权限、节流等操作。
原始 dispatch :
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
APIView 类也是继承自 View 并重写了 dispatch 方法,加上了认证等信息。
下面一步一步进行解析:
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs # 重新封装 request 方法以及其他参数,此时这个request不再是django的request 而是 drf 里面的 request = self.initialize_request(request, *args, **kwargs) # 重新复制给request,记住,这是封装之后的 self.request = request self.headers = self.default_response_headers # deprecate? try: # 执行认证、权限、节流相关逻辑,认证也在里面,先看认证。 self.initial(request, *args, **kwargs) # Get the appropriate handler method # 以下和 django View 里面内容一样 if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
initial 方法:
def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # 认证、权限、节流,依次, self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request)
perform_authentication 方法:
def perform_authentication(self, request): """ Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication will instead be performed lazily, the first time either `request.user` or `request.auth` is accessed. """ # 执行了 user 这个属性,找到这个 user 属性,只不过是方法加了 property 装饰器 request.user @property def user(self): """ Returns the user associated with the current request, as authenticated by the authentication classes provided to the request. """ if not hasattr(self, '_user'): with wrap_attributeerrors(): # 执行了_authenticate() 方法。 self._authenticate() return self._user
_authenticate 方法:
def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ # MyAuthentication 自定义的认证类 # APIView 中指定的 authentication_classes = [MyAuthentication, ] # authenticators 就是封装 request 的时候已经赋值给 authenticators """ def initialize_request(self, request, *args, **kwargs): parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), # 将认证类赋值给 authenticators negotiator=self.get_content_negotiator(), parser_context=parser_context ) def get_authenticators(self): # 返回一个实例化好的列表 return [auth() for auth in self.authentication_classes] """ # 循环每一个认证类,这个时候 self.authenticators # 内容就是 get_authenticators 里面实例化好的对象了,在这循环遍历认证。 for authenticator in self.authenticators: try: # 返回一个二元组,分别是认证成功的 user 实例和 auth user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() # 如果认证失败,会往上抛出异常,将会被 dispatch 捕获并返回 raise if user_auth_tuple is not None: self._authenticator = authenticator # 赋值给 user 和 auth 对象 self.user, self.auth = user_auth_tuple return # 认证失败 self._not_authenticated() def _not_authenticated(self): """ Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None. """ self._authenticator = None # 默认配置文件中配置的,default_sttings.py if api_settings.UNAUTHENTICATED_USER: # 也可以在配置文件中配置成自己想要的 self.user = api_settings.UNAUTHENTICATED_USER() # AnonymousUser else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() # None else: self.auth = None
配置文件中配置认证类:
默认会去配置文件读,如果 APIView 中写了就用 APIView 中的。

key 的名称叫 REST_FRAMEWORK。

配置如下:
REST_FRAMEWORK = {
# 自定义认证类的路径,可以多个
"DEFAULT_AUTHENTICATION_CLASSES": ["api.utils.auth.MyAuthentication"],
}
默认的认证类:
为了规范,自定义的认证类都要继承:BaseAuthentication
class BaseAuthentication: """ All authentication classes should extend BaseAuthentication. """ def authenticate(self, request): """ Authenticate the request and return a two-tuple of (user, token). """ raise NotImplementedError(".authenticate() must be overridden.") def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ pass
**BasicAuthentication:**基于用户名密码的身份验证。
class BasicAuthentication(BaseAuthentication):
"""
HTTP Basic authentication against username/password.
"""
pass
**SessionAuthentication:**使用 Django 的会话框架进行身份验证。
class SessionAuthentication(BaseAuthentication):
"""
Use Django's session framework for authentication.
"""
pass
**TokenAuthentication: ** 基于 token 令牌的方式
如:Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a
class TokenAuthentication(BaseAuthentication):
"""
Simple token based authentication.
Clients should authenticate by passing the token key in the "Authorization"
HTTP header, prepended with the string "Token ". For example:
Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a
"""
pass
不建议使用 drf 自带的 token 认证,因为 token 是保存在后端,
每次都需要查询数据库,增加数据库的压力,不利于分布式系统中使用,
在实际项目中会使用 JWT 标准的认证方式,python中可以使用 pyjwt
一般配置文件中这样设置:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
权限模块也是在 initial 中:check_permissions
def check_permissions(self, request): """ Check if the request should be permitted. Raises an appropriate exception if the request is not permitted. """ # 这次读取的是 APIView 中的 permission_classes # 设置如:permission_classes = [IsAuthenticated] # 需要在类中实现一个 has_permission 方法。如下: """ class IsAuthenticated(BasePermission): def has_permission(self, request, view): return bool(request.user and request.user.is_authenticated) """ # 循环的是 get_permissions 实例化之后的权限类 for permission in self.get_permissions(): if not permission.has_permission(request, self): self.permission_denied( # message 可以自定义,默认是异常里面抛出的 request, message=getattr(permission, 'message', None) )
同样也有一个获取所有权限类的方法,并且实例化之后返回一个列表:
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]
没有权限直接抛出异常:
def permission_denied(self, request, message=None):
"""
If request is not permitted, determine what kind of exception to raise.
"""
if request.authenticators and not request.successful_authenticator:
raise exceptions.NotAuthenticated()
raise exceptions.PermissionDenied(detail=message)
同样也可以配置文件中配置:
REST_FRAMEWORK = {
# 自定义认证类路径
"DEFAULT_AUTHENTICATION_CLASSES": ["rest_framework.authentication.SessionAuthentication"],
# 自定义权限类路径,这里是使用的默认的,自定义的可以把路径写在这里
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
}
源码内容:
drf 中文网:https://q1mi.github.io/Django-REST-framework-documentation/api-guide/authentication_zh/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。