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

Flutter中SharedPreferences存用户ID的正确性、安全及GetX存储问题

关于登录系统自动登录与数据存储的疑问

问题背景

我正在开发登录系统,已在main.dart的import语句后、void main前声明全局变量uID和nameOfUser。目前用SharedPreferences存储用户信息,通过检查uID是否已保存实现用户自动登录,无需重复登录。想确认:

  1. 该方式是否正确?
  2. 是否存在被破解的风险?
  3. 能否在GetX状态管理器中实现类似SharedPreferences的数据存储功能?

附上相关代码:

void auth() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();

    try {
      var authResponse = await _dio.post(
        '/app_api/login.php',
        data: {
          "mobile": _phoneController.text,
          "pass": _passController.text,
        },
        options: Options(
          headers: {
            "Content-Type": "application/json",
          },
        ),
      );

      var responseData = authResponse.data;

      if (responseData['status'] == true) {
        setState(() {
          uID = responseData['UID'];
          nameOfUser = responseData['Name'];
        });
        await prefs.setString('uID', '${responseData['UID']}');
        await prefs.setString('nameOfUser', '${responseData['Name']}');
        Get.offAll(() => const HomeScreen());
      } else {
        _passController.clear();
        Get.snackbar(
          'Login Failed',
          'Please enter correct mobile number and password',
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
      }
    } catch (e) {
      Get.snackbar(
        'Error',
        'An error occurred during login: $e',
        backgroundColor: Colors.red,
        colorText: Colors.white,
      );
    }
  }

解答

1. 当前自动登录方式是否正确?

从功能实现角度,这个方式可行,但有几个优化点:

  • 全局变量uID和nameOfUser在APP重启后会丢失初始值,必须在APP启动时从SharedPreferences读取并赋值,否则启动后全局变量会是默认值,直到登录操作触发才更新。
  • 仅靠uID做自动登录逻辑存在局限性:如果后端有用户会话过期机制,本地存储的uID无法验证用户是否仍处于有效登录状态。建议同时存储后端返回的登录令牌(Token),自动登录时携带Token请求后端验证有效性,再决定是否进入首页。

2. 是否存在被破解的风险?

是的,存在明确风险:

  • SharedPreferences是明文存储在设备沙盒目录中,root过的设备可直接查看数据。如果后端仅靠uID验证用户身份,攻击者获取uID后可能伪造请求冒用身份。
  • 优化方案:优先存储后端签发的JWT令牌或会话Token;如果必须存储uID,可对其进行AES加密后再存储,提升破解成本。

3. 能否在GetX中实现类似SharedPreferences的数据存储功能?

可以实现,两种常见方案:

方案一:在GetX Controller中封装SharedPreferences

将存储逻辑封装到GetX控制器中,结合响应式状态管理实现数据同步:

class AuthController extends GetxController {
  final RxString uID = ''.obs;
  final RxString nameOfUser = ''.obs;
  late SharedPreferences prefs;

  @override
  void onInit() async {
    super.onInit();
    prefs = await SharedPreferences.getInstance();
    // 初始化时读取本地数据
    uID.value = prefs.getString('uID') ?? '';
    nameOfUser.value = prefs.getString('nameOfUser') ?? '';
  }

  Future<void> auth(String mobile, String pass) async {
    try {
      var authResponse = await _dio.post(
        '/app_api/login.php',
        data: {"mobile": mobile, "pass": pass},
        options: Options(headers: {"Content-Type": "application/json"}),
      );
      var responseData = authResponse.data;
      if (responseData['status'] == true) {
        uID.value = responseData['UID'];
        nameOfUser.value = responseData['Name'];
        await prefs.setString('uID', uID.value);
        await prefs.setString('nameOfUser', nameOfUser.value);
        Get.offAll(() => const HomeScreen());
      } else {
        Get.snackbar(
          '登录失败',
          '请输入正确的手机号和密码',
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
      }
    } catch (e) {
      Get.snackbar(
        '错误',
        '登录过程中发生错误: $e',
        backgroundColor: Colors.red,
        colorText: Colors.white,
      );
    }
  }
}

方案二:使用GetX官方的GetStorage插件

get_storage是轻量级本地存储方案,API更简洁且支持响应式:

  1. 添加依赖到pubspec.yaml:
dependencies:
  get_storage: ^2.1.1
  1. 在main.dart初始化:
void main() async {
  await GetStorage.init();
  runApp(const MyApp());
}
  1. 在GetX控制器中使用:
class AuthController extends GetxController {
  final RxString uID = ''.obs;
  final RxString nameOfUser = ''.obs;
  final storage = GetStorage();

  @override
  void onInit() {
    super.onInit();
    // 读取存储数据
    uID.value = storage.read('uID') ?? '';
    nameOfUser.value = storage.read('nameOfUser') ?? '';
  }

  Future<void> auth(String mobile, String pass) async {
    try {
      var authResponse = await _dio.post(
        '/app_api/login.php',
        data: {"mobile": mobile, "pass": pass},
        options: Options(headers: {"Content-Type": "application/json"}),
      );
      var responseData = authResponse.data;
      if (responseData['status'] == true) {
        uID.value = responseData['UID'];
        nameOfUser.value = responseData['Name'];
        // 存储数据
        storage.write('uID', uID.value);
        storage.write('nameOfUser', nameOfUser.value);
        Get.offAll(() => const HomeScreen());
      } else {
        Get.snackbar(
          '登录失败',
          '请输入正确的手机号和密码',
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
      }
    } catch (e) {
      Get.snackbar(
        '错误',
        '登录过程中发生错误: $e',
        backgroundColor: Colors.red,
        colorText: Colors.white,
      );
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 08:43:10