iOS: วิธีการจัดเก็บชื่อผู้ใช้ / รหัสผ่านภายในแอป


276

ฉันมีหน้าจอเข้าสู่ระบบในแอพ iOS ของฉัน ชื่อผู้ใช้และรหัสผ่านจะถูกบันทึกไว้ในNSUserDefaultsและจะถูกโหลดเข้าสู่หน้าจอการเข้าสู่ระบบอีกครั้งเมื่อคุณเข้าสู่แอปอีกครั้ง (แน่นอนNSUserDefaultsเป็นแบบถาวร)

ตอนนี้ผู้ใช้มีความเป็นไปได้ที่จะปิดการใช้งานคุณสมบัติการบันทึกชื่อผู้ใช้ / รหัสผ่าน

ดังนั้นNSUserDefaultsจะถูกล้างออกแล้ว

แต่ในแอพของฉันฉันต้องการชื่อผู้ใช้ / รหัสผ่านนี้สำหรับการสืบค้นฐานข้อมูลสำหรับผู้ใช้ ดังนั้น: จะเก็บข้อมูลNSUserDefaultsที่ไหน (สถานที่นี้สามารถ / ควรถูกลบเมื่อผู้ใช้ออกจากแอปหรือออกจากระบบ)


ผู้ใช้สามารถล้างได้ด้วยการรีเซ็ตอุปกรณ์หรือลบแอป ฉันพลาดอะไรไปรึเปล่า?

3
และโดยวิธีการถ้าข้อมูลควรถูกลบเมื่อผู้ใช้ออกจากแอปทำไมไม่เพียงเก็บไว้ใน RAM?

10
คุณควรพิจารณาใช้ Keychain เพื่อจัดเก็บชื่อผู้ใช้และรหัสผ่านแทน NSUserDefaults
Filip Radelic

1
คุณจะได้รับความคิดพื้นฐานในการดำเนิน swift3 จากที่นี่
Anish Parajuli 웃

ได้โปรดฉันควรใช้ kSecValueData และ kSecValueData เป็นกุญแจเสมอ? หรือฉันจะใช้สตริงใด ๆ เป็นคีย์ได้หรือไม่
Ne AS

คำตอบ:


424

คุณควรใช้ Keychain เพื่อเก็บชื่อผู้ใช้และรหัสผ่านและเนื่องจากมันถูกเก็บไว้อย่างปลอดภัยและสามารถเข้าถึงแอพของคุณได้เท่านั้นจึงไม่จำเป็นต้องลบออกเมื่อแอพหยุดทำงาน (ถ้าคุณกังวล)

Apple ให้รหัสตัวอย่างที่เก็บอ่านและลบรายการพวงกุญแจและนี่คือวิธีการใช้คลาส wrapper ของพวงกุญแจจากตัวอย่างที่ทำให้การใช้งาน Keychain ง่ายขึ้นอย่างมาก

รวม Security.framework (ใน Xcode 3 คลิกขวาที่โฟลเดอร์เฟรมเวิร์กและเพิ่มเฟรมเวิร์กที่มีอยู่ใน Xcode 4 เลือกโครงการของคุณจากนั้นเลือกเป้าหมายไปที่แท็บสร้างเฟสและคลิก + ภายใต้ลิงค์ไบนารีพร้อมไฟล์)และ KeychainItemWrapper .h & m ไฟล์ลงในโครงการของคุณ #import ไฟล์. h ทุกที่ที่คุณต้องการใช้ Keychain และสร้างอินสแตนซ์ของคลาสนี้:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];

( YourAppLoginสามารถเป็นอะไรก็ได้ที่คุณเลือกที่จะโทรหารายการ Keychain และคุณสามารถมีได้หลายรายการหากต้องการ)

จากนั้นคุณสามารถตั้งชื่อผู้ใช้และรหัสผ่านโดยใช้:

[keychainItem setObject:@"password you are saving" forKey:kSecValueData];
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];

รับพวกเขาโดยใช้:

NSString *password = [keychainItem objectForKey:kSecValueData];
NSString *username = [keychainItem objectForKey:kSecAttrAccount];

หรือลบทิ้งโดยใช้:

[keychainItem resetKeychainItem];

5
ฉันได้อัปเดตคำตอบพร้อมรหัสและคำอธิบายแล้ว มันไม่ยากอย่างที่คิด
Filip Radelic

44
ATTANTION! โปรดเพิ่มคำตอบของคุณว่า "คัดลอก KeychainItemWrapper" เท่านั้นยังไม่พอ! ฉันมีปัญหาที่ฉันไม่สามารถสร้างได้ในภายหลัง! คุณต้องเพิ่ม security.framework ในโครงการของคุณที่ KeychainItemWrapper จะทำงาน! (HowTo: เลือกโครงการ -> เลือกเป้าหมาย -> เลือกแท็บ "สร้างเฟส" -> เลือก "เชื่อมโยงไบนารีกับ Libaries" -> "+" -> เพิ่ม Security.Framework)
Kovu

