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)

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM