JWT 的优点
优点:无状态,服务端不保存 token
缺点:生成的 token 在有效期内一直有效,无论用户注销、修改密码。(可以通过存 redis,通过代码进行删除 redis 里的 token 解决。)
开始使用
配置
setting.py 里面设置默认值
1 2 3 4 5 6 7 8 9 10
| REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', ), } JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), 'JWT_AUTH_HEADER_PREFIX': 'JWT', 'JWT_ALLOW_REFRESH': False, }
|
这里的 DEFAULT_AUTHENTICATION_CLASSES 是默认设置,也可以给类视图自定义
views.py
1 2 3 4 5 6 7 8 9
| class XxxView(APIView):
|
authentication_classes = [JSONWebTokenAuthentication]
urls.py 里面配置生成 token 的路由
1 2
| from rest_framework_jwt.views import obtain_jwt_token path('api-token-auth/', obtain_jwt_token),
|
使用
先访问 api-token-auth 接口,这个接口只能通过 POST 访问,带上用户名和密码,认证成功后会生成一个 token 类似于:
1 2 3
| { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Iml1eHQiLCJleHAiOjE3MDc5MDI5MTUsImVtYWlsIjoiaXV4dEBxcS5jb20ifQ.tHc3koO95mEh_HKUc3QNge0KwBYwoRz4n4bltq4xadg" }
|
拿着这个 token 去请求接口:
1 2
| curl --location 'http://localhost:8000/jwttest/carts/' \ --header 'Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Iml1eHQiLCJleHAiOjE3MDc5MDI5MTUsImVtYWlsIjoiaXV4dEBxcS5jb20ifQ.tHc3koO95mEh_HKUc3QNge0KwBYwoRz4n4bltq4xadg'
|
我们前缀设置的是 JWT, 所以在请求时在 Header 里带上 JWT
即可
修改默认返回值
默认的返回值仅有 token,通过修改该视图的返回值可以完成我们的需求。在应用中新建一个 utils.py 文件:
1 2 3 4 5 6 7
| def jwt_response_payload_handler(token, user=None, request=None):
return { 'token': token, 'id': user.id, 'username': user.username, }
|
在 settings.py 里面引用
1 2 3 4 5 6
| JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), 'JWT_ALLOW_REFRESH': False, 'JWT_AUTH_HEADER_PREFIX': 'JWT', 'JWT_RESPONSE_PAYLOAD_HANDLER': '< app名字 >.utils.jwt_response_payload_handler', }
|
这样获取 token 的时候返回:
1 2 3 4 5
| { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Iml1eHQiLCJleHAiOjE3MDc5MDMwODMsImVtYWlsIjoiaXV4dEBxcS5jb20ifQ.YR0X8-b1gEPnp-xZd0wavszWVwBQVBaCnd4_LDqnGn8", "id": 1, "username": "iuxt" }
|
JWT 判断权限
permissions.py
1 2 3 4 5 6 7 8 9 10 11 12 13
| from rest_framework_jwt.authentication import jwt_decode_handler
class IsOwnOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj):
token = request.META.get('HTTP_AUTHORIZATION')[5:] token_user = jwt_decode_handler(token) if token_user: return obj.user.id == token_user.get('user_id') return False
|
在类视图里定义权限
1 2 3 4 5 6
| from .permissions import IsOwnOrReadOnly
class GameDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Game.objects.all() serializer_class = GameSerializer permission_classes = [IsAuthenticated, IsOwnOrReadOnly]
|