我有一个带有外部数据库的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身份验证过程中不会破坏任何内容?还是我需要重写一些权限类?预先感谢。
答案 0 :(得分:0)
不确定是否解决了这个问题,但是是否在rest框架设置中添加了自定义身份验证类?像这样的东西:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'myapp.location.JSONWebTokenAuthentication' #location of your custom authentication class.
),
}