rest_framework提供了幾種認證方式:Session、Token等。Session是最簡單的,幾乎不用寫任何代碼就可以是實現,Token方式其實也不復雜,網上的教程一大把,但是最后都是用Postman這類工具來實現API調用的,通過這類工具來增加HTTP頭信息以回傳Token。那么真正的前端網頁應該怎么辦呢?網上基本上就是基于Aixos來實現的,但是我就不想用Vue,純Javascript是不能改寫HTTP頭的。怎么辦?
首先,看一下TokenAuthentication的源碼:
auth = request.META.get('HTTP_AUTHORIZATION', b'')
它是從HTTP頭中讀取Authorization,其中是的內容Token(固定字) Token(值)。
那既然JavaScript不能改寫頭,但是能用cookie啊,所以我就通過自定義TokenAuthentication類,讀取Cookie來實現。
1. 用戶登錄之后先創建Token,并返回給前端。
2. 前端把Token保存到Cookie。
$.ajax({url: "login",type:'POST',data: {'username':username,'password':password},dataType: "json",success: function (data) { //請求成功后執行的操作if (data.status == "SUCCESS") {//localStorage.setItem('token',data.token);$.cookie('token', data.token);window.location.href = '/';}else{alert('Invalid username or password.');}} });
這樣每次發起HTTP請求時都會把Token帶上
3. 在logout的時候把Token刪除。防止失效Token仍然可以使用。
def logout_view(request):request.user.auth_token.delete()logout(request)return redirect('login')
4. 寫一個自定義的TokenAuthentication類:
from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from rest_framework.authtoken.models import Tokenclass CustomTokenAuthentication(BaseAuthentication):keyword = 'token'def authenticate(self, request):cookie_token = request.COOKIES.get(self.keyword)if cookie_token is None:raise AuthenticationFailed('No Token Found in Cookies!')try:user_token = Token.objects.get(key=cookie_token)if user_token is None:raise AuthenticationFailed('No Token Found for Current User!')return (user_token.user, user_token)except Token.DoesNotExist:raise AuthenticationFailed('Token in cookie is invalidate!')
當Token未提供或者無效時,直接拋出AuthenticationFailed異常
5. 講自定義的類放入項目的settings,否則不生效:
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ('bid_request_system_app.commons.authentications.CustomTokenAuthentication','rest_framework.authentication.SessionAuthentication',), }
6. 在相對應的view方法前面加上相應的注解:
@api_view(['GET', 'POST']) @csrf_exempt @login_required() @authentication_classes([CustomTokenAuthentication]) @permission_classes([IsAuthenticated, IsAdminUser]) def department_management_view(request):
這樣的話就OK了。