使用Rails App + Devise登录iOS的问题

时间:2014-09-25 03:02:23

标签: android ios ruby-on-rails devise

我有一个Rails应用程序,我正在设计一个iOS应用程序。 Rails应用程序使用Devise进行用户登录。当用户通过移动应用程序登录时,Rails应用程序应返回一个身份验证令牌,然后在整个应用程序中使用。

我已经有了一个有效的Android应用程序,我按照此页面上的教程启用了通过Android应用程序登录:

http://lucatironi.github.io/tutorial/2012/10/15/ruby_rails_android_app_authentication_devise_tutorial_part_one/

这就是我的自定义Devise SessionsController的样子:

class SessionsController < Devise::SessionsController
  skip_before_filter :verify_authenticity_token,
                     :if => Proc.new { |c| c.request.format == 'application/json' }
warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure")
render :status => 200,
       :json => { :success => true,
                  :info => "Logged in",
                  :data => { :auth_token => current_user.authentication_token } }
end
end

这一直适用于我的Android应用。

但是我在使用我的iOS应用程序登录时遇到了一个奇怪的问题。以下是我登录的内容:

-(IBAction)authenticateUser {
    [keychainItem resetKeychainItem]; // remove any existing auth_token

    NSURL *url=[NSURL URLWithString:sessionsBaseURL];
    NSError *error = nil;
    NSDictionary * userDict = [[NSDictionary alloc] initWithObjectsAndKeys: [_txtUsername text], @"username", [_txtPassword text], @"password", nil];
    NSDictionary * holderDict = [[NSDictionary alloc] initWithObjectsAndKeys: userDict, @"user", nil];
    NSLog(@"holderDict: %@", holderDict);
    NSData * holder = [NSJSONSerialization dataWithJSONObject:holderDict options:0 error:&error];

    NSLog(@"auth_token%@", [keychainItem objectForKey:(__bridge id)(kSecAttrType)]);
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:url];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:holder];

    NSURLResponse * response = nil;
    NSData * receivedData = nil;

    receivedData = [NSMutableData data];
    receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    NSLog(@"attempting login");

    // get dictionary from json data
    NSDictionary * jsonResponse = [NSJSONSerialization
                                   JSONObjectWithData: receivedData
                                   options:kNilOptions
                                   error:&error];
    if(error == nil){
        NSLog(@"successful login");
        [keychainItem setObject:[_txtUsername text] forKey:(__bridge id)(kSecAttrAccount)];
        NSLog(@"jsonResponse: %@", jsonResponse);
        NSDictionary * dataResponse = [jsonResponse objectForKey:@"data"];
        auth_token = [dataResponse objectForKey:@"auth_token"];
        // save user authentication token
       [keychainItem setObject:auth_token forKey:(__bridge id)(kSecValueData)];
    }else{
        NSLog(@"jsonResponse: %@", jsonResponse);
    }
    NSLog(@"error: %@" , [error localizedDescription]);

    [self checkLoginApproved];
}

用户可以第一次正确登录,并返回正确的身份验证令牌。但是,如果用户注销然后使用iOS应用程序上的错误密码重新登录,则Rails应用程序似乎认为用户已正确登录(它不会返回未经授权的密码,并返回空认证令牌)。

这让我相信warden.authenticate有问题。在Rails应用程序或iOS应用程序中,我很难找出这个问题所在。

有没有人有一个让他们的iOS应用程序与Rails一起工作并设计用户登录的例子?或者有没有人对我为什么没有工作有其他想法?

1 个答案:

答案 0 :(得分:0)

Hello StackOverflow World。

它最终成为一个问题,主要是与我预期的warden.authenticate。我最终编写了一个自定义方法来确定用户的登录凭据是否有效(Rails):

def create
    user = User.find_for_authentication(:username=>params[:user][:username])
    if user && user.valid_password?(params[:user][:password])
      # generate token if it doesn't exist
      if user.authentication_token.blank?
          Rails.logger.debug "generating authentication token for #{user.username} #{user.id}"
          new_token = generate_authentication_token
          Rails.logger.debug "new authentication token: #{new_token}"
          user.update_column("authentication_token", new_token)
      end

       render :status => 200,
           :json => { :success => true,
                      :info => "Logged in",
                      :data => { :auth_token => user.authentication_token } }
        Rails.logger.debug "current user: #{current_user.username} #{current_user.id}"
        Rails.logger.debug "current user authentication_token: #{current_user.authentication_token}"
    else
        render :status => 401,
         :json => { :success => false,
                    :info => "Login Failed",
                    :data => {} }
    end

 def generate_authentication_token
    loop do
      token = Devise.friendly_token
      break token unless User.where(authentication_token: token).first
    end
  end

这就是我在iOS中登录的内容:

-(IBAction)authenticateUser {
    [keychainItem resetKeychainItem]; // remove any existing auth_token

    NSURL *url=[NSURL URLWithString:sessionsBaseURL];
    NSError *error = nil;
    NSDictionary * userDict = [[NSDictionary alloc] initWithObjectsAndKeys: [_txtUsername text], @"username", [_txtPassword text], @"password", nil];
    NSDictionary * holderDict = [[NSDictionary alloc] initWithObjectsAndKeys: userDict, @"user", nil];
    NSLog(@"holderDict: %@", holderDict);
    NSData * holder = [NSJSONSerialization dataWithJSONObject:holderDict options:0 error:&error];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:url];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:holder];

    NSURLResponse * response = nil;
    NSData * receivedData = nil;

    receivedData = [NSMutableData data];
    receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//    NSLog(@"attempting login");

    // get dictionary from json data
    NSDictionary * jsonResponse = [NSJSONSerialization
                                   JSONObjectWithData: receivedData
                                   options:kNilOptions
                                   error:&error];

    NSLog(@"jsonResponse: %@", jsonResponse);

    if( [[jsonResponse objectForKey:@"data"] objectForKey:@"auth_token"] ) {
        NSLog(@"saving login");
        NSDictionary * dataResponse = [jsonResponse objectForKey:@"data"];
        auth_token = [dataResponse objectForKey:@"auth_token"];
        [keychainItem setObject:[_txtUsername text] forKey:(__bridge id)(kSecAttrAccount)];
        // save user authentication token
        [keychainItem setObject:auth_token forKey:(__bridge id)(kSecAttrType)];
    }

    [self checkLoginApproved];
}

并退出:

NSString *logoutURLString = [NSString stringWithFormat:[sessionsBaseURL stringByAppendingFormat:@"?auth_token=%@", auth_token]];
    NSLog(@"logoutURLString: %@", logoutURLString);


    [request setURL:[NSURL URLWithString:logoutURLString]];
    [request setHTTPMethod:@"DELETE"];

    // receive JSON response
    NSURLResponse * response = nil;
    NSData * receivedData = nil;

    NSError *error = [[NSError alloc] init];

    receivedData = [NSMutableData data];
    receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

    NSString *jsonString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];

    [self.keychainItem resetKeychainItem];

我无法找到这样的例子,所以我希望这可以帮助其他人。