tXD29Yhja 发表于 2025-2-21 20:11:57

Django RestFramework 使用 JWT 认证

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),# Token 过期时间为一周
    'JWT_AUTH_HEADER_PREFIX': 'JWT',# Token的头为:JWT
    'JWT_ALLOW_REFRESH': False,
}
这里的 DEFAULT_AUTHENTICATION_CLASSES 是默认设置,也可以给类视图自定义
views.py
1
2
3
4
5
6
7
8
9
class XxxView(APIView):
    # 不用认证
    # authentication_classes = []

    # 基于jwt验证
    # authentication_classes =

    # 只有登录才能访问
    # permission_classes =
authentication_classes =
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: JWTeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.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):
      # if request.method in permissions.SAFE_METHODS:
      #   return True

      token = request.META.get('HTTP_AUTHORIZATION')# 从 header 中获取 token
      token_user = jwt_decode_handler(token)# 解析出来
      if token_user:
            return obj.user.id == token_user.get('user_id')# 证明 当前jwt验证的用户就是数据创建者
      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 =
页: [1]
查看完整版本: Django RestFramework 使用 JWT 认证