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

JWT在Postman验证正常但Flutter移动端验证失败问题排查

Node.js JWT在Flutter移动端验证失败问题

使用Node.js后端生成JWT令牌对Flutter移动端用户进行身份验证时遇到问题:Postman中测试时令牌的生成与验证均正常,但移动端始终判定令牌无效。

后端(Node.js)代码

登录与令牌生成逻辑

const loginUser = async (req, res) => {
  const { username, password } = req.body;
  try {
    const user = await User.findOne({ username })
        .populate('address', 'street reference city state postalCode coordinates isDefault');

    if (!user) {
      return res.status(400).json({ message: 'Usuário não corresponde' });
    }

    const isMatch = await bcrypt.compare(password, user.password);

    if (!isMatch) {
      return res.status(400).json({ message: 'Senha inválida' });
    }

    const token = generateToken(user);

    // 将Mongoose文档转为JS对象
    const userObj = user.toObject();
    delete userObj.password; // 移除密码字段

    res.json({ data: userObj, token });
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Erro no servidor');
  }
};

const generateToken = (user) => {
  return jwt.sign({ id: user._id, role: user.role }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES_IN });
};

const verifyToken = (token) => {
  return jwt.verify(token, process.env.JWT_SECRET);
};

身份验证中间件(authMiddleware.js)

const authMiddleware = async (req, res, next) => {
  const token = req.header('Authorization')?.replace('Bearer ', ''); 
  if (!token) {
    return res.status(401).json({ message: 'Acesso negado. Nenhum token fornecido.' });
  }

  try {
    const decoded = jwtUtil.verifyToken(token, process.env.JWT_SECRET);

    req.user = await User.findById(decoded.id).select('-password');

    if (!req.user) {
      return res.status(404).json({ message: 'Usuário não encontrado' });
    }
    next();
  } catch (err) {
    console.error('Algo deu errado com a verificação de token:', err.message);
    res.status(401).json({ message: 'Token não é válido' });
  }
};

const verifyAndAuthorization = (req, res, next) => {
  authMiddleware(req, res, () => {
    if (req.user.role === "Admin" || req.user.role === "Manager" || req.user.role === "Vendor" || req.user.role === "Customer" || req.user.role === "Delivery") {
      next()
    } else {
      res.status(403).json({ msg: "Você não tem autorização para acessar esta rota." })
    }
  })
}

移动端(Flutter/Dart)代码

Future<void> sendLogin() async {
    try {
      if (formKeyLorgin.currentState?.saveAndValidate() ?? false) {
        final username = formKeyLorgin.currentState?.fields['username']?.value;
        final password = formKeyLorgin.currentState?.fields['password']?.value;
        final response = await _authRepository.login(username, password);
        userData.value = UserModel.fromJson(response);
        String user = jsonEncode(response['data']);
        String token = jsonEncode(response['token']);
        log(user);
        _storageService
          ..box.write('user', user)
          ..saveLogged(isLogged.value)
          ..saveToken(token);
        log(token);
        Get.offAndToNamed('/home');
      } else {}
    } catch (e) {
      SnackbarService.showErrorMessage('$e');
      log("Erro: $e", error: e);
    }
  }

已执行的排查步骤

  • 令牌结构:确认Postman与移动端的令牌结构及载荷完全一致。
  • 时间同步:确保服务器与设备时钟同步,排除令牌过期问题。
  • HTTP请求头:检查Postman与移动端的HTTP请求头,确认Authorization头设置正确。
  • 网络请求:通过开发者工具检查网络请求,未发现差异或错误。
  • 令牌存储:尝试在设备上用不同方式存储令牌(如安全存储、Shared Preferences),排除存储相关问题。

附加信息

  • 设备与系统:[请指定设备及操作系统]
  • 移动端框架:Flutter/Dart
  • JWT库:jsonwebtoken
  • 网络环境:本地环境(含NGROK与不含NGROK两种情况)
  • 数据库:MongoDB Atlas

问题排查与解决建议

  1. 移除令牌的JSON编码处理
    移动端代码中使用jsonEncode(response['token'])会给JWT令牌添加多余的双引号,导致后端验证失败。修改为直接存储原始令牌:

    String token = response['token']; // 去掉jsonEncode调用
    

    可以打印存储前的令牌内容,确认没有多余的引号或转义字符。

  2. 修正后端验证函数的调用参数
    后端verifyToken函数仅接收一个token参数,但authMiddleware中调用时传入了两个参数(token和process.env.JWT_SECRET)。虽然当前逻辑不会直接报错,但为了避免混淆,建议统一参数:

    • 要么修改verifyToken函数支持传入密钥:
      const verifyToken = (token, secret) => {
        return jwt.verify(token, secret || process.env.JWT_SECRET);
      };
      
    • 要么在authMiddleware中直接调用jwt.verify,或者去掉多余的参数:
      const decoded = jwtUtil.verifyToken(token);
      
  3. 验证请求头的令牌格式
    在移动端发送请求时,确保Authorization头严格遵循Bearer <令牌>格式,令牌前后无多余空格。可以在请求拦截器中打印完整的请求头内容,确认令牌与Postman中使用的完全一致。

内容的提问来源于stack exchange,提问作者João Mello

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 12:19:50