iOS: Làm cách nào để lưu tên người dùng / mật khẩu trong một ứng dụng?


276

Tôi có một màn hình đăng nhập trong ứng dụng iOS của mình. Tên người dùng và mật khẩu sẽ được lưu trong NSUserDefaultsvà được tải lại vào màn hình đăng nhập khi bạn nhập lại ứng dụng (tất nhiên NSUserDefaultslà vĩnh viễn).

Bây giờ, người dùng có khả năng vô hiệu hóa tính năng lưu tên người dùng / mật khẩu.

Vì vậy, NSUserDefaultssẽ được xóa sau đó.

Nhưng trong ứng dụng của mình, tôi cần tên người dùng / mật khẩu này cho các truy vấn cơ sở dữ liệu cho người dùng. Vì vậy: Nơi lưu trữ dữ liệu ngoại trừ NSUserDefaults? (Địa điểm này có thể / nên bị xóa khi người dùng thoát khỏi ứng dụng hoặc đăng xuất).


Người dùng chỉ có thể xóa nó bằng cách đặt lại thiết bị hoặc xóa ứng dụng. Tui bỏ lỡ điều gì vậy?

3
Và nhân tiện, nếu dữ liệu nên bị xóa khi người dùng thoát khỏi ứng dụng, tại sao bạn không giữ nó trong RAM?

10
Bạn nên nghiêm túc xem xét việc sử dụng Keychain để lưu trữ tên người dùng và mật khẩu thay vì NSUserDefaults.
Filip Radelic

1
Bạn có thể lấy ý tưởng cơ bản về triển khai swift3 từ đây
Anish Parajuli

Vui lòng luôn sử dụng kSecValueData và kSecValueData làm khóa? Hoặc tôi có thể sử dụng bất kỳ chuỗi nào làm khóa?
Né AS

Câu trả lời:


424

Bạn phải luôn sử dụng Keychain để lưu trữ tên người dùng và mật khẩu, và vì nó được lưu trữ an toàn và chỉ có thể truy cập vào ứng dụng của bạn, nên không cần phải xóa nó khi thoát ứng dụng (nếu đó là mối quan tâm của bạn).

Apple cung cấp mã mẫu lưu trữ, đọc và xóa các mục móc khóa và đây là cách sử dụng lớp trình bao bọc móc khóa từ mẫu đó giúp đơn giản hóa rất nhiều khi sử dụng Keychain.

Bao gồm Security.framework (trong Xcode 3, nhấp chuột phải vào thư mục khung và thêm khung hiện có. Trong Xcode 4, chọn dự án của bạn, sau đó chọn mục tiêu, đi tới tab Build Ph Phase và nhấp + trong Link Binary With Files) và KeychainItemWrapper .h &. m tệp vào dự án của bạn, # nhập tệp .h bất cứ nơi nào bạn cần sử dụng móc khóa và sau đó tạo một thể hiện của lớp này:

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

( YourAppLogin có thể là bất cứ điều gì bạn chọn để gọi mục Keychain của mình và bạn có thể có nhiều mục nếu cần)

Sau đó, bạn có thể đặt tên người dùng và mật khẩu bằng cách sử dụng:

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

Nhận chúng bằng cách sử dụng:

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

Hoặc xóa chúng bằng cách sử dụng:

[keychainItem resetKeychainItem];

5
Tôi đã cập nhật câu trả lời của tôi với mã và mô tả. Nó không khó như bạn nghĩ đâu.
Filip Radelic

44
CHÚ Ý! Vui lòng thêm vào câu trả lời của bạn, rằng chỉ "sao chép KeychainItemWrapper" là không đủ! Tôi đã có một vấn đề, rằng tôi không thể xây dựng nó sau đó! Bạn phải thêm security.framework vào dự án của mình để KeychainItemWrapper hoạt động! (Howto: Chọn dự án -> Chọn Target -> Chọn Tab "giai đoạn xây dựng" -> Chọn "Liên kết Binary Với libaries" -> "+" -> thêm Security.Framework)
Kovu

63
Khi sử dụng ARC, trình biên dịch sẽ mắng bạn vì đã sử dụng các hằng số kSecValueDatakSecAttrAccounttrong mã Objective-C, do đó hãy chắc chắn sử dụng chúng bằng cách sử dụng (__bridge id), ví dụ: [keychainItem setObject:obj forKey:(__bridge id)kSecValueData];
Joe Hankin

7
KeychainItemWrapper.m dường như bị rò rỉ bộ nhớ ở dòng 196. Thay đổi dòng thành "self.keychainItemData = [[[NSMutableDixi alloc] init] autorelease];" sửa nó
Olof

12
Khi sử dụng ARC, mã cập nhật đã được đưa ra ở đây bởi Apple. Nhìn vào Liệt kê 2-1. Mặc dù cách tiếp cận là như nhau.
Rick


48

Một giải pháp rất dễ dàng thông qua Keychains .

