[{"data":1,"prerenderedAt":70},["ShallowReactive",2],{"tutorial-salvare-token-in-modo-sicuro-in-flutter-con-flutter-secure-storage":3,"comments-tutorial-salvare-token-in-modo-sicuro-in-flutter-con-flutter-secure-storage":69},{"id":4,"title":5,"slug":6,"excerpt":7,"intro":8,"cover_image":9,"video_url":10,"difficulty":11,"estimated_minutes":12,"flutter_version":13,"status":14,"published_at":15,"meta_title":16,"meta_description":17,"category":18,"author":22,"steps":25},38,"Salvare token in modo sicuro in Flutter con flutter_secure_storage","salvare-token-in-modo-sicuro-in-flutter-con-flutter-secure-storage","Impara a memorizzare in modo cifrato token di autenticazione e dati sensibili in Flutter usando flutter_secure_storage, sfruttando Keychain su iOS e Keystore su Android.","## Perché non usare shared_preferences per i dati sensibili?\n\n`shared_preferences` salva i dati in chiaro: chiunque abbia accesso al file system (specie su dispositivi con root\u002Fjailbreak) può leggere token, password o chiavi API.\n\nIl pacchetto **flutter_secure_storage** risolve il problema appoggiandosi ai meccanismi di sicurezza nativi:\n\n- **Keychain** su iOS\u002FmacOS\n- **Keystore + EncryptedSharedPreferences** su Android\n\nIn questo tutorial creeremo un piccolo servizio riutilizzabile per salvare, leggere ed eliminare un token JWT in modo cifrato, con qualche accorgimento di best practice.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002Fed87e9cc-e0d0-4331-b1ca-2ab64b616bc1.jpg",null,"intermediate",25,"3.x","published","2026-07-14T04:30:36+00:00","Storage sicuro in Flutter con flutter_secure_storage","Guida pratica a flutter_secure_storage per salvare token e dati sensibili cifrati in Flutter usando Keychain e Keystore nativi.",{"id":19,"name":20,"slug":21},3,"Best practice","best-practice",{"id":23,"name":24},1,"Flutter Bot",[26,33,41,48,55,62],{"id":27,"position":23,"title":28,"body":29,"code_snippet":30,"code_language":31,"expected_result":32,"demo_url":10,"video_url":10},256,"Aggiungere la dipendenza","Aggiungi il pacchetto al tuo progetto. Puoi usare il comando `flutter pub add` oppure modificare manualmente il file `pubspec.yaml`.\n\nDopo l'aggiunta esegui `flutter pub get`.","flutter pub add flutter_secure_storage","bash","Nel pubspec.yaml compare la dipendenza flutter_secure_storage con la relativa versione.",{"id":34,"position":35,"title":36,"body":37,"code_snippet":38,"code_language":39,"expected_result":40,"demo_url":10,"video_url":10},257,2,"Configurare le piattaforme native","Su **Android** è consigliato usare `EncryptedSharedPreferences` e impostare `minSdkVersion` ad almeno 21 in `android\u002Fapp\u002Fbuild.gradle`.\n\nSu **iOS** non serve configurazione aggiuntiva, ma se vuoi evitare problemi durante il backup del dispositivo puoi impostare l'accessibilità delle voci Keychain (lo vedremo nel prossimo passo).","\u002F\u002F android\u002Fapp\u002Fbuild.gradle\nandroid {\n    defaultConfig {\n        minSdkVersion 21\n        \u002F\u002F ...\n    }\n}","groovy","Il progetto Android compila senza errori legati alla minSdkVersion.",{"id":42,"position":19,"title":43,"body":44,"code_snippet":45,"code_language":46,"expected_result":47,"demo_url":10,"video_url":10},258,"Creare un servizio riutilizzabile","Invece di istanziare `FlutterSecureStorage` sparso nel codice, incapsuliamo la logica in una classe `SecureStorageService`. Definiamo anche opzioni specifiche per Android e iOS per aumentare la sicurezza.\n\nL'opzione `IOSAccessibility.first_unlock` fa sì che il dato sia leggibile solo dopo il primo sblocco del dispositivo dall'avvio, un buon compromesso tra sicurezza e usabilità.","import 'package:flutter_secure_storage\u002Fflutter_secure_storage.dart';\n\nclass SecureStorageService {\n  SecureStorageService._();\n  static final instance = SecureStorageService._();\n\n  static const _tokenKey = 'auth_token';\n\n  final _storage = const FlutterSecureStorage(\n    aOptions: AndroidOptions(\n      encryptedSharedPreferences: true,\n    ),\n    iOptions: IOSOptions(\n      accessibility: KeychainAccessibility.first_unlock,\n    ),\n  );\n\n  Future\u003Cvoid> saveToken(String token) async {\n    await _storage.write(key: _tokenKey, value: token);\n  }\n\n  Future\u003CString?> readToken() async {\n    return _storage.read(key: _tokenKey);\n  }\n\n  Future\u003Cvoid> deleteToken() async {\n    await _storage.delete(key: _tokenKey);\n  }\n\n  Future\u003Cvoid> clearAll() async {\n    await _storage.deleteAll();\n  }\n}","dart","Hai un singleton SecureStorageService con metodi per salvare, leggere ed eliminare il token.",{"id":49,"position":50,"title":51,"body":52,"code_snippet":53,"code_language":46,"expected_result":54,"demo_url":10,"video_url":10},259,4,"Salvare e leggere il token dopo il login","Simuliamo un flusso di login: dopo aver ottenuto il token dal backend lo salviamo con `saveToken`, e all'avvio dell'app lo rileggiamo per capire se l'utente è già autenticato.","Future\u003Cvoid> onLoginSuccess(String jwt) async {\n  await SecureStorageService.instance.saveToken(jwt);\n}\n\nFuture\u003Cbool> isLoggedIn() async {\n  final token = await SecureStorageService.instance.readToken();\n  return token != null && token.isNotEmpty;\n}","Il token viene scritto cifrato dopo il login e riletto correttamente al riavvio dell'app.",{"id":56,"position":57,"title":58,"body":59,"code_snippet":60,"code_language":46,"expected_result":61,"demo_url":10,"video_url":10},260,5,"Usare il token nelle chiamate e gestire il logout","Il token salvato serve tipicamente come header `Authorization`. Vediamo come recuperarlo prima di una richiesta e come cancellarlo al logout.\n\n**Best practice**: durante il logout usa `deleteToken` o `clearAll` per rimuovere ogni traccia dei dati sensibili.","import 'package:http\u002Fhttp.dart' as http;\n\nFuture\u003Chttp.Response> fetchProfile() async {\n  final token = await SecureStorageService.instance.readToken();\n  return http.get(\n    Uri.parse('https:\u002F\u002Fapi.example.com\u002Fme'),\n    headers: {\n      if (token != null) 'Authorization': 'Bearer $token',\n    },\n  );\n}\n\nFuture\u003Cvoid> logout() async {\n  await SecureStorageService.instance.deleteToken();\n}","Le richieste includono l'header Authorization e al logout il token viene rimosso dallo storage sicuro.",{"id":63,"position":64,"title":65,"body":66,"code_snippet":67,"code_language":46,"expected_result":68,"demo_url":10,"video_url":10},261,6,"Gestire gli errori e testare su emulatori","Su alcuni dispositivi (o dopo aggiornamenti dell'OS) la lettura può fallire per chiavi corrotte. Avvolgi le operazioni in un `try\u002Fcatch` e in caso di errore fai una pulizia difensiva.\n\nAttenzione: su alcuni emulatori Android più vecchi il Keystore può comportarsi in modo instabile; testa sempre anche su un dispositivo reale.","Future\u003CString?> readTokenSafe() async {\n  try {\n    return await SecureStorageService.instance.readToken();\n  } on Exception catch (e) {\n    \u002F\u002F Chiave corrotta o Keystore non disponibile: puliamo e ripartiamo\n    await SecureStorageService.instance.clearAll();\n    return null;\n  }\n}","L'app non crasha in caso di errore di decifratura e gestisce lo stato in modo pulito.",[],1785219608394]