Django REST Framework-为自定义JWT身份验证覆盖JSONWebTokenAuthentication

时间:2018-06-28 09:01:05

标签: django authentication jwt

我有一个带有外部数据库的Django应用程序,这意味着对于每个用户请求,我都将SQL查询发送到外部数据库服务器。不存在本地数据库(如sqllite等)。同样,应使用JWT来验证用户身份。 为此,我已经覆盖了ObtainJSONWebToken视图:

class ObtainJWT(ObtainJSONWebToken):
    def post(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')

        # verify that user with given credentials exist in db
        resp = requests.post(settings.SERVER_HOST+"/auth/",
                             json={"username":username, "password":password})
        if resp.status_code == status.HTTP_401_UNAUTHORIZED:
            return Response({'error':'Invalid credentials'},
                            status=status.HTTP_401_UNAUTHORIZED)

        # create token
        payload = jwtutils.jwt_payload_handler(username, password, api_settings.JWT_EXPIRATION_DELTA)
        token = jwt_encode_handler(payload)
        return Response({'token': token},
                        status=status.HTTP_200_OK)

还有jwt_payload_handler中的jwtutils

def jwt_payload_handler(username, password, delta):
    # custom payload handler
    payload = {
        'username': username,
        'password': password,
        'exp': datetime.utcnow() + delta
    }

    return payload

现在,我无需使用任何User对象就可以成功获取令牌。但是,当使用获得的令牌(用户尝试使用令牌访问受保护的路由)时,将返回{"detail":"Invalid signature."}。我认为这是因为我正在使用的DRF的JSONWebTokenAuthentication类具有authenticate_credentials方法,该方法检查本地DB(https://github.com/GetBlimp/django-rest-framework-jwt/blob/master/rest_framework_jwt/authentication.py#L59)中是否存在具有给定凭据的用户,因此出现错误。因此,我决定创建自定义身份验证类。 我写的是:

class JSONWebTokenAuthentication(BaseAuthentication):
"""
Token based authentication using the JSON Web Token standard.
"""
def get_jwt_value(self, request):
    auth = get_authorization_header(request).split()
    auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()

    if not auth:
        if api_settings.JWT_AUTH_COOKIE:
            return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
        return None

    if smart_text(auth[0].lower()) != auth_header_prefix:
        return None

    if len(auth) == 1:
        msg = _('Invalid Authorization header. No credentials provided.')
        raise exceptions.AuthenticationFailed(msg)
    elif len(auth) > 2:
        msg = _('Invalid Authorization header. Credentials string '
                'should not contain spaces.')
        raise exceptions.AuthenticationFailed(msg)

    return auth[1]

def authenticate(self, request):
    """
    Returns a two-tuple of `User` and token if a valid signature has been
    supplied using JWT-based authentication.  Otherwise returns `None`.
    """
    jwt_value = self.get_jwt_value(request)
    if jwt_value is None:
        return None

    try:
        payload = jwt_decode_handler(jwt_value)
    except jwt.ExpiredSignature:
        msg = ('Signature has expired.')
        raise exceptions.AuthenticationFailed(msg)
    except jwt.DecodeError:
        msg = _('Error decoding signature.')
        raise exceptions.AuthenticationFailed(msg)
    except jwt.InvalidTokenError:
        raise exceptions.AuthenticationFailed()

    return (None, payload)

但是,这不起作用。我返回的是None而不是有效的User对象。在Django的身份验证过程中,稍后的某个地方将读取并测试该值是否为is_authenticated()返回True。返回None显然会导致{"detail":"You do not have permission to perform this action."}

我是Django和JWT的新手,重写身份验证类的最佳方法是什么,因此我将无法在本地保存任何Django User并且在Django身份验证过程中不会破坏任何内容?还是我需要重写一些权限类?预先感谢。

1 个答案:

答案 0 :(得分:0)

不确定是否解决了这个问题,但是是否在rest框架设置中添加了自定义身份验证类?像这样的东西:

  REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'myapp.location.JSONWebTokenAuthentication' #location of your custom authentication class.
        ),
    }