iOS: como armazenar nome de usuário / senha em um aplicativo?

276

Eu tenho uma tela de login no meu aplicativo iOS. O nome de usuário e a senha serão salvos NSUserDefaultse carregados na tela de login novamente quando você entrar no aplicativo novamente (é claro, NSUserDefaultsé permanente).

Agora, o usuário tem a possibilidade de desativar o recurso de economia de nome de usuário / senha.

Então o NSUserDefaultsserá limpo então.

Mas, no meu aplicativo, preciso desse nome de usuário / senha para consultas ao banco de dados do usuário. Então: onde armazenar os dados, exceto NSUserDefaults? (Este local pode / deve ser excluído quando o usuário sair do aplicativo ou sair).

Kovu
fonte
O usuário pode limpá-lo apenas redefinindo o dispositivo ou removendo o aplicativo. Estou esquecendo de algo?
3
E, a propósito, se os dados devem ser excluídos quando o usuário sai do aplicativo, por que não mantê-lo na RAM?
10
Você deve considerar seriamente o uso do Keychain para armazenar nomes de usuário e senhas em vez de NSUserDefaults.
precisa saber é o seguinte
1
Você pode obter idéia básica sobre a implementação swift3 a partir daqui
Anish Parajuli 웃
Por favor, sempre devo usar kSecValueData e kSecValueData como chaves? Ou posso usar qualquer string como chave?
Ne AS

Respostas:

424

Você deve sempre usar o Keychain para armazenar nomes de usuário e senhas e, como ele é armazenado de forma segura e acessível apenas ao seu aplicativo, não há necessidade de excluí-lo quando o aplicativo é encerrado (se essa era sua preocupação).

A Apple fornece um código de amostra que armazena, lê e exclui itens de chaveiro e eis como usar a classe de wrapper de chaveiro dessa amostra, o que simplifica bastante o uso de chaveiro.

Inclua Security.framework (no Xcode 3, clique com o botão direito do mouse na pasta frameworks e adicione a estrutura existente. No Xcode 4, selecione seu projeto, selecione o destino, vá para a guia Build Phases e clique em + em Link Binary With Files) e KeychainItemWrapper .h &. m no seu projeto, # importe o arquivo .h onde quer que você precise usar as chaves e, em seguida, crie uma instância desta classe:

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

( YourAppLogin pode ser qualquer coisa que você escolher para chamar seu item Keychain e você pode ter vários itens, se necessário)

Em seguida, você pode definir o nome de usuário e a senha usando:

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

Obtenha-os usando:

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

Ou exclua-os usando:

[keychainItem resetKeychainItem];
Filip Radelic
fonte
5
Atualizei minha resposta com o código e a descrição. Não é tão difícil quanto você pensava.
Filip Radelic
44
ATENÇÃO! Por favor, adicione à sua resposta que apenas "copiar KeychainItemWrapper" não é suficiente! Eu tive o problema, que não posso construí-lo depois! Você deve adicionar o security.framework ao seu projeto para que o KeychainItemWrapper funcione! (HowTo: Selecione Projeto -> Select Target -> selecione a guia "Fases construir" -> Selecionar "Link binário com libaries" -> "+" -> adicionar Security.Framework)
Kovu
63
Ao usar o ARC, o compilador gritará com você por usar as constantes kSecValueDatae kSecAttrAccountno código Objective-C, portanto, lance-as usando (__bridge id), por exemplo, [keychainItem setObject:obj forKey:(__bridge id)kSecValueData];
Joe Hankin
7
KeychainItemWrapper.m parece ter um vazamento de memória na linha 196. Alterando a linha para "self.keychainItemData = [[[alocação de NSMutableDictionary] init] autor] liberação];" corrige isso.
Olof
12
Ao usar o ARC, o código atualizado foi fornecido aqui pela Apple. Veja a Listagem 2-1. Embora a abordagem seja a mesma.
Rick
97

Se você precisar de uma versão ARC da embalagem aqui está o link https://gist.github.com/1170641 Graças à

Manuel Pintaldi
fonte
Obrigado, recebo KeychainItemWrapper .h & .m desse URL.
precisa saber é o seguinte
48

Uma solução muito fácil via Keychains .