63
เมื่อใช้ ARC คอมไพเลอร์จะตะโกนใส่คุณเพื่อใช้ค่าคงที่kSecValueDataและkSecAttrAccountในรหัส Objective-C ดังนั้นโปรดใช้รหัส(__bridge id)เช่น [keychainItem setObject:obj forKey:(__bridge id)kSecValueData];
Joe Hankin

7
KeychainItemWrapper.m ดูเหมือนว่าหน่วยความจำรั่วที่บรรทัด 196 การเปลี่ยนบรรทัดเป็น "self.keychainItemData = [[[NSMutableDictionary alloc] init] autorelease] init] แก้ไขมัน
Olof

12
เมื่อใช้ ARC รหัสที่อัปเดตได้รับที่นี่โดยแอปเปิ้ล ดูรายการ 2-1 แม้ว่าวิธีการจะเหมือนกัน
ริ


48

เป็นทางออกที่ง่ายผ่านพวงกุญแจ

มันเป็นเสื้อคลุมที่เรียบง่ายสำหรับ Keychain ระบบ เพียงแค่เพิ่มSSKeychain.h, SSKeychain.m, SSKeychainQuery.hและSSKeychainQuery.mไฟล์ไปยังโครงการของคุณและเพิ่ม Security.framework เป้าหมายของคุณ

ในการบันทึกรหัสผ่าน:

[SSKeychain setPassword:@"AnyPassword" forService:@"AnyService" account:@"AnyUser"]

วิธีดึงรหัสผ่าน:

NSString *password = [SSKeychain passwordForService:@"AnyService" account:@"AnyUser"];

ในกรณีที่setPasswordเป็นสิ่งที่มีค่าที่คุณต้องการบันทึกและforServiceคือสิ่งที่ตัวแปรที่คุณต้องการและบันทึกไว้ภายใต้บัญชีสำหรับสิ่งที่ผู้ใช้ / วัตถุรหัสผ่านและข้อมูลอื่น ๆ สำหรับ


คุณรู้วิธีใช้ sskeychain เพื่อประสานแอพด้วยชื่อผู้ใช้และรหัสผ่านเดียวกันหรือไม่
Joe Shamuraq

4
คุณจัดเก็บชื่อผู้ใช้และรหัสผ่านได้อย่างไร คุณจะลบบัญชีทั้งหมดจาก SSKeychain ได้อย่างไร ทั้งคู่ไม่ได้กล่าวถึงเอกสาร
user798719

2
NSString *username = [[SSKeychain accountsForService:@"AnyService"][0] valueForKey:@"acct"]ที่จะได้รับชื่อผู้ทำ สิ่งนี้น่าจะใช้ได้ดีถ้าคุณใช้เพียงหนึ่งบัญชีเท่านั้น และเช่นเคยโปรดตรวจสอบความยาวของอาร์เรย์ก่อนที่จะพยายามเข้าถึงดัชนี 0
ราคา Jared

27

คุณสามารถใช้ NSURLCredentialมันจะประหยัดทั้งชื่อผู้ใช้และรหัสผ่านในพวงกุญแจในเวลาเพียงสองบรรทัดของรหัส

ดูรายละเอียดของฉันคำตอบ


13

ฉันตัดสินใจที่จะตอบวิธีใช้ keychain ใน iOS 8 โดยใช้ Obj-C และ ARC

1) ฉันใช้ keychainItemWrapper (เวอร์ชั่น ARCifief) จาก GIST: https://gist.github.com/dhoerl/1170641/download - เพิ่ม (+ คัดลอก) KeychainItemWrapper.h และ. m ในโครงการของคุณ

2) เพิ่มกรอบความปลอดภัยให้กับโครงการของคุณ (เช็คอินโครงการ> สร้างเฟส> ลิงก์ไบนารีกับไลบรารี)

3) เพิ่มไลบรารีความปลอดภัย (#import) และ KeychainItemWrapper (#import "KeychainItemWrapper.h") ไปยังไฟล์. h และ. m ที่คุณต้องการใช้ Keychain

4) ในการบันทึกข้อมูลลงในพวงกุญแจ:

NSString *emailAddress = self.txtEmail.text;
NSString *password = self.txtPasword.text;
//because keychain saves password as NSData object
NSData *pwdData = [password dataUsingEncoding:NSUTF8StringEncoding];

