如何将 fmdb 用于登录页面?

Posted

技术标签:

【中文标题】如何将 fmdb 用于登录页面?【英文标题】:How to use fmdb for a login page? 【发布时间】:2014-01-01 22:37:06 【问题描述】:

我有一个登录页面,其中包含 2 个用于用户名和密码的文本字段以及一些按钮。我想知道如何使用 fmdb 检查数据库中是否存在用户名/密码组合?我还有另一个用于用户注册页面的视图控制器,有 4 个用于用户名、密码、电子邮件、联系电话的文本字段和一个用于将输入的文本输入数据库的按钮。如何将输入的 4 个字段中的文本保存到数据库中?提前致谢。这是我的代码: 我成功地将数据输入数据库,在 registerViewController 但如果用户忘记密码和/或用户名,我想在“btnforgot”(忘记密码)操作中输入代码。我在 loginViewController 中也取得了一些成功,就检查用户名和密码是否在数据库中匹配相同,但我想我在两者之间遗漏了一些东西(一些代码)。

LOGINVIEW


#import "loginViewController.h"
#import "carTypeViewController.h"
#import "registerViewController.h"
#import "FMDatabase.h"
#import "AppDelegate.h"
#import "FMDatabaseAdditions.h"
@interface loginViewController ()

@end
@implementation loginViewController
@synthesize lblPass,lblUserName,txtPass,txtUser;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 如果(自我) // 自定义初始化 回归自我;

- (void)viewDidLoad


self.title = @"Enter Credentials";
username = [[NSMutableArray alloc]init];
password = [[NSMutableArray alloc]init];

[super viewDidLoad];
// Do any additional setup after loading the view from its nib.

- (void)didReceiveMemoryWarning

[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.

- (IBAction)btnLogin:(id)sender

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:@"contacts.db"];
FMDatabase *database = [FMDatabase databaseWithPath:path];

[database open];

BOOL success = NO;

    NSInteger count = [database intForQuery:@"select count(*) from RegMembers where USERNAME = ? and PASSWORD = ?", username, password];

if ([txtUser.text length] == 0 || [txtPass.text length]== 0)
    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Error" message:@"Kindly enter details in all fields" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [alert show];


else if (count > 0)

        success = YES;

        UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Welcome" message:@"Login successful" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [alert show];

        carTypeViewController *car = [[carTypeViewController alloc]initWithNibName:@"carTypeViewController" bundle:nil];
        [self.navigationController pushViewController:car animated:YES];


    else
    
        UIAlertView *alert1 = [[UIAlertView alloc]initWithTitle:@"Kindly enter the correct credentials" message:@"Entered username or password is incorrect" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [alert1 show];

        txtUser.text=@"";
        txtPass.text=@"";
    
[database close];

-(IBAction)removeKeyboard

[self.txtUser resignFirstResponder];
[self.txtPass resignFirstResponder];


- (IBAction)btnSignUp:(id)sender 
registerViewController *reg = [[registerViewController alloc]initWithNibName:@"registerViewController" bundle:nil];
[self.navigationController pushViewController:reg animated:YES];



- (IBAction)btnExit:(id)sender 
exit(1);



- (IBAction)btnForgotPass:(id)sender 


@end






REGISTERVIEW

- (void)viewDidLoad

self.title = @"Create Account";
[super viewDidLoad];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,      NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:@"contacts.db"];
FMDatabase *database = [FMDatabase databaseWithPath:path];

[database open];

[database executeUpdate:@"create table if not exists RegMembers (ID INTEGER NOT NULL     PRIMARY KEY AUTOINCREMENT DEFAULT 0, USERNAME TEXT UNIQUE NOT NULL , PASSWORD TEXT NOT NULL     , EMAIL TEXT NOT NULL , NUMBER TEXT  NOT NULL)"];

[database close];

- (void)didReceiveMemoryWarning

