- 一、Token与Session
- 1. Session的弊端
- 2. Token认证机制
- 二、JWT的概念及构成
- 三、Djangorestframework-simplejwt
- 1. 安装与配置
- 2. 基本使用方法
- 3. 常用配置
- 4. 自定义生成的Token内容
- 5. JWT解码
我们之前已经学过session,它是将用户的敏感信息保存到服务端,而只给客户端一个sessionid(保存为cookie)作为与服务器端session交互的凭证。在用户通过验证并拿到sessionid后,几乎每次访问都需要携带sessionid,并需要服务端每次都从数据库中获取session信息。这就暴露了如下的弊端:
-
需要频繁查询数据库中的session,服务器压力大。
-
session是基于cookie的,如果cookie被截获,容易遭到CSRF攻击。
-
session被保存在用户第一次访问的服务器中(一般在内存中),如果有多个服务器,其他服务器无法获取到这些session信息,所以扩展性差。
-
用户通过验证后,由服务端对数据进行编码;
-
将加密后的数据保存到客户端,称为Token:由js进行读写,保存在Local Storage中;
-
以后再访问服务端时,附带Token,服务端进行验证、解码。
如此,便减小了服务端的压力,由于不依赖于cookie,所以无需担心CSRF攻击。并且,Token由客户端发送,不依赖于某一台服务器,所以扩展性很好。
二、JWT的概念及构成JWT的全称为JSON Web Token,它是一个开放的标准,定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。
JWT的本质就是一个字符串,它将用户信息保存到JSON字符串中,然后进行BSEA64编码,并附带一个签名(Signature),防止信息被篡改。
JWT看起来就是下面这样,因为是编码后的,所以是一堆乱七八糟的字符:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
它由三部分构成:
-
Header(头部):通常包含token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)以及其他信息。
-
Payload(载荷):是JWT的主体,通常包含用户主键、用户名、签发时客户信息(设备号、地址)、过期时间等。
注意:因为base64是可逆的,所以不要在JWT的payload或header中放置敏感信息,除非它们是加密的!
-
Signature(签名):是对上面两部分编码后的内容再进行加密后得到的密文。即:base64(Header)+"."+base64(Payload)通过SHA256加密后,得到的密文就是签名。
最后,将上面三部分通过.连接起来就得到了JWT。
三、Djangorestframework-simplejwtDjangorestframework-simplejwt是DRF(django rest framework)的一个JWT认证插件,为DRF提供了一个JWT认证后端。
1. 安装与配置-
使用pip安装:
pip install djangorestframework-simplejwt
-
全局配置:
在项目配置文件settings.py中,将JWTAuthentication添加到认证后端列表:
REST_frameWORK = { ... 'DEFAULT_AUTHENTICATION_CLASSES': ( ... 'rest_framework_simplejwt.authentication.JWTAuthentication', ) ... }
-
局部配置:
或者,仅在需要的视图中配置:
class UserCenterView(GenericAPIView): authentication_classes = [JWTAuthentication] ……
-
添加路由:
from rest_framework_simplejwt.views import ( TokenObtainPairView, TokenRefreshView) urlpatterns = [ # 获取token用的路由 path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), # 刷新token用的路由 path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh') ]
-
测试:
创建django超级用户:
python manage.py createsuperuser
使用浏览器访问路径api/token/,会显示以下界面:
输入用户名和密码点击POST后,验证通过就会返回token:
说明:
- access是token的主体;
- refresh是用来刷新token的,执行token刷新 *** 作时,只会返回一个新的access。
在之前配置的基础上(此处默认为全局),向视图写入权限等其他配置:
class UserAPIView(GenericAPIView): """ 测试的Api1 """ # 全局设置了认证后端,不再进行局部设置 # 设置权限认证,比如仅允许认证后的用户访问 permission_classes = [permissions.IsAuthenticated] queryset = User.objects.all() serializer_class = UserSerializer def get(self, request): return Response("只有登录后的用户才能访问哦")
然后进行token的获取:
运行项目,使用POST方法请求路径api/token/,并在body中附带username和password信息。即可得到access。
最后使用GET方法访问UserAPIView视图,分为两种情况:
- 错误情况:直接访问,没有附带Token,则会返回“身份认证信息未提供”的提示信息。
- 正确情况:将token添加到请求头的Authorization中,就会成功返回“只有登录后的用户才能访问哦”。
可以在项目配置文件中,对jwt进行配置:
# Django project settings.py from datetime import timedelta ... SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), 'ROTATE_REFRESH_TOKENS': False, 'BLACKLIST_AFTER_ROTATION': False, 'UPDATe_LAST_LOGIN': False, 'ALGORITHM': 'HS256', 'SIGNING_KEY': settings.SECRET_KEY, 'VERIFYING_KEY': None, 'AUDIENCE': None, 'ISSUER': 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', 'JTI_CLAIM': 'jti', 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', 'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), }
-
ACCESS_TOKEN_LIFETIME:
access token的有效时长,必须是一个datetime.timedelta对象。
-
REFRESH_TOKEN_LIFETIME:
refresh token的有效时长,必须是一个datetime.timedelta对象。
-
ROTATE_REFRESH_TOKENS:
刷新access token时,是否刷新refresh token。
-
UPDATE_LAST_LOGIN:
当设置为True时,用户进行登录时,会在自动更新auth_user表的last_login字段。
-
ALGORITHM:
生成签名部分的算法,可以使用’HS256’(默认), ‘HS384’, ‘HS512’。
-
AUTH_HEADER_NAME:
用于身份验证的authorization头名称,默认为HTTP_AUTHORIZATION。
比如获取authorization头的内容:
class ExampleView(GenericAPIView): ... def get(self, request): token = request.meta.get("HTTP_AUTHORIZATION")
Djangorestframework-simplejwt也是通过序列化器来序列化和反序列化JWT的,所以我们可以通过自定义序列化器的方式,定制token。
在定义序列化器的文件中:
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer class MyTokenPairSerializer(TokenObtainPairSerializer): @classmethod def get_token(cls, user): token = super().get_token(user) # 添加自定义内容 token['name'] = user.name …… return token
在视图文件中:
from rest_framework_simplejwt.views import TokenObtainPairView from .serializers import * class MyTokenObtainPairView(TokenObtainPairView): # 设置为自定义的token序列化器 serializer_class = MyTokenObtainPairSerializer
最后,与标准token视图一样,配置好url即可。
注意:上面的修改会同时作用于获取Token和刷新Token *** 作。
5. JWT解码在定义序列化器的文件中:
from rest_framework_simplejwt.serializers import TokenVerifySerializer from jwt import decode as jwt_decode class MyTokenVerifySerializer(TokenVerifySerializer): def validate(self, attrs): """ attrs['token']: 是请求的token settings.SECRET_KEY: setting.py默认的key 除非在配置文件中修改了 algorithms: 加密的方法 """ decoded_data = jwt_decode(attrs['token'], settings.SECRET_KEY, algorithms=["HS256"]) return decoded_data
在视图中:
from rest_framework_simplejwt.views import TokenObtainPairView, TokenViewbase class MyTokenVerifyView(TokenViewbase): """ 验证token得到用户信息 token: 验证的token """ serializer_class = MyTokenVerifySerializer
之后,访问该视图就会得到解码后的内容了。
当然,我们也可以通过python自带的base64库手动解码。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)