0

我有一个仪表板 ViewController,它检查数据库是否存在,如果不存在,则将用户发送到登录 ViewController,然后在其中创建数据库。然后用户必须登录,并且他们的密码和用户名被保存到 SQLite 数据库中。然后它们被发送到仪表板 ViewController。这样做的问题是,一旦用户确实登录并被发送回仪表板 ViewController,他们就会被拒绝并被发送回登录 ViewController。我不知道信息是否没有保存到数据库中或发生了什么但这是我的登录 m 文件

#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() 下的登录视图中

4

1 回答 1

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)而不是同步地进行网络请求。你永远不应该在主队列上做同步请求。(请参阅避免在主线程上进行同步网络调用。)

  3. 也不相关,但您不应该使用stringWithFormat. 如果密码中有引号怎么办?充其量,如果用户不小心输入了带引号的内容,您的 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]]];
    

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

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

于 2013-09-30T22:58:46.063 回答