[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.


-(IBAction)removeKeyboard

[self.txtUname resignFirstResponder];
[self.txtPass resignFirstResponder];
[self.txtEmail resignFirstResponder];
[self.txtNumber resignFirstResponder];


- (IBAction)btnUAcount:(id)sender

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,     NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:@"contacts.db"];
FMDatabase *database = [FMDatabase databaseWithPath:path];

if ([txtUname.text length] == 0 ||
    [txtPass.text length]== 0   ||
    [txtEmail.text length]== 0  ||
    [txtNumber.text length]== 0)

    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Error" message:@"Kindly     enter details in all fields" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [alert show];


else


    if ([database open])
    

        [database executeUpdate:@"insert into RegMembers(username, password, email,     number) values(?,?,?,?)",
        [NSString stringWithFormat: @"%@",txtUname.text],[NSString     stringWithFormat:@"%@",txtPass.text],[NSString stringWithFormat:@"%@",txtEmail.text],    [NSString stringWithFormat:@"%@",txtNumber.text],nil];
        if (1)
        
            NSLog(@"contact added");
            self.txtUname.text = @"";
            self.txtPass.text = @"";
            self.txtEmail.text = @"";
            self.txtNumber.text = @"";
        
        else
        
            NSLog(@"failed to add contact");
        

    
    [database close];
    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Registeration succesful"     message:@"Thanks for choosing edmunds" delegate:nil cancelButtonTitle:@"OK"     otherButtonTitles: nil];
    [alert show];
    [self.navigationController popViewControllerAnimated:YES];



【问题讨论】:

【参考方案1】:

如果您想检查用户名/密码组合是否在您的数据库中,您可以:

#import "FMDatabaseAdditions"

然后您可以查看用户名/密码组合是否符合成功登录的条件,如下所示:

BOOL success = NO;

NSInteger count = [database intForQuery:@"select count(*) from RegMembers where USERNAME = ? and PASSWORD = ?", username, password];

if (count > 0)
    success = YES;

话虽如此,我有一些观察,从琐碎到关键:

    现在您总是报告“已添加联系人”。您应该真正确定插入是否成功。您可以通过检查 executeUpdate 方法返回的值来执行此操作(如果成功则返回 YES,如果 INSERT 失败则返回 NO)。

    使用PRIMARY KEY AUTOINCREMENT 键时,不应使用DEFAULT 0。首先,我认为这不会完成任何事情(无论如何,它将从1 开始)。其次,即使是这样,使用零也是非常糟糕的,因为 FMDB 的 lastInsertRowId 调用 SQLite sqlite3_last_insert_rowid,使用数字键的零值来指示错误。所以,永远不要使用零。

    更重要的是,您应该永远在未加密的设备上存储密码。 (即使您不关心应用的安全性,也有太多用户重复使用密码,因此您要确保自己不会成为他们密码被泄露的可能途径。)

    至少,您希望在将密码存储在数据库中时对其进行加密,然后在登录时对用户提供的密码进行加密,并比较两个加密密码。如果您想查看一些可用的加密服务,请参阅Cryptographic Services Guide。

    话虽如此,您甚至可能不想自己编写任何密码加密代码(要正确执行此操作并非完全是微不足道的)。

    使用 ios 钥匙串可能会更好。 Apple 关于该主题的入门读物是 WWDC 2013 视频Protecting Secrets with the Keychain。

    例如,要保存与用户 ID 关联的密码、电子邮件地址和号码,您可以执行以下操作:

    // see if the userid already exists
    
    NSDictionary* query = @(__bridge id)kSecClass       : (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecAttrService : @"com.domain.app",
                            (__bridge id)kSecAttrAccount : self.useridTextField.text,
                            (__bridge id)kSecMatchLimit  : (__bridge id)kSecMatchLimitOne,
                            (__bridge id)kSecReturnData  : @NO;
    
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL);
    
    if (status == errSecSuccess)
    
        [[[UIAlertView alloc] initWithTitle:nil message:@"That userid is already registered" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
        return;
    
    
    // create payload of secret data
    
    NSDictionary *secret = @@"password" : self.passwordTextField.text ?: [NSNull null],
                             @"email"    : self.emailTextField.text    ?: [NSNull null],
                             @"number"   : self.numberTextField.text   ?: [NSNull null];
    
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:secret];
    
    // create keychain query
    
    query = @(__bridge id)kSecClass       : (__bridge id)kSecClassGenericPassword,
              (__bridge id)kSecAttrService : @"com.domain.app",
              (__bridge id)kSecAttrAccount : self.useridTextField.text,
              (__bridge id)kSecValueData   : data;
    
    // add data associated with the userid in the keychain
    
    status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
    NSAssert(status == errSecSuccess, @"%s: SecItemAdd error: %lu", __FUNCTION__, (unsigned long)status);
    

    然后,当您想登录时,您可以执行以下操作:

    BOOL success = NO;
    
    NSDictionary* query = @(__bridge id)kSecClass       : (__bridge id)kSecClassGenericPassword,
                            (__bridge id)kSecAttrService : @"com.domain.app",
                            (__bridge id)kSecAttrAccount : self.useridTextField.text,
                            (__bridge id)kSecMatchLimit  : (__bridge id)kSecMatchLimitOne,
                            (__bridge id)kSecReturnData  : @YES;
    
    CFTypeRef typeRef;
    
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &typeRef);
    
    // if we found keychain entry for that userid, check the password
    
    if (status == noErr)
    
        NSDictionary *dictionary = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)typeRef];
        NSAssert(dictionary, @"%s: unarchiveObjectWithData failed: %@", __FUNCTION__, (__bridge NSData *)typeRef);
        NSString *password = dictionary[@"password"];
        if ([self.passwordTextField.text isEqualToString:password])
            success = YES;
    
        CFRelease(typeRef);
    
    
    // if we didn't succeed for any reason, tell the user
    
    if (!success)
    
        [[[UIAlertView alloc] initWithTitle:nil message:@"Login failed" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil] show];
    
    

