drf Web API運(yùn)用(一)--TokenAuthentication

前言

最近在研究學(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)行攻擊塞椎,所以這種驗證方式并不安全。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末睛低,一起剝皮案震驚了整個濱河市案狠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钱雷,老刑警劉巖骂铁,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異罩抗,居然都是意外死亡拉庵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門套蒂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钞支,“玉大人,你說我怎么就攤上這事操刀∷感” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵骨坑,是天一觀的道長撼嗓。 經(jīng)常有香客問我,道長欢唾,這世上最難降的妖魔是什么且警? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮礁遣,結(jié)果婚禮上斑芜,老公的妹妹穿的比我還像新娘。我一直安慰自己祟霍,他們只是感情好杏头,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浅碾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪续语。 梳的紋絲不亂的頭發(fā)上垂谢,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機(jī)與錄音疮茄,去河邊找鬼滥朱。 笑死根暑,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的徙邻。 我是一名探鬼主播排嫌,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼缰犁!你這毒婦竟也來了淳地?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤帅容,失蹤者是張志新(化名)和其女友劉穎颇象,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體并徘,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡遣钳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了麦乞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蕴茴。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖姐直,靈堂內(nèi)的尸體忽然破棺而出倦淀,到底是詐尸還是另有隱情,我是刑警寧澤简肴,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布晃听,位于F島的核電站,受9級特大地震影響砰识,放射性物質(zhì)發(fā)生泄漏能扒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一辫狼、第九天 我趴在偏房一處隱蔽的房頂上張望初斑。 院中可真熱鬧,春花似錦膨处、人聲如沸见秤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鹃答。三九已至,卻和暖如春突硝,著一層夾襖步出監(jiān)牢的瞬間测摔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留锋八,地道東北人浙于。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像挟纱,于是被迫代替她去往敵國和親羞酗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容