You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter与Django基于JWT令牌的登录存token无法保持认证问题

问题描述

我希望在Flutter端完成登录操作,但目前在Django服务器和Flutter客户端均无法保持认证状态。
我需要通过存储JWT令牌让用户在Flutter端保持认证状态,同时也希望Django服务器端也能维持用户的认证状态。

Django端代码(Views.py)
class LoginAPIView(APIView):
    def post(self, request):
        username = request.data.get('username')
        phonenumber = request.data.get('phonenumber')
        password = request.data.get('password')

        try:
            user = UserProfile.objects.get(
                username=username, phone_number=phonenumber)
        except UserProfile.DoesNotExist:
            # Handle the case where the user with the given username doesn't exist
            return Response({"message": "A user or phone number does not found."}, status=status.HTTP_400_BAD_REQUEST)
        print(user)
        if user.password == password:
            if user is not None:  # if user.check_password(password):
                # Password is correct, log in the user
                print(user)
                login(request, user)

        # Generate JWT token
            payload = {
                'user_id': user.id,
                'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=60),
                'iat': datetime.datetime.utcnow()
            }
            token = jwt.encode(payload, 'secret', algorithm='HS256')
            uname = username
        # Set the token as a cookie
            # print(token)"jwt": token,
            return Response({"message": "Successfully Login", "jwt": token, "uname": uname}, status=status.HTTP_200_OK)
            # response = Response(
            #     {"message": "Successfully Login", "uname": uname}, status=status.HTTP_200_OK)
            # response.set_cookie(key='jwt', value=token, httponly=True)

            return response
        else:
            # Password is incorrect
            raise AuthenticationFailed(
                {"message": "Password Incorrect"}, code=status.HTTP_401_UNAUTHORIZED)
Flutter端代码
onPressed: () async {
                var username = usernameController.text;
                final phonenumber = phoneNumberController.text;
                final password = passwordController.text;
                const apiUrl = 'http://127.0.0.1:8000/Login/';
                AuthService authService = AuthService();
                var token;
                final response = await http.post(
                  Uri.parse(apiUrl),
                  headers: {'Content-Type': 'application/json'},
                  body: jsonEncode({
                    'Authorization': 'Bearer $token',
                    'username': username,
                    'phonenumber': phonenumber,
                    'password': password,
                  }),
                );
                if (response.statusCode == 200) {
                  // Handle successful response
                  final Map<String, dynamic> responseData =
                      json.decode(response.body);
                  final String token = responseData['jwt'];
                  print(token);
                  setState(() {
                    message = responseData['message'];
                    uname = responseData['uname'];
                  });

                  
                  const storage = FlutterSecureStorage();

                  // await storage.write(key: 'jwt', value: token);

              
              
问题修复方案

Django端问题及修复

  1. 密码验证逻辑错误:直接对比user.password == password不成立,Django默认对密码做哈希存储,必须用user.check_password(password)验证密码。
  2. 代码缩进错误:生成JWT的代码块缩进层级错误,会导致非登录成功场景也执行JWT生成逻辑,需将其移入密码验证通过的分支内。
  3. 服务器端认证状态维持:login(request, user)已经会创建session维持后端状态,若需同时支持JWT验证,可将JWT存入HttpOnly Cookie,提升安全性;另外JWT密钥不要硬编码,建议从环境变量读取。

修复后的Views.py代码:

import os
from datetime import datetime, timedelta
from django.contrib.auth import login
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import AuthenticationFailed
from .models import UserProfile
import jwt

class LoginAPIView(APIView):
    def post(self, request):
        username = request.data.get('username')
        phonenumber = request.data.get('phonenumber')
        password = request.data.get('password')

        try:
            user = UserProfile.objects.get(
                username=username, phone_number=phonenumber)
        except UserProfile.DoesNotExist:
            return Response({"message": "用户或手机号不存在"}, status=status.HTTP_400_BAD_REQUEST)
        
        # 正确验证哈希密码
        if user.check_password(password):
            # 登录用户,维持后端session认证状态
            login(request, user)
            
            # 生成JWT令牌
            payload = {
                'user_id': user.id,
                'exp': datetime.utcnow() + timedelta(minutes=60),
                'iat': datetime.utcnow()
            }
            # 从环境变量读取密钥,避免硬编码
            jwt_secret = os.environ.get('JWT_SECRET_KEY', 'default_secret')
            token = jwt.encode(payload, jwt_secret, algorithm='HS256')
            
            # 返回响应并将JWT存入HttpOnly Cookie
            response = Response(
                {"message": "登录成功", "jwt": token, "uname": username}, 
                status=status.HTTP_200_OK
            )
            response.set_cookie(
                key='jwt', 
                value=token, 
                httponly=True, 
                secure=True, 
                samesite='Lax'
            )
            return response
        else:
            raise AuthenticationFailed({"message": "密码错误"}, code=status.HTTP_401_UNAUTHORIZED)

Flutter端问题及修复

  1. 登录请求冗余字段:登录时还未获取到token,无需在请求body中携带Authorization字段,该字段应在后续认证请求的headers中使用。
  2. 未实际存储JWT:需取消await storage.write(key: 'jwt', value: token)的注释,将token存入安全存储。
  3. 后续请求需携带JWT:每次向后端发起认证请求时,从安全存储中取出token,放在请求headers的Authorization: Bearer <token>中。

修复后的Flutter代码片段:

onPressed: () async {
  var username = usernameController.text;
  final phonenumber = phoneNumberController.text;
  final password = passwordController.text;
  const apiUrl = 'http://127.0.0.1:8000/Login/';
  
  final response = await http.post(
    Uri.parse(apiUrl),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({
      'username': username,
      'phonenumber': phonenumber,
      'password': password,
    }),
  );
  
  if (response.statusCode == 200) {
    final Map<String, dynamic> responseData = json.decode(response.body);
    final String token = responseData['jwt'];
    print(token);
    
    setState(() {
      message = responseData['message'];
      uname = responseData['uname'];
    });
    
    // 将JWT存入安全存储
    const storage = FlutterSecureStorage();
    await storage.write(key: 'jwt', value: token);
    
    // 登录成功后跳转至认证页面
    Navigator.pushReplacementNamed(context, '/home');
  } else {
    // 处理登录失败逻辑
    final errorData = json.decode(response.body);
    setState(() {
      message = errorData['message'];
    });
  }
}

后端JWT验证补充(可选)

若要让Django统一通过JWT验证请求,可使用djangorestframework-simplejwt扩展,配置后后端可同时支持session和JWT两种认证方式:

  1. 安装依赖:pip install djangorestframework-simplejwt
  2. 在settings.py中配置:
INSTALLED_APPS = [
    ...
    'rest_framework_simplejwt',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
}

内容的提问来源于stack exchange,提问作者Shahan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:54:56