【讨论】:

嗯,我同意你在文本中给我的建议,但坦率地说,我是数据库/fmdb 和 ios 的新手。 我确信除了 alertview、nsdictionary 和基本/常规 ios 代码之外,我没有从代码部分得到任何东西。一些基本教程的链接将不胜感激。其次,一个更简单的解决方案/答案,尽管不是高级代码会帮助我,尽管这里每个人的建议都可以帮助我学到很多东西。 :-D @Neelang 在我回答的开头,我向您展示了如何在数据库中查找用户 ID/密码(尽管我对存储未加密的密码非常担心)。顺便说一句,FMDB 支持SQLiteEncrypt(您必须单独购买),因此如果您不想处理钥匙串解决方案,这可能是加密数据库的另一种解决方案。关于正确进行加密这一令人恐惧的复杂主题的良好教程,我只能向您推荐我在答案中链接到的 WWDC 视频。祝你好运! 我理解您对应用程序安全性和加密等的担忧和建议。但是这个登录页面是我非常重要的项目的一部分,我必须尽快提交,所以我想你是否可以暂时忘记安全/加密等,并为我提供一个简单、简单的解决方案。尽管我非常重视您完全正确的建议。 @Neelang 那么我的答案开头的intForQuery 代码就是您登录屏幕需要做的所有事情,以确认是否在数据库中找到了用户名/密码组合。因此,当用户点击“登录”按钮时,输入用户名和密码后,只需执行intForQuery 以确认是否在数据库中找到它。 (如果这是出于演示目的,您可以放弃加密。但如果是生产应用程序,则不可取。)

以上是关于如何将 fmdb 用于登录页面?的主要内容,如果未能解决你的问题,请参考以下文章

Angular 制作安全登录/注册页面

从另一个页面填写登录表单

如果未登录,如何将用户重定向到登录页面

如果登录成功,如何将用户从登录页面重定向到另一个反应页面?

如何爬取淘宝登录页面

Buildfire:如何修改登录和注册页面?