• django, django_restful 关于Authentication的学习总结


    一、关于配置

    django: 配置为AUTHENTICATION_BACKENDS,setting.py不写的话,AUTHENTICATION_BACKENDS默认设置为(‘django.contrib.auth.backends.ModelBackend’,),

              这是检测Django用户数据库的基本认证方案。按照 AUTHENTICATION_BACKENDS 的排列顺序,如果同样的用户名和密码在第一次就匹配了,那么Django将停止处理后面的东西        

    restful:  配置为 DEFAULT_AUTHENTICATION_CLASSES

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework.authentication.BasicAuthentication',
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.TokenAuthentication',
            'rest_framework_simplejwt.authentication.JWTAuthentication',
    
        ],
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 2
    
    }
    
    AUTHENTICATION_BACKENDS = (
        #new
        'rest_framework.authentication.TokenAuthentication',
        #default  ModelBackend
        'django.contrib.auth.backends.ModelBackend',
    )

    二、django什么时候调用authenticate?

    1.django, 在login view中,进行用户验证时, 调用 django.contrib.auth.authenticate.

    def login_custom(request):
    
        if request.method == 'POST':
    
            # create a form instance and populate it with data from the request:
            form = myAuthenticationForm(request.POST)
            # check whether it's valid:
            if form.is_valid():
    
                username = form.cleaned_data['username']
                password = form.cleaned_data['password']
                user = authenticate(request, username=username, password=password)
                if user is not None:
    
                    login(request, user)
                    return redirect('login:home')
                else:
                    return render(request, 'registration_my/login_custom.html', {'form': form})

    验证的参数: 可以是username+password, 也可以是token, 

    返回:none 或  setting. AUTH_USER_MODEL (如果不custom,则是auth.user)

    注意,如果不调用 django.contrib.auth.authenticate. 那么setting. AUTHENTICATION_BACKENDS根本用不上,就不会被调用。

    三. restful 什么时候调用DEFAULT_AUTHENTICATION_CLASSES 中的定义类?

    参考:https://www.cnblogs.com/eric_yi/p/8422373.html

    在setting.py中,配置参数,token认证需要和权限认证配合使用

    REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': (
    
            'rest_framework.permissions.IsAuthenticated',
    
        ),
    
        'DEFAULT_AUTHENTICATION_CLASSES': (
    
            'rest_framework.authentication.TokenAuthentication',
    
        ),
    }

     如下,所有继承 rest framework的APIView的类,都会走token认证,而有的view我们不需要认证,则在view中,指定permission_classes 和 authentication_classes为空,

    如下:则是不走token认证。

    class LoginView(APIView):
    authentication_classes = ()
    permission_classes = ()
    
    def post(self, request):
    username = request.data.get('username')
    password = request.data.get('password')

    (1) APIView中,不指定permission_classes = (xxx) 和 authentication_classes = (xxx),默认会进行setting.py中的 token认证。,发起请求时候,需要在请求头中添加 Authorization=Token 7d7770cb909ceead7d33ea7bafe7e6034ee012fc 

    (2)为需要的view指定permission_classes 和 authentication_classes, 则该view 不使用settings中添加REST_FRAMEWORK

    permission_classes = (permissions.AllowAny,) # 所有用户
    permission_classes = (permissions.IsAuthenticated,) # 登陆成功的token
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,) # 登陆成功的token,只能读操作
    permission_classes = (permissions.IsAdminUser,) # 登陆成功的管理员token

    注意,APIView中,调用 django.contrib.auth.authenticate时候,同样与普通django一样,调用的是配置参数AUTHENTICATION_BACKENDS中的内容

    四. restful 认证详解

    DEFAULT_AUTHENTICATION_CLASSES、AUTHENTICATION_BACKENDS 可以定制多个类, 有框架默认,还可以自己定制。
    1. 认证成功后的返回参数
    (1). 返回参数:restful authenticate 返回元组(return (toke_obj.user, toke_obj)) ,分别是user model 和 userToken model 的instanse
    (2). django 的authentivate返回user model instanse
    2. 配置多个authenticate时候循环调用机制
    如下,是restful的 APIView在进行request请求时候,层层跟踪,会执行如下语句,就是循环找认证函数的逻辑

    class APIView(View):
    as_view():dispatch():initialize_request():get_authenticators():
    self.authentication_classes
    self.perform_authentication 实现认证。 (认证就是取得user和token信息,至于是否拒绝访问,主要看permission。
    
    
       def _authenticate(self):
            """
            Attempt to authenticate the request using each authentication instance
            in turn.
            """
            for authenticator in self.authenticators:
                try:
         #执行认证类的authenticate方法
                    #这里分三种情况
                    #1.如果authenticate方法抛出异常,self._not_authenticated()执行
                    #2.有返回值,必须是元组:(request.user,request.auth)
                    #3.返回None,表示当前认证不处理,等下一个认证来处理
                    user_auth_tuple = authenticator.authenticate(self)
                except exceptions.APIException:
                    self._not_authenticated()
                    raise
    
                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    self.user, self.auth = user_auth_tuple  #返回值对应示例中的token_obj.user和token_obj
                    return

     配置了多个authenticate, 则循环调用,遇到异常停止,返回none,说明不使用本认证,会依次调用下一个。

    最后若都没有返回值,就执行self._not_authenticated(),相当于匿名用户,没有通过认证,并且此时django会返回默认的匿名用户设置AnonymousUser

    注意,每一个APIView都需要认证,认证分全局和局部。
    全局:所有的未局部定义的APIView都有效
    #全局认证配置
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',]   #其中写认证的类的路径,不要在views中,这里我放在了utils目录下auth.py中
    }

    局部:在APIView中单独定义

    authentication_classes = []    #authentication_classes为空,代表不需要认证

     匿名:

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',],  #其中写认证的类的路径,不要在views中,这里我放在了utils目录下auth.py中
        "UNAUTHENTICATED_USER": lambda:"匿名"#匿名用户配置,只需要函数或类的对应的返回值,对应request.user="匿名"
    "UNAUTHENTICATED_token": None,#匿名token,只需要函数或类的对应的返回值,对应request.auth=None
    ##路径:rest_framework.authentication
    BaseAuthentication是django rest framework为我们提供了最基本的认证类
    BasicAuthentication  #基于浏览器进行认证
    SessionAuthentication #基于django的session进行认证
    RemoteUserAuthentication #基于django admin中的用户进行认证,这也是官网的示例
    TokenAuthentication #基于drf内部的token认证
    如何进行定制?
    (1)继承BaseAuthentication,重写authenticate方法和authenticate_header(pass就可以),authenticate()
    方法需要有三种情况(返回元祖、出现异常、返回none)。
    (2)定制完后,进行注册(配置)

    #全局认证
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',]
    }
    
    #局部认证
    authentication_classes = [BaseAuthentication,]
    
    #是某个视图不进行认证
    authentication_classes =[]

    restful的例子


    from rest_framework.views import APIView
    from rest_framework.response import Response
    class HelloView(APIView):
        def get(self, request):
            content = {'message': 'Hello, World!'}
            return Response(content)
    

      如上代码,不会走认证,被调用时候,会返回  200 {'message': 'Hello, World!'}

    class HelloView(APIView):
        permission_classes = (IsAuthenticated,)             # <-- And here
        def get(self, request):
            content = {'message': 'Hello, World!'}
            return Response(content)

    增加一行 permission_classes = (IsAuthenticated,)则只有认证过的用户才可以访问。

    直接被调动时候,报错,HTTP 403 Forbidden error.

    INSTALLED_APPS = [
        # Django Apps
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        # Third-Party Apps
        'rest_framework',
        'rest_framework.authtoken',  # <-- Here
        # Local Apps (Your project's apps)
        'myapi.core',
    ]
    
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework.authentication.TokenAuthentication',  # <-- And here
        ],
    }
    class HelloView(APIView): 
    在APIView
    中,不指定authentication_classes, 则默认都会用全局设定setting.DEFAULT_AUTHENTICATION_CLASSES,
    想单独去掉认证,则设定authentication_classes=[] ,想定制独特的认证,可以在APIView中设置。

    必须和permission_classes 结合在一起使用,才能限制API的访问。 认证过程,仅仅是取得request.user 和request.auth , 而permission才是去检查user和auth,去限制访问。

    五. django 认证详解

    1. AUTHENTICATION_BACKENDS 默认为'django.contrib.auth.backends.ModelBackend',
    认证成功,返回user model 否则 none

    2. contrib.auth.authenticate的代码, 它调用的
    AUTHENTICATION_BACKENDS
    def authenticate(request=None, **credentials):
        """
        If the given credentials are valid, return a User object.
        """
        for backend, backend_path in _get_backends(return_tuples=True):
            try:
                inspect.getcallargs(backend.authenticate, request, **credentials)
            except TypeError:
                # This backend doesn't accept these credentials as arguments. Try the next one.
                continue
            try:
                user = backend.authenticate(request, **credentials)
            except PermissionDenied:
                # This backend says to stop in our tracks - this user should not be allowed in at all.
                break
            if user is None:
                continue
            # Annotate the user object with the path of the backend.
            user.backend = backend_path
            return user
    
        # The credentials supplied are invalid to all backends, fire signal
        user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials), request=request)
    3. 我们(程序员)调用contrib.auth.auth.authenticate 时候,传入参数可以是
    username+userpassword 或者是Token , 因为
    **credentials接收的是{}字典
    def signup(request):
        if request.method == 'POST':
            form = UserCreationForm_simple(request.POST)
            if form.is_valid():
                form.save()
    
                username = form.cleaned_data.get('username')
                raw_password = form.cleaned_data.get('password1')
                user = authenticate(username=username, password=raw_password)
                login(request, user)
                return redirect('login:home')

    4. 如何定制 backends authentication

    参考 https://docs.djangoproject.com/en/3.0/topics/auth/customizing/

    An authentication backend is a class that implements two required methods:
     get_user(user_id) and authenticate(request, **credentials),其中,
    authenticate ()

    方法要返回user. 看起来这样:

    from django.contrib.auth.backends import BaseBackend
    
    class MyBackend(BaseBackend):
        def authenticate(self, request, username=None, password=None):
            # Check the username/password and return a user.

    或者

    from django.contrib.auth.backends import BaseBackend
    
    class MyBackend(BaseBackend):
        def authenticate(self, request, token=None):
            # Check the token and return a user.

    总结: authentication()就是检查用户的凭证(userame+password, 或者 token 或者其他,)成功则返回user,否则返回None

    注意,凭证不限于db, 也可以是文件,任意逻辑都可以,返回user则OK,官网实例是通过文件。

    5.定制完 class 后,要在setting 的 AUTHENTICATION_BACKENDS中,增加设置该class为候选。

    但这并不代表,服务启动了,它就被自动调用了。只有在需要登录的时候调用 contrib.auth.authenticate , contrib.auth.authenticate循环调用AUTHENTICATION_BACKENDS 的定义。

    按倒序,由里往外总结

    (1)定制 BACKEND authenticate, 加入到 setting.AUTHENTICATION_BACKENDS 列表中

    (2)contrib.auth.authenticate 循环调用 backend authenticate ,返回user 或 none 或抛出异常

    (3)在我们程序员自己的view中,如 loginView,  调用contrib.auth.authenticate,进行认证。

    def signup(request):
        if request.method == 'POST':
            form = UserCreationForm_simple(request.POST)
            if form.is_valid():
                form.save()
    
                username = form.cleaned_data.get('username')
                raw_password = form.cleaned_data.get('password1')
                user = authenticate(username=username, password=raw_password)
    
    
  • 相关阅读:
    C++中智能指针的设计和使用
    [转]C++ 智能指针详解
    C++ const 常量和常指针
    深入理解C++中的mutable关键字
    C++ 静态常量
    BZOJ 1875: [SDOI2009]HH去散步
    BZOJ 1024: [SCOI2009]生日快乐
    BZOJ 1059: [ZJOI2007]矩阵游戏
    bzoj 1833: [ZJOI2010]count 数字计数
    LUOGU P2587 [ZJOI2008]泡泡堂
  • 原文地址:https://www.cnblogs.com/lxgbky/p/12132117.html
Copyright © 2020-2023  润新知