Token in query string with Django REST Framework's TokenAuthentication

醉酒当歌 提交于 2021-02-18 22:55:41

问题


In an API built with Django REST Framework authentication can be done using the TokenAuthentication method. Its documentation says the authentication token should be sent via an Authorization header.

Often one can send API-keys or tokens via a query string in order to authenticate, like https://domain.com/v1/resource?api-key=lala.

Is there a way to do the same with Django REST Framework's TokenAuthentication?


回答1:


By deafult DRF doesn't support query string to authenticate, but you can easily override their authenticate method in TokenAuthentication class to support it.

An example would be:

class TokenAuthSupportQueryString(TokenAuthentication):
    """
    Extend the TokenAuthentication class to support querystring authentication
    in the form of "http://www.example.com/?auth_token=<token_key>"
    """
    def authenticate(self, request):
        # Check if 'token_auth' is in the request query params.
        # Give precedence to 'Authorization' header.
        if 'auth_token' in request.QUERY_PARAMS and \
                        'HTTP_AUTHORIZATION' not in request.META:
            return self.authenticate_credentials(request.QUERY_PARAMS.get('auth_token'))
        else:
            return super(TokenAuthSupportQueryString, self).authenticate(request)



回答2:


class QueryStringBasedTokenAuthentication(TokenAuthentication):
    def authenticate(self, request):
        key = request.query_params.get('auth_token').strip()
        if key:
            return self.authenticate_credentials(key)
        return False

DRF has TokenAuthentication which looks for token in header. The method authenticate_credentials takes care verifying the token.




回答3:


As of 2018 using Django Rest Framework you can create your own Authentication class see http://getblimp.github.io/django-rest-framework-jwt/#extending-jsonwebtokenauthentication

class JSONWebTokenAuthenticationQS(BaseJSONWebTokenAuthentication):
    def get_jwt_value(self, request):
        return request.QUERY_PARAMS.get('jwt')

Then in the APIView class add authentication_classes = (JSONWebTokenAuthenticationQS,)

Or @authentication_classes((JSONWebTokenAuthenticationQS,))

On the view function.




回答4:


Following up on Scott Warren's answer. That was a step in the right direction, because the DRFJWT docs don't include the important authentication_classes line. But with that, as noted in Issue 441 there is a big problem that you can't mix the JWT authentication methods. I ended up with:

class JSONWebTokenAuthenticationQS(JSONWebTokenAuthentication):
    def get_jwt_value(self, request):
        return request.GET.get('jwt') or JSONWebTokenAuthentication.get_jwt_value(self, request)

which so far seems to work well. It uses JSONWebTokenAuthentication instead of the Base class, because it has to in order to use the original get_jwt_value method.




回答5:


refer answer of OmriToptix and Scott Warren and others website 1 and 2

for now most case use JSONWebTokenAuthentication, so now should override its get_jwt_value, full code is:

# from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.authentication import get_authorization_header

class JWTAuthByQueryStringOrHeader(JSONWebTokenAuthentication):
# class JWTAuthByQueryStringOrHeader(BaseJSONWebTokenAuthentication):
    """
    Extend the TokenAuthentication class to support querystring authentication
    in the form of "http://www.example.com/?jwt_token=<token_key>"
    """

    def get_jwt_value(self, request):
        # Check if 'jwt_token' is in the request query params.
        # Give precedence to 'Authorization' header.
        queryParams = request.query_params
        reqMeta = request.META

        if ('jwt_token' in queryParams) and ('HTTP_AUTHORIZATION' not in reqMeta):
            jwt_token = queryParams.get('jwt_token')
            # got jwt token from query parameter
            return jwt_token
        else:
            # call JSONWebTokenAuthentication's get_jwt_value
            # to get jwt token from header of 'Authorization'
            return super(JWTAuthByQueryStringOrHeader, self).get_jwt_value(request)

here save above code to apps/util/jwt_token.py, then NOT FORGET add related Django settings:

REST_FRAMEWORK = {
    ...
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'apps.util.jwt_token.JWTAuthByQueryStringOrHeader',
        ...
    ),
    ...
}

now frontend/web side can call api like this:

http://localhost:65000/api/v1/scripts/3d9e77b0-e538-49b8-8790-60301ca79e1d/script_word_export/?jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWVkMGEwZDgtMmFiYi00MDFkLTk5NTYtMTQ5MzcxNDIwMGUzIiwidXNlcm5hbWUiOiJsc2R2aW5jZW50IiwiZXhwIjoxNTMxOTAyOTU0LCJlbWFpbCI6InZpbmNlbnQuY2hlbkBuYXR1cmxpbmcuY29tIn0.wheM7Fmv8y8ysz0pp-yUHFqfk-IQ5a8n_8OplbYkj7s

to pass jwt_token into server side, get authorized to download/export the file.

while still support original method pass jwt token inside header 'Authorization':

POST http://localhost:65000/api/v1/scripts/3d9e77b0-e538-49b8-8790-60301ca79e1d/script_word_export/
Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMWVkMGEwZDgtMmFiYi00MDFkLTk5NTYtMTQ5MzcxNDIwMGUzIiwidXNlcm5hbWUiOiJsc2R2aW5jZW50IiwiZXhwIjoxNTMxOTAyOTU0LCJlbWFpbCI6InZpbmNlbnQuY2hlbkBuYXR1cmxpbmcuY29tIn0.wheM7Fmv8y8ysz0pp-yUHFqfk-IQ5a8n_8OplbYkj7s


来源:https://stackoverflow.com/questions/29433416/token-in-query-string-with-django-rest-frameworks-tokenauthentication

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!