Đó là một trình bao bọc đơn giản cho Keychain hệ thống. Chỉ cần thêm SSKeychain.h, SSKeychain.m, SSKeychainQuery.hSSKeychainQuery.mcác tập tin để dự án của bạn và thêm Security.framework đến mục tiêu của bạn.

Để lưu mật khẩu:

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

Để lấy lại mật khẩu:

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

Đâu setPasswordlà giá trị bạn muốn lưu và forServicelà biến bạn muốn lưu ở đâu và tài khoản dành cho người dùng / đối tượng mật khẩu và bất kỳ thông tin nào khác dành cho.


Bạn có biết cách sử dụng sskeychain để đồng bộ hóa các ứng dụng có cùng tên người dùng và mật khẩu không?
Joe Shamuraq

4
Làm thế nào để bạn lưu trữ một tên người dùng ngoài mật khẩu? Làm cách nào để xóa toàn bộ tài khoản khỏi SSKeychain? Cả hai đều không được đề cập đến các tài liệu
user798719

2
Để có được tên người dùng làm NSString *username = [[SSKeychain accountsForService:@"AnyService"][0] valueForKey:@"acct"]. Điều này sẽ hoạt động tốt nếu bạn chỉ sử dụng một tài khoản. Như mọi khi, hãy chắc chắn kiểm tra độ dài mảng trước khi thử truy cập chỉ mục 0.
Jared Giá

27

Bạn có thể chỉ cần sử dụng NSURLCredential, nó sẽ lưu cả tên người dùng và mật khẩu trong móc khóa chỉ trong hai dòng mã .

Xem câu trả lời chi tiết của tôi .


13

Tôi quyết định trả lời cách sử dụng móc khóa trong iOS 8 bằng Obj-C và ARC.

1) Tôi đã sử dụng móc khóaItemWrapper (phiên bản ARCifief) từ GIST: https://gist.github.com/dhoerl/1170641/doad - Thêm (+ sao chép) KeychainItemWrapper.h và .m vào dự án của bạn

2) Thêm khung Bảo mật cho dự án của bạn (đăng ký dự án> Xây dựng các giai đoạn> Liên kết nhị phân với Thư viện)

3) Thêm thư viện bảo mật (#import) và KeychainItemWrapper (#import "KeychainItemWrapper.h") vào tệp .h và .m nơi bạn muốn sử dụng móc khóa.

4) Để lưu dữ liệu vào móc khóa:

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) Đọc dữ liệu (có thể là màn hình đăng nhập khi tải> 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;

Thưởng thức!


11

Nếu bạn gặp sự cố khi truy xuất mật khẩu bằng trình bao bọc móc khóa, hãy sử dụng mã này:

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

3

kiểm tra mã mẫu này trước tiên tôi đã thử trình bao bọc của apple từ mã mẫu nhưng điều này đơn giản hơn nhiều đối với tôi


2

hãy thử cái này

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

có thể nó sẽ giúp


2

Tôi đã xem xét bằng cách sử dụng KeychainItemWrapper (phiên bản ARC) nhưng tôi không thấy trình bao bọc Objective C của nó hoàn hảo như mong muốn.

Tôi đã sử dụng giải pháp này của Kishikawa Katsumi , điều đó có nghĩa là tôi đã viết ít mã hơn và không phải sử dụng phôi để lưu trữ các giá trị NSString.

Hai ví dụ về lưu trữ:

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

Hai ví dụ về việc lấy

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

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

2

Có một lỗi nhỏ trong đoạn mã trên (nhân tiện Dave rất hữu ích bài viết của bạn cảm ơn bạn)

Trong phần chúng tôi lưu thông tin đăng nhập, nó cũng cần mã sau đây để hoạt động đúng.

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

rất có thể là vì lần thứ hai chúng tôi cố gắng (đăng nhập lại) với cùng thông tin đăng nhập, nó thấy chúng đã được gán trong các mục móc khóa và ứng dụng gặp sự cố. với đoạn mã trên nó hoạt động như một lá bùa.


2

Để cập nhật câu hỏi này:

Đối với những người sử dụng thanh toán Swift, việc triển khai kéo và thả nhanh này của Mihai Costea hỗ trợ các nhóm truy cập:

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

Trước khi sử dụng móc khóa: cân nhắc hai lần trước khi lưu trữ mật khẩu. Trong nhiều trường hợp lưu trữ mã thông báo xác thực (chẳng hạn như id phiên liên tục) và tên tài khoản hoặc email có thể là đủ. Bạn có thể dễ dàng vô hiệu hóa mã thông báo xác thực để chặn truy cập trái phép, yêu cầu người dùng đăng nhập lại trên thiết bị bị xâm nhập nhưng không yêu cầu đặt lại mật khẩu và phải đăng nhập lại trên tất cả các thiết bị (chúng ta không chỉ sử dụng Apple?).


1

Nhưng, bây giờ bạn có thể dùng NURLCredential thay vì trình bao bọc móc khóa. Nó làm những gì chúng ta cần làm.



0

Sau đây nên hoạt động tốt:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[keychainItem setObject:@"password you are saving" forKey:kSecValueData]; 
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.