É um invólucro simples para o sistema Keychain. Basta adicionar o SSKeychain.h, SSKeychain.m,SSKeychainQuery.h eSSKeychainQuery.m arquivos ao seu projeto e adicionar o Security.framework para o seu alvo.

Para salvar uma senha:

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

Para recuperar uma senha:

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

Onde setPasswordestá o valor que você deseja salvar e forServicequal a variável em que você deseja que ele seja salvo e a conta é para qual usuário / objeto a senha e qualquer outra informação serve.

sust86
fonte
Você sabe como usar o sskeychain para sincronizar aplicativos com o mesmo nome de usuário e senha?
21813 Joe Shamuraq
4
como você armazena um nome de usuário, além de uma senha? Como você exclui uma conta inteira do SSKeychain? Ambos não são mencionados nos documentos
user798719 09/09
2
Para obter o nome de usuário faça NSString *username = [[SSKeychain accountsForService:@"AnyService"][0] valueForKey:@"acct"]. Isso deve funcionar bem se você usar apenas uma conta. Como sempre, certifique-se de verificar o comprimento da matriz antes de tentar índice de acesso 0.
Jared Preço
27

Você pode simplesmente usar NSURLCredential, ele salvará nome de usuário e senha no chaveiro em apenas duas linhas de código .

Veja minha resposta detalhada .

Phil
fonte
13

Decidi responder como usar chaveiro no iOS 8 usando Obj-C e ARC.

1) Usei o keychainItemWrapper (versão ARCifief) do GIST: https://gist.github.com/dhoerl/1170641/download - Adicione (+ copie) o KeychainItemWrapper.he .m ao seu projeto

2) Adicione a estrutura de segurança ao seu projeto (verifique o projeto> Fases de construção> Vincular binário às bibliotecas)

3) Adicione a biblioteca de segurança (#import) e KeychainItemWrapper (#import "KeychainItemWrapper.h") ao arquivo .he .m onde deseja usar as chaves.

4) Para salvar dados no chaveiro:

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) Leia os dados (provavelmente a tela de login ao carregar> 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;

Aproveitar!

D_RBD
fonte
11

Se você estiver com problemas para recuperar a senha usando o wrapper de chaveiro, use este código:

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

checkout este código de exemplo, tentei primeiro o invólucro da maçã a partir do código de exemplo, mas isso é muito mais simples para mim

BSevo
fonte
2

tente este:

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

pode ajudar.

Vara
fonte
2

Eu olhei para usar o KeychainItemWrapper (a versão do ARC), mas não achei o invólucro do Objective C tão íntegro quanto desejado.

Eu usei esta solução por Kishikawa Katsumi , o que significa que escrevi menos código e não precisei usar moldes para armazenar valores de NSString.

Dois exemplos de armazenamento:

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

Dois exemplos de recuperação

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

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

Existe um pequeno bug no código acima (a propósito, Dave, foi muito útil seu post, obrigado)

Na parte em que salvamos as credenciais, ele também precisa do código a seguir para funcionar corretamente.

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

o mais provável é que, na segunda vez em que tentamos (re) entrar com as mesmas credenciais, eles já estão atribuídos nos itens de chaveiro e o aplicativo trava. com o código acima, funciona como um encanto.

Realidade
fonte
2

Para atualizar esta pergunta:

Para aqueles que usam o Swift checkout, essa implementação rápida de arrastar e soltar do Mihai Costea que suporta grupos de acesso:

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

Antes de usar o chaveiro: considere duas vezes antes de armazenar senhas. Em muitos casos, o armazenamento de um token de autenticação (como um ID de sessão de persistência) e o nome do email ou da conta podem ser suficientes. Você pode invalidar facilmente os tokens de autenticação para bloquear o acesso não autorizado, exigindo que o usuário efetue login novamente no dispositivo comprometido, mas não exigindo redefinição de senha e tendo que efetuar login novamente em todos os dispositivos (não estamos apenas usando a Apple, não é?).

pizzamonster
fonte
1

Mas agora você pode optar por NURLCredential em vez de wrapper de chaveiro. Faz o que precisamos fazer.

Sumitiscreative
fonte
0

O seguinte deve funcionar bem:

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