JWT认证的REST框架
概述
这个包提供对Django REST framework的JSON Web Token 认证支持。
需要满足条件
- Python (2.7, 3.3, 3.4, 3.5)
- Django (1.8, 1.9, 1.10)
- Django REST Framework (3.0, 3.1, 3.2, 3.3, 3.4, 3.5)
安全
与JWT的一些更典型的用法不同,此模块仅生成身份验证令牌,该身份验证令牌将验证请求DRF保护的API资源之一的用户。实际的请求参数本身不包含在JWT声明中,这意味着它们没有被签名并且可能被篡改。 您仅应通过SSL / TLS公开API端点,以防止内容篡改和某些类型的重放攻击。
安装
使用pip
方式安装
$ pip install djangorestframework-jwt
用法
在settings.py
中,添加JSONWebTokenAuthentication
到Django rest framework的DEFAULT_AUTHENTICATION_CLASSES
中。
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
在urls.py
中,添加以下URL路由,以允许通过包含用户名和密码的POST
获得令牌。
from rest_framework_jwt.views import obtain_jwt_token
#...
urlpatterns = [
'',
# ...
url(r'^api-token-auth/', obtain_jwt_token),
]
如果您拥有一个用户名为admin和密码为password123的用户,则可以通过在终端中执行以下操作来简单测试端点是否正常运行。
$ curl -X POST -d "username=admin&password=password123" http://localhost:8000/api-token-auth/
或者,您可以使用Django REST框架支持的所有内容类型来获取auth令牌。 例如:
$ curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"password123"}' http://localhost:8000/api-token-auth/
您是不是要找: [Now in order to access protected api urls you must include the Authorization: JWT <your token> header.](javascript:void(0))
现在,为了访问受保护的api网址,您必须包含Authorization:JWT <your_token>
标头。
$ curl -H "Authorization: JWT <your_token>" http://localhost:8000/protected-url/
刷新令牌
如果JWT_ALLOW_REFRESH
为True,则可以“刷新”未过期的令牌以获得具有更新的过期时间的全新令牌。 添加如下网址格式:
from rest_framework_jwt.views import refresh_jwt_token
# ...
urlpatterns = [
# ...
url(r'^api-token-refresh/', refresh_jwt_token),
]
如下所示将现有令牌传递到刷新端点:{“ token”:EXISTING_TOKEN}
。 请注意,只有未过期的令牌才有效。 JSON响应看起来与正常的获取令牌端点{“ token”:NEW_TOKEN}
相同。
$ curl -X POST -H "Content-Type: application/json" -d '{"token":"<EXISTING_TOKEN>"}' http://localhost:8000/api-token-refresh/
可以重复使用令牌刷新(令牌1->令牌2->令牌3),但是此令牌链将原始令牌(使用用户名/密码凭证获取)的时间存储为orig_iat
。 您最多只能将令牌刷新到JWT_REFRESH_EXPIRATION_DELTA
。
一个典型的用例是一个Web应用程序,您想让用户“登录”该网站而不必重新输入密码,或者在令牌过期之前被吓倒了。 想象一下,他们有一个1个小时的令牌,正好在他们仍在做某事的最后一刻。 使用移动设备,您也许可以存储用户名/密码来获取新令牌,但这在浏览器中并不是一个好主意。 每次用户加载页面时,您都可以检查是否存在现有的未过期令牌,如果该令牌即将过期,请刷新该令牌以扩展会话。 换句话说,如果用户正在活跃地使用您的网站,则他们可以保持其“会话(session)”有效。
验证令牌
在某些微服务架构中,身份验证由单个服务处理。 其他服务委托确认用户已登录到此身份验证服务的责任。 这通常意味着服务会将将从用户收到的JWT传递给身份验证服务,并在将受保护资源返回给用户之前等待JWT有效的确认。
此软件包使用验证端点支持此设置。 添加以下网址格式:
from rest_framework_jwt.views import verify_jwt_token
#...
urlpatterns = [
# ...
url(r'^api-token-verify/', verify_jwt_token),
]
将令牌传递到验证端点将返回200响应,如果令牌有效,则返回令牌。 否则,它将返回一个400 Bad Request(错误请求)以及一个指出令牌无效的原因的错误。
$ curl -X POST -H "Content-Type: application/json" -d '{"token":"<EXISTING_TOKEN>"}' http://localhost:8000/api-token-verify/
其它配置
JWT_AUTH = {
'JWT_ENCODE_HANDLER':
'rest_framework_jwt.utils.jwt_encode_handler',
'JWT_DECODE_HANDLER':
'rest_framework_jwt.utils.jwt_decode_handler',
'JWT_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_payload_handler',
'JWT_PAYLOAD_GET_USER_ID_HANDLER':
'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',
'JWT_RESPONSE_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_response_payload_handler',
'JWT_SECRET_KEY': settings.SECRET_KEY,
'JWT_GET_USER_SECRET_KEY': None,
'JWT_PUBLIC_KEY': None,
'JWT_PRIVATE_KEY': None,
'JWT_ALGORITHM': 'HS256',
'JWT_VERIFY': True,
'JWT_VERIFY_EXPIRATION': True,
'JWT_LEEWAY': 0,
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
'JWT_AUDIENCE': None,
'JWT_ISSUER': None,
'JWT_ALLOW_REFRESH': False,
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
'JWT_AUTH_HEADER_PREFIX': 'JWT',
'JWT_AUTH_COOKIE': None,
}
该软件包使用JSON Web令牌Python实现PyJWT,并允许修改其中的一些可用选项。
JWT_SECRET_KEY
这是用于签署JWT的密钥。确保这是安全的,并且不会共享或公开。
默认值为项目的settings.SECRET_KEY
。
JWT_GET_USER_SECRET_KEY
这是JWT_SECRET_KEY的更强大的版本。它是按用户定义的,因此,如果令牌被盗用,所有者可以轻松更改它。更改此值将使给定用户的所有令牌不可用。值应该是一个函数,仅接受用户作为参数并返回它的密钥。
默认为None
。
JWT_PUBLIC_KEY
这是cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey
类型的对象。它将用于验证传入的JWT的签名。设置后将覆盖JWT_SECRET_KEY
。阅读文档以获取更多详细信息。请注意,必须将JWT_ALGORITHM
设置为RS256
,RS384
或RS512
之一。
默认为None
。
JWT_PRIVATE_KEY
这是cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey
类型的对象。它将用于对JWT的签名组件进行签名。设置后将覆盖JWT_SECRET_KEY
。阅读文档以获取更多详细信息。请注意,必须将JWT_ALGORITHM
设置为RS256
,RS384
或RS512
之一。
默认为None
。
JWT_ALGORITHM
可能的值是PyJWT中任何受支持的密码签名算法。
默认值为“ HS256”
。
JWT_VERIFY
如果密码错误,则会引发一个jwt.DecodeError
告诉您。您仍然可以通过将JWT_VERIFY
设置为False
来获得有效负载。
默认值为True
。
JWT_VERIFY_EXPIRATION
您可以通过将JWT_VERIFY_EXPIRATION
设置为False
来关闭到期时间验证。如果没有到期验证,JWT将永远存在,这意味着攻击者可以无限期地使用泄露的令牌。
默认值为True
。
JWT_LEEWAY
这使您可以验证过去但不是很远的到期时间。例如,如果您有一个JWT有效负载,其有效时间设置为创建后的30秒,但您知道有时您将在30秒后对其进行处理,则可以将回旋时间设置为10秒,以便有一定的余量。
默认值为0
秒。
JWT_EXPIRATION_DELTA
这是Python的datetime.timedelta的一个实例。这将被添加到datetime.utcnow()来设置到期时间。
默认值为datetime.timedelta(seconds = 300)(5分钟)
。
JWT_AUDIENCE
这是一个字符串,将根据令牌的aud
字段(如果存在)进行检查。
默认值为None
(如果JWT上出现aud
,则失败)。
JWT_ISSUER
这是一个字符串,将根据令牌的iss
字段进行检查。
默认值为None
(不检查JWT上的iss
)。
JWT_ALLOW_REFRESH
启用令牌刷新功能。从rest_framework_jwt.views.obtain_jwt_token
发行的令牌将具有orig_iat
字段。默认为False
。
JWT_REFRESH_EXPIRATION_DELTA
令牌刷新的限制是一个datetime.timedelta
实例。这是原始令牌之后可以刷新未来令牌的时间。
默认值为datetime.timedelta(days = 7)(7天)
。
JWT_PAYLOAD_HANDLER
指定自定义函数以生成令牌有效载荷。
JWT_PAYLOAD_GET_USER_ID_HANDLER
如果您存储的user_id
与默认的有效负载处理程序不同,请实现此功能以从有效负载中获取user_id
。注意:不推荐使用JWT_PAYLOAD_GET_USERNAME_HANDLER
。
JWT_PAYLOAD_GET_USERNAME_HANDLER
如果您存储的username
与默认有效负载处理程序不同,请实施此功能以从有效负载中获取用户名。
JWT_RESPONSE_PAYLOAD_HANDLER
负责控制登录或刷新后返回的响应数据。重写以返回自定义响应,例如包括用户的序列化表示形式。默认返回JWT
令牌。
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'user': UserSerializer(user, context={'request': request}).data
}
默认是 {'token': token}
JWT_AUTH_HEADER_PREFIX
您可以修改需要与令牌一起发送的Authorization标头值前缀。 默认值为JWT。 PR#4中引入了此决定,以允许在DRF中同时使用此程序包和OAuth2。
用于令牌和授权标头的另一个常见值是Bearer
。
默认值为JWT
。
JWT_AUTH_COOKIE
如果除了授权标头之外还想使用http cookie
作为令牌的有效传输方式,则可以将其设置为字符串。 您在此处设置的字符串将用作在请求令牌时将在响应标头中设置的cookie名称。 令牌验证过程还将调查此cookie(如果已设置)。 如果请求中同时包含标头和cookie,则“授权”标头具有优先权。
默认值为“None”
,创建令牌时不设置cookie,或在验证令牌时不接受。
扩展JSONWebTokenAuthentication
现在,JSONWebTokenAuthentication假定JWT将出现在标头或cookie(如果已配置)中(请参阅JWT_AUTH_COOKIE)。 JWT规范不需要这样做(请参阅:进行服务调用)。 例如,JWT可以出现在查询字符串中。 如果用户无法设置标头(例如HTML中的src元素),则需要具有在查询字符串中发送JWT的功能。
为了实现此功能,用户可以编写自定义身份验证
:
class JSONWebTokenAuthenticationQS(BaseJSONWebTokenAuthentication):
def get_jwt_value(self, request):
return request.QUERY_PARAMS.get('jwt')
建议使用BaseJSONWebTokenAuthentication
,这是一个新的基类,没有解析HTTP标头的逻辑。
手动创建新令牌
有时您可能需要手动生成令牌,例如在创建帐户后立即将令牌返回给用户。 您可以按照以下步骤进行操作:
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)