前言
最近在研究學(xué)習(xí)django rest framework中,對于運(yùn)用過的一些技術(shù)點進(jìn)行總結(jié),因此設(shè)置相關(guān)技術(shù)專題嚎幸。
基礎(chǔ)
在官網(wǎng)中對Authentication解釋如下:
Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The permission and throttling policies can then use those credentials to determine if the request should be permitted.
大概意思就是說Authentication是一種把request信息和用戶身份信息綁定起來的機(jī)制,也就用來驗證登錄用戶评腺。
對于本篇內(nèi)容主要講解的TokenAuthentication實際上是Authentication驗證方式的一種载庭,主要是用于數(shù)據(jù)庫中通過用戶登錄時創(chuàng)建一個Token存儲在數(shù)據(jù)庫中扩借,每次都會從http header中取出來于用戶客戶端的token進(jìn)行比對腾降。TokenAuthentication主要是運(yùn)用于移動端和服務(wù)端進(jìn)行交互驗證系洛。
用法
首先先把下面的代碼放進(jìn)settings.py中螃征,用于調(diào)用各個類中的驗證方法搪桂,把user放進(jìn)request中。request請求的請求盯滚,Token信息是放在auth字段中
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
....
'rest_framework.authentication.TokenAuthentication',
)
}
然后踢械,在setting中加入authtoken用于創(chuàng)建Token表存儲用戶登陸時的token
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
接著在urls.py中增加:
from rest_framework.authtoken import views
urlpatterns = [
path('api-token-auth/', views.obtain_auth_token),
]
原理
第一步:
在settings.py添加AUTHENTICATION類TokenAuthentication
,在執(zhí)行請求的時候魄藕,會調(diào)用authenticate
方法内列。調(diào)用之前,先通過get_authorization_header
把Token取出來背率,注意话瞧,在用戶做登錄請求的時候,注意Token是放在http header中寝姿,參數(shù)格式:Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b交排,接著split 拿到Token信息
源碼:
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
"""
keyword = 'Token'
model = None
"""
A custom token model may be used, but must have the following properties.
* key -- The string identifying the token
* user -- The user to which the token belongs
"""
def authenticate(self, request):
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != self.keyword.lower().encode():
return None
if len(auth) == 1:
msg = _('Invalid token header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid token header. Token string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
try:
token = auth[1].decode()
except UnicodeError:
msg = _('Invalid token header. Token string should not contain invalid characters.')
raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(token)
第二步:
拿到用戶request中的Token后,再調(diào)用authenticate_credentials
方法進(jìn)行驗證:使用get_model方法取到數(shù)據(jù)庫存儲的model饵筑,在model中進(jìn)行查詢是否包含Token對應(yīng)的信息進(jìn)行判斷埃篓。
源碼:
def authenticate_credentials(self, key):
model = self.get_model()
try:
token = model.objects.select_related('user').get(key=key)
except model.DoesNotExist:
raise exceptions.AuthenticationFailed(_('Invalid token.'))
if not token.user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (token.user, token)
def authenticate_header(self, request):
return self.keyword
def get_model(self):
if self.model is not None:
return self.model
from rest_framework.authtoken.models import Token
return Token
第三步
如果用戶Token表中沒有信息,用戶在請求的過程中根资,會調(diào)用試圖方法中的get_or_create
進(jìn)行創(chuàng)建表架专,并寫入用戶Token信息同窘。
urlpatterns = [
path('api-token-auth/', views.obtain_auth_token),
]
class ObtainAuthToken(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
if coreapi is not None and coreschema is not None:
schema = ManualSchema(
fields=[
coreapi.Field(
name="username",
required=True,
location='form',
schema=coreschema.String(
title="Username",
description="Valid username for authentication",
),
),
coreapi.Field(
name="password",
required=True,
location='form',
schema=coreschema.String(
title="Password",
description="Valid password for authentication",
),
),
],
encoding="application/json",
)
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({'token': token.key})
總結(jié)
缺點:Django rest framework的Token驗證,在數(shù)據(jù)庫中的Token表存儲的Token信息缺少一個過期時間來對Token進(jìn)行管理胶征,導(dǎo)致別人竊取或仿造Token進(jìn)行攻擊塞椎,所以這種驗證方式并不安全。