//Save item
self.keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[self.keychainItem setObject:emailAddress forKey:(__bridge id)(kSecAttrAccount)];
[self.keychainItem setObject:pwdData forKey:(__bridge id)(kSecValueData)];

5) อ่านข้อมูล (อาจเป็นหน้าจอเข้าสู่ระบบเมื่อโหลด> viewDidLoad):

self.keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];

self.txtEmail.text = [self.keychainItem objectForKey:(__bridge id)(kSecAttrAccount)];

//because label uses NSString and password is NSData object, conversion necessary
NSData *pwdData = [self.keychainItem objectForKey:(__bridge id)(kSecValueData)];
NSString *password = [[NSString alloc] initWithData:pwdData encoding:NSUTF8StringEncoding];
self.txtPassword.text = password;

สนุก!


11

หากคุณประสบปัญหาในการเรียกรหัสผ่านโดยใช้ wrapper keychain ให้ใช้รหัสนี้:

NSData *pass =[keychain objectForKey:(__bridge id)(kSecValueData)];
NSString *passworddecoded = [[NSString alloc] initWithData:pass
                                           encoding:NSUTF8StringEncoding];


2

ลองอันนี้:

 KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[keychainItem setObject:@"password you are saving" forKey:kSecValueData]; 
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];

มันอาจจะช่วย


2

ฉันดูที่การใช้ KeychainItemWrapper (เวอร์ชัน ARC) แต่ฉันไม่พบเสื้อคลุม Objective C ของมันที่ดีเท่าที่ต้องการ

ฉันใช้วิธีแก้ปัญหานี้โดยKishikawa Katsumiซึ่งหมายความว่าฉันเขียนโค้ดน้อยลงและไม่ต้องใช้ casts เพื่อเก็บค่า NSString

สองตัวอย่างของการจัดเก็บ:

[UICKeyChainStore setString:@"kishikawakatsumi" forKey:@"username"];
[UICKeyChainStore setString:@"P455_w0rd$1$G$Z$" forKey:@"password"];

สองตัวอย่างของการดึงข้อมูล

UICKeyChainStore *store = [UICKeyChainStore keyChainStore];
    // or
UICKeyChainStore *store = [UICKeyChainStore keyChainStoreWithService:@"YOUR_SERVICE"];

NSString *username = [store stringForKey:@"username"];
NSString *password = [store stringForKey:@"password"];

2

มีข้อผิดพลาดเล็ก ๆ ในรหัสข้างต้น (โดยวิธีเดฟมันเป็นประโยชน์มากโพสต์ของคุณขอบคุณ)

ในส่วนที่เราบันทึกข้อมูลประจำตัวมันยังต้องใช้รหัสต่อไปนี้เพื่อให้ทำงานได้อย่างถูกต้อง

[self.keychainItem setObject:@"myCredentials" forKey:(__bridge id)(kSecAttrService)];

อาจเป็นเพราะครั้งที่สองที่เราพยายามลงชื่อเข้าใช้อีกครั้งด้วยข้อมูลประจำตัวเดียวกันพบว่าพวกเขาได้รับมอบหมายในรายการพวงกุญแจและแอปขัดข้อง ด้วยรหัสข้างต้นมันทำงานเหมือนมีเสน่ห์


2

หากต้องการอัปเดตคำถามนี้:

สำหรับผู้ที่ใช้เช็คเอาต์ Swift การลากและวางการใช้งาน swift โดย Mihai Costea ที่สนับสนุนกลุ่มการเข้าถึง:

https://github.com/macostea/KeychainItemWrapper.swift/blob/master/KeychainItemWrapper.swift

ก่อนใช้พวงกุญแจ: พิจารณาสองครั้งก่อนจัดเก็บรหัสผ่าน ในหลายกรณีการจัดเก็บโทเค็นการตรวจสอบความถูกต้อง (เช่นรหัสเซสชันที่มีอยู่) และอีเมลหรือชื่อบัญชีอาจเพียงพอ คุณสามารถทำให้โทเค็นการตรวจสอบความถูกต้องเป็นโมฆะเพื่อป้องกันการเข้าถึงโดยไม่ได้รับอนุญาตทำให้ผู้ใช้ต้องลงชื่อเข้าใช้อีกครั้งบนอุปกรณ์ที่ถูกโจมตี แต่ไม่ต้องการรหัสผ่านรีเซ็ตและต้องเข้าสู่ระบบอีกครั้งบนอุปกรณ์ทั้งหมด


1

แต่ตอนนี้คุณสามารถไปหา NURLCredential ได้แทนที่จะใช้ Keychain wrapper มันทำในสิ่งที่เราต้องทำ



0

ต่อไปนี้ควรทำงานได้ดี:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[keychainItem setObject:@"password you are saving" forKey:kSecValueData]; 
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.