DRF -- Jwt认证使用流程
1. jwt (django_1.10及以下)
1. Jwt介绍
1.1 构成
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).
该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供
者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所
必须的声明信息,该token也可直接被用于认证,也可被加密
JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
1. header
jwt的头部承载两部分信息:
1. 声明类型,这里是jwt
2. 声明加密的算法 通常直接使用 HMAC SHA256
定义一个header
{
'typ': 'JWT',
'alg': 'HS256'
}
然后将头部Json进行base64加密(该加密是可以对称解密的),构成了第一部分.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
2. payload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
1. 标准中注册的声明
a. iss: jwt签发者
b. sub: jwt所面向的用户
c. aud: 接收jwt的一方
d. exp: jwt的过期时间,这个过期时间必须要大于签发时间
e. nbf: 定义在什么时间之前,该jwt都是不可用的.
f. iat: jwt的签发时间
g: jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
2. 公共的声明
a. 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.
b. 但不建议添加敏感信息,因为该部分在客户端可解密.
3. 私有的声明
a. 私有声明是提供者和消费者所共同定义的声明,
b. 一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
定义一个payload:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
然后将其进行base64加密,得到JWT的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
3. signature
JWT的第三部分是一个签证信息,这个签证信息由三部分组成:
1. header (base64后的)
2. payload (base64后的)
3. secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
# javascript如果要模拟生成你的jwttoken,可能可以采用以下代码生成[注意:伪代码]
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分用.连接成一个完整的字符串,构成了最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
**
1.2 注意事项
secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了
2. 模块下载
pip install djangorestframework-jwt -i https://mirrors.aliyun.com/pypi/simple
3. 全局配置JWT
项目目录/settings.py
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',
),
}
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': 'baler.utils.jwt_response_payload_handler', # 默认登录成功只有token,可以自定义登录成功的返回数据
'JWT_SECRET_KEY': 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(days=365),
'JWT_AUDIENCE': None,
'JWT_ISSUER': None,
'JWT_ALLOW_REFRESH': False,
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=365),
'JWT_AUTH_HEADER_PREFIX': 'Bearer', # token前缀 如token的值必须以Bearer开头+一个空格+token串,如Bearer df1lmvc...
'JWT_AUTH_COOKIE': None,
}
4. 手动生成Jwt的内置方法
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)
5. 使用JWT内置认证接口
1. 路由
obtain_jwt_token 是 Jwt 自己实现的登录视图,可以直接拿来用
app/urls.py
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path(r'authorizations/', obtain_jwt_token, name='authorizations'),
# url可以修改
# path(r'login/', obtain_jwt_token, name='login'),
]
2. 自定义验证成功的响应数据
scadasoft/utils.py
def jwt_response_payload_handler(token, user=None, request=None):
"""
自定义jwt认证成功返回数据
"""
return {
'token': token,
'id': user.id,
'username': user.username
}
6. 单独配置JWT
app/views.py
class StationInfoView(APIView):
"""
空压站
"""
authentication_classes = [JSONWebTokenAuthentication] # 配置Jwt认证类,如果全局配置了这里可以省略,如果某个视图不想认证,如LoginView 可以设置为 authentication_classes = []
permission_classes = [IsAuthenticated] # 配置权限,登录成功才可以访问本接口,同上
def get(self, request):
station_objs = air_compressor_station.objects.filter(
existence=True,
)
data_count = station_objs.count()
result = StationSerializer(station_objs, many=True)
return PublicRes(data=result.data, data_count=data_count)
2. simplejwt(django_1.10以上)
2.0 官方网站
https://django-rest-framework-simplejwt.readthedocs.io/zh-hans/latest/settings.html
2.1 安装
pip install djangorestframework-simplejwt
2.2 配置
settings.py
# settings.py
INSTALLED_APPS = [
...
'rest_framework',
'rest_framework_simplejwt',
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
# 认证类
# 先进行token的验证,如果没有携带token就进行session认证,如果没有session就就基本认证
# 认证顺序是从上到下,需要哪个加哪个
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
SIMPLE_JWT = {
# token有效时长(返回的 access 有效时长)
'ACCESS_TOKEN_LIFETIME': datetime.timedelta(seconds=30),
# token刷新的有效时间(返回的 refresh 有效时长)
'REFRESH_TOKEN_LIFETIME': datetime.timedelta(seconds=20),
}
urls.py
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView
urlpatterns = [
path('authorizations/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('verify/', TokenVerifyView.as_view(), name='token_verify'),
]
2.3 其他配置
settings.py
# Django project settings.py
from datetime import timedelta
...
SIMPLE_JWT = {
# datetime.timedelta指定访问令牌有效时间的对象。该timedelta值在令牌生成期间添加到当前 UTC 时间以获得令牌的默认“exp”声明值。
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=5),
# datetime.timedelta指定刷新令牌有效时间的对象。该timedelta值在令牌生成期间添加到当前 UTC 时间以获得令牌的默认“exp”声明值。
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
# 设置为True时,如果将刷新令牌提交给 TokenRefreshView,则新的刷新令牌将与新的访问令牌一起返回。这个新的刷新令牌将通过 JSON 响应中的“刷新”键提供。新的刷新令牌将有一个更新的到期时间,该时间是通过将设置中的 timedelta 添加REFRESH_TOKEN_LIFETIME 到发出请求时的当前时间来确定的。如果黑名单应用程序正在使用中并且BLACKLIST_AFTER_ROTATION设置为True,则提交到刷新视图的刷新令牌将被添加到黑名单中。
"ROTATE_REFRESH_TOKENS": False,
# 设置为True时,如果黑名单应用程序正在使用且设置设置True为 ,则提交给 的刷新令牌 将被添加到黑名单。您需要在设置文件中添加才能使用此设置。 TokenRefreshViewROTATE_REFRESH_TOKENSTrue’rest_framework_simplejwt.token_blacklist’,INSTALLED_APPS
"BLACKLIST_AFTER_ROTATION": False,
"UPDATE_LAST_LOGIN": False,
"ALGORITHM": "HS256",
"SIGNING_KEY": settings.SECRET_KEY,
"VERIFYING_KEY": "",
"AUDIENCE": None,
"ISSUER": None,
"JSON_ENCODER": None,
"JWK_URL": None,
"LEEWAY": 0,
"AUTH_HEADER_TYPES": ("Bearer",),
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "user_id",
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
"TOKEN_TYPE_CLAIM": "token_type",
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
"JTI_CLAIM": "jti",
"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
"TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
"TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
"TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
"TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}
python防脱发技巧

浙公网安备 33010602011771号