SQLite数据库访问不起作用

时间:2013-09-30 22:39:53

标签: ios objective-c sqlite

这可能看起来有点复杂和令人困惑但与我无关。我有一个Dashboard ViewController,用于检查数据库是否存在,如果没有,则将用户发送到登录ViewController,然后创建数据库。然后,用户必须登录,并将其密码和用户名保存到SQLite数据库中。然后将它们发送到仪表板ViewController。这个问题是,一旦用户登录并被发送回仪表板ViewController,他们就会被拒绝并被发送回登录ViewController。我不知道信息是否未保存到数据库中或发生了什么但这里是我的登录文件

#import "LoginView.h"
#import "SBJson.h"
#import "SignupView.h"
#import "sqlite3.h"

@interface LoginView ()

@end

@implementation LoginView


@synthesize txtPassword;
@synthesize txtUsername;

-(void)viewDidLoad {
    NSString *docsDir;
    NSArray *dirPaths;

    // Get the documents directory
    dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    docsDir = [dirPaths objectAtIndex:0];

    // Build the path to the database file
    databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: @"username.db"]];

    NSFileManager *filemgr = [NSFileManager defaultManager];

    if ([filemgr fileExistsAtPath: databasePath ] == NO)
    {
        const char *dbpath = [databasePath UTF8String];

        if (sqlite3_open(dbpath, &usernameDB) == SQLITE_OK)
        {
            char *errMsg;
            const char *sql_stmt = "CREATE TABLE IF NOT EXISTS USERNAME (USERNAME TEXT, PASSWORD TEXT)";

            if (sqlite3_exec(usernameDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
            {

            }

            sqlite3_close(usernameDB);

        } else {

        }
    }
    [super viewDidLoad];
}

- (void) alertStatus:(NSString *)msg :(NSString *)title
{
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title
                                                        message:msg
                                                       delegate:self
                                              cancelButtonTitle:@"Ok"
                                              otherButtonTitles:nil, nil];

    [alertView show];
}

- (IBAction)signupButton:(id)sender {
    SignupView *myView = [[SignupView alloc] initWithNibName:@"SignupView" bundle:nil];
    [myView setModalPresentationStyle:UIModalPresentationFormSheet]; //you can change the way it is presented
    [myView setModalTransitionStyle:UIModalTransitionStyleCoverVertical]; //you can change the animation
    [self presentViewController:myView animated:YES completion:nil]; //show the modal view

}

- (IBAction)backgroundClicked:(id)sender {
    [txtUsername resignFirstResponder];
    [txtPassword resignFirstResponder];
}

- (IBAction)loginClicked:(id)sender {


    @try {

        if([[txtUsername text] isEqualToString:@""] || [[txtPassword text] isEqualToString:@""] ) {
            [self alertStatus:@"Please enter both Username and Password" :@"Login Failed!"];
        } else {
            NSString *post =[[NSString alloc] initWithFormat:@"username=%@&password=%@",[txtUsername text],[txtPassword text]];
            NSLog(@"PostData: %@",post);

            NSURL *url=[NSURL URLWithString:@"http://example.com/ios_login/index.php"];

            NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];

            NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];

            NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
            [request setURL:url];
            [request setHTTPMethod:@"POST"];
            [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
            [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
            [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
            [request setHTTPBody:postData];

            //[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];

            NSError *error = [[NSError alloc] init];
            NSHTTPURLResponse *response = nil;
            NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

            NSLog(@"Response code: %d", [response statusCode]);
            if ([response statusCode] >=200 && [response statusCode] <300)
            {
                NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
                NSLog(@"Response ==> %@", responseData);

                SBJsonParser *jsonParser = [SBJsonParser new];
                NSDictionary *jsonData = (NSDictionary *) [jsonParser objectWithString:responseData error:nil];
                NSLog(@"%@",jsonData);
                NSInteger success = [(NSNumber *) [jsonData objectForKey:@"success"] integerValue];
                NSLog(@"%d",success);
                if(success == 1)
                {
                    [self saveData];

                } else {

                    NSString *error_msg = (NSString *) [jsonData objectForKey:@"error_message"];
                    [self alertStatus:error_msg :@"Login Failed!"];
                }

            } else {
                if (error) NSLog(@"Error: %@", error);
                [self alertStatus:@"Connection Failed" :@"Login Failed!"];
            }
        }
    }
    @catch (NSException * e) {
        NSLog(@"Exception: %@", e);
        [self alertStatus:@"Login Failed." :@"Login Failed!"];
    }
}

-(void)saveData{
    sqlite3_stmt    *statement;

    const char *dbpath = [databasePath UTF8String];

    if (sqlite3_open(dbpath, &usernameDB) == SQLITE_OK)
    {
        NSString *insertSQL = [NSString stringWithFormat: @"INSERT INTO USERNAME (username, password) VALUES (\"%@\", \"%@\")", txtUsername.text, txtPassword.text];

        const char *insert_stmt = [insertSQL UTF8String];

        sqlite3_prepare_v2(usernameDB, insert_stmt, -1, &statement, NULL);
        if (sqlite3_step(statement) == SQLITE_DONE)
        {

        }
        sqlite3_finalize(statement);
        sqlite3_close(usernameDB);

        [self dismissViewControllerAnimated:YES completion:nil];


    }
}

@end

这是我的仪表板M

#import "ViewController.h"
#import "LoginView.h"

@interface ViewController ()

@end

@implementation ViewController
- (void)viewDidAppear:(BOOL)animated {
    [self checkIfLogged];

}

- (void) checkIfLogged
{
    const char *dbpath = [databasePath UTF8String];
    sqlite3_stmt    *statement;

    if (sqlite3_open(dbpath, &usernameDB) == SQLITE_OK) //second if
    {
        NSString *querySQL = [NSString stringWithFormat: @"SELECT * FROM username"];

        const char *query_stmt = [querySQL UTF8String];

        if (sqlite3_prepare_v2(usernameDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
        {
            if (sqlite3_step(statement) == SQLITE_ROW)
            {//match found



            } else {// match not found

                LoginView *loginView = [[LoginView alloc] initWithNibName:@"LoginView" bundle:nil];
                [loginView setModalPresentationStyle:UIModalPresentationFormSheet];
                [loginView setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
                [self presentViewController:loginView animated:YES completion:nil];



            }//end else
            sqlite3_finalize(statement);


        }//end second if
        else{

            LoginView *loginView = [[LoginView alloc] initWithNibName:@"LoginView" bundle:nil];
            [loginView setModalPresentationStyle:UIModalPresentationFormSheet];
            [loginView setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
            [self presentViewController:loginView animated:YES completion:nil];

        }
        sqlite3_close(usernameDB);

    }//end first IF
   }//end checkIfLogged

@end

我感觉信息没有保存到数据库中,但我不知道为什么。信息应保存在saveData()

下的登录视图中

1 个答案:

答案 0 :(得分:0)

有几点想法:

  1. 如果您的任何SQLite调用失败,则不会显示任何错误或记录任何消息。因此,您自己很难确定任何问题。如果SQLite调用失败,您应该记录一些消息,例如,而不是:

    sqlite3_prepare_v2(usernameDB, insert_stmt, -1, &statement, NULL);
    if (sqlite3_step(statement) == SQLITE_DONE)
    {
    
    }
    

    你应该这样做:

    if (sqlite3_prepare_v2(usernameDB, insert_stmt, -1, &statement, NULL) != SQLITE_OK)
    {
        NSLog(@"%s: insert prepare failed: %s", __FUNCTION__, sqlite3_errmsg(usernameDB));
        sqlite3_close(usernameDB);
        return;
    }
    
    if (sqlite3_step(statement) != SQLITE_DONE)
    {
        NSLog(@"%s: insert step failed: %s", __FUNCTION__, sqlite3_errmsg(usernameDB));
    }
    

    您应该为调用的每个 SQLite函数执行此类日志记录。这不仅会在开发过程中通知您是否存在问题,更重要的是,sqlite3_errmsg功能会告诉您问题所在。

    如果您没有报告错误,那么检查您的SQLite返回代码几乎没有什么好处。这样做,您可以准确识别出错的地方。这个问题可能在这一点上变得不言而喻,但如果没有,请告诉我们。

  2. 一旦您遇到此问题,您应该查看您的网络请求。你真的应该异步地进行网络请求(例如sendAsynchronousRequest),而不是同步。您永远不应该在主队列上执行同步请求。 (见Avoid Synchronous Networking Calls on the Main Thread。)

  3. 也不相关,但您不应该使用stringWithFormat构建SQL。如果密码中有引号怎么办?如果用户不小心输入带引号的内容,那么您的SQLite调用最多也会失败。更糟糕的是,你打开了SQL注入攻击。

    您应该在SQL中使用?占位符,然后将值绑定到这些占位符,例如sqlite3_bind_text

    const char *insert_stmt = "INSERT INTO USERNAME (username, password) VALUES (?, ?)";
    
    if (sqlite3_prepare_v2(usernameDB, insert_stmt, -1, &statement, NULL) != SQLITE_OK)
    {
        NSLog(@"%s: insert prepare failed: %s", __FUNCTION__, sqlite3_errmsg(usernameDB));
        sqlite3_close(usernameDB);
        return;
    }
    
    if (sqlite3_bind_text(insert_stmt, 1, [txtUsername.text UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK)
    {
        NSLog(@"%s: insert bind user failed: %s", __FUNCTION__, sqlite3_errmsg(usernameDB));
        sqlite3_finalize(statement);
        sqlite3_close(usernameDB);
        return;
    }
    
    if (sqlite3_bind_text(insert_stmt, 2, [txtPassword.text UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK)
    {
        NSLog(@"%s: insert bind password failed: %s", __FUNCTION__, sqlite3_errmsg(usernameDB));
        sqlite3_finalize(statement);
        sqlite3_close(usernameDB);
        return;
    }
    
    if (sqlite3_step(statement) != SQLITE_DONE)
    {
        NSLog(@"%s: insert step failed: %s", __FUNCTION__, sqlite3_errmsg(usernameDB));
        sqlite3_close(usernameDB);
        return;
    }
    
    sqlite3_finalize(statement);
    sqlite3_close(usernameDB);
    
  4. 此外,您应该百分比转义您在网络请求中传递的用户名和密码参数。如果密码中有&符号或加号怎么办? (&符号会过早终止密码。加号会转换为空格。)

    有些人建议使用stringByAddingPercentEscapesUsingEncoding,但实际上你必须使用CFURLCreateStringByAddingPercentEscapes,这会给你一点控制权。因此,您可以定义一个方法:

    - (NSString *)stringForPostParameterValue:(NSString *)string
    {
    
        NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                                     (CFStringRef)string,
                                                                                     (CFStringRef)@" ",
                                                                                     (CFStringRef)@";/?:@&=+$,",
                                                                                     kCFStringEncodingUTF8));
        return [result stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    }
    

    所以,而不是:

    NSString *post =[[NSString alloc] initWithFormat:@"username=%@&password=%@",[txtUsername text],[txtPassword text]];
    

    然后你会:

    NSString *post = [NSString stringWithFormat:@"username=%@&password=%@", [self stringForPostParameterValue:[txtUsername text]], [self stringForPostParameterValue:[txtPassword text]]];
    

    如果您使用的密码测试当前代码,例如其中包含+&,您就会明白为什么必须执行上述操作。

  5. 你显然不应该以明文形式传输/存储密码。但这是一个更大的话题。