2012-12-06 112 views
3

嗨夥計們,我的應用程序的登錄憑據的身份驗證有問題。我目前有一個從我的本地主機運行的rails web服務,它爲管理員用戶提供了主要的登錄屏幕,並帶有密碼憑據來顯示功能。身份驗證登錄問題RESTful iOS

問題是我正在創建一個RESTful iOS應用程序,與我的Rails應用程序一起運行,只需要這些JSON請求並繞過它們。

我想要的只是創建一種方法讓我輸入管理員和密碼,而不必使用auth_token?它似乎是做到這一點的唯一方法,只需在鑰匙串中首先對管理用戶進行身份驗證即可。 Im使用框架AFNetowrking進行身份驗證.SSKeychain用於帳戶包裝,SVProgressHUD用於輕量級部署

。我還在J終端中記錄了JSON和XML請求,但由於無法連接到服務器而失敗與此錯誤

error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server." UserInfo=0x7556d50 {NSErrorFailingURLStringKey=http://localhost:3000.json/, NSErrorFailingURLKey=http://localhost:3000.json/, NSLocalizedDescription=Could not connect to the server., NSUnderlyingError=0xeb805c0 "Could not connect to the server."} 

這是怎麼了IM存儲爲authClient的憑據,都希望做的是指定用於登錄到Web服務客戶端是相同的信息。

用戶名:admin和密碼:taliesin

但不確定如何做到這一點?

這些是我有這樣的AuthAPIClient,CredentialsStore和LoginViewController

更新如果有人知道一個更簡單的方法來做到這一點請你能告訴我,我將非常感激。

AuthAPIClient.m

#import "AuthAPIClient.h" 
#import "CredentialStore.h" 

#define BASE_URL @"http://admin:[email protected]:3000" 


@implementation AuthAPIClient 

+ (id)sharedClient { 

static AuthAPIClient *__instance; 
static dispatch_once_t onceToken; 
dispatch_once(&onceToken, ^{ 
    NSURL *baseUrl = [NSURL URLWithString:BASE_URL]; 
    __instance = [[AuthAPIClient alloc] initWithBaseURL:baseUrl]; 
}); 
return __instance; 
} 

- (id)initWithBaseURL:(NSURL *)url { 
self = [super initWithBaseURL:url]; 
if (self) { 
    [self registerHTTPOperationClass:[AFJSONRequestOperation class]]; 
    [self setAuthTokenHeader]; 

    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(tokenChanged:) 
               name:@"token-changed" 
               object:nil]; 
} 
return self; 
} 

- (void)setAuthTokenHeader { 
CredentialStore *store = [[CredentialStore alloc] init]; 
NSString *authToken = [store authToken]; 
[self setDefaultHeader:@"auth_token" value:authToken]; 
} 

- (void)tokenChanged:(NSNotification *)notification { 
[self setAuthTokenHeader]; 
} 

@end 

CredentialStore.m

#import "CredentialStore.h" 
#import "SSKeychain.h" 

#define SERVICE_NAME @"http://admin:[email protected]:3000" 
#define AUTH_TOKEN_KEY @"auth_token" 

    @implementation CredentialStore 


- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { 
return YES; 
} 

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 
NSString *user = [NSString stringWithFormat:@"%c%s%@", 'a', "a", @"a"]; 
NSString *password = [NSString stringWithFormat:@"%c%s%@", 'a', "a", @"a"]; 

NSURLCredential *credential = [NSURLCredential credentialWithUser:user 
                 password:password 
                   persistence:NSURLCredentialPersistenceForSession]; 
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; 

} 

- (BOOL)isLoggedIn { 
return [self authToken] != nil; 
} 

    - (void)clearSavedCredentials { 
[self setAuthToken:nil]; 
} 

    - (NSString *)authToken { 
    return [self secureValueForKey:AUTH_TOKEN_KEY]; 
    } 

    - (void)setAuthToken:(NSString *)authToken { 
[self setSecureValue:authToken forKey:AUTH_TOKEN_KEY]; 
[[NSNotificationCenter defaultCenter] postNotificationName:@"token-changed" object:self]; 
} 

    - (void)setSecureValue:(NSString *)value forKey:(NSString *)key { 
    if (value) { 
    [SSKeychain setPassword:@"taliesin" 
       forService:SERVICE_NAME 
        account:key]; 
    } else { 
    [SSKeychain deletePasswordForService:SERVICE_NAME account:key]; 
    } 
    } 

    - (NSString *)secureValueForKey:(NSString *)key { 
return [SSKeychain passwordForService:SERVICE_NAME account:key]; 
    } 

@end 

LoginViewApi.m

#import "LoginViewController.h" 
#import "AuthAPIClient.h" 
#import "CredentialStore.h" 
#import "SVProgressHUD.h" 

@interface UIViewController() 
@property (nonatomic, strong) IBOutlet UITextField *userTextField; 
@property (nonatomic, strong) IBOutlet UITextField *passwordTextField; 
@property (nonatomic, strong) CredentialStore *credentialStore; 
@end 

@implementation LoginViewController 

+ (void)presentModallyFromViewController:(UIViewController *)viewController { 
LoginViewController *loginViewController = [[LoginViewController alloc] init]; 
UINavigationController *navController = [[UINavigationController alloc] 
             initWithRootViewController:loginViewController]; 
[viewController presentViewController:navController 
          animated:YES 
          completion:nil]; 
} 
- (void)viewDidLoad { 
[super viewDidLoad]; 

self.credentialStore = [[CredentialStore alloc] init]; 

self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel 
         target:self 
                          action:@selector(cancel:)]; 
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Login" 
                      style:UIBarButtonItemStyleDone 
target:self 
                       action:@selector(login:)]; 

[self.userTextField becomeFirstResponder]; 
} 

- (void)login:(id)sender { 
[SVProgressHUD show]; 

id params = @{ 
@"admin": self.userTextField.text, 
@"taliesin": self.passwordTextField.text 
}; 

[[AuthAPIClient sharedClient] postPath:@"/auth/login.json" 
          parameters:params 
           success:^(AFHTTPRequestOperation *operation, id responseObject) { 

            NSString *authToken = [responseObject  objectForKey:@"auth_token"]; 
            [self.credentialStore setAuthToken:authToken]; 

            [SVProgressHUD dismiss]; 

            [self dismissViewControllerAnimated:YES completion:nil]; 

           } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 
            if (operation.response.statusCode == 500) { 
             [SVProgressHUD showErrorWithStatus:@"Something went wrong!"]; 
            } else { 
             NSData *jsonData = [operation.responseString dataUsingEncoding:NSUTF8StringEncoding]; 
             NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData 
                          options:0 
                           error:nil]; 
             NSString *errorMessage = [json objectForKey:@"error"]; 
             [SVProgressHUD showErrorWithStatus:errorMessage]; 
            } 
           }]; 

} 

- (void)cancel:(id)sender { 
[self dismissViewControllerAnimated:YES completion:nil]; 
} 

@end 

任何幫助或更多的疑問,請讓我知道歡呼:)

回答

1

端點解決

我不知道該怎麼處理AFNetworking遠程地址,但我認爲你是一個簡單的錯誤調用從iOSlocalhost時候聯繫你網絡服務。事情是iOS是一個操作系統,它有它自己的localhost。因此,當你認爲你打電話給一個遠程網絡服務時,你實際上要求iOS查看這個服務。因此,無論您是在本地還是在實際的遠程服務器上運行Web服務,都可以找到運行服務的服務器的IP地址,並將其用作端點而不是localhost

因此,更新指向您的服務使用IP的一切。不要更改它爲127.0.0.1,這將有相同的效果。

例如:

#define BASE_URL @"http://admin:[email protected]:3000" 

認證:iOS

假設你的Web服務支持,那就是,你已經配置了基本的HTTP認證;那麼從iOS認證不應該是一個巨大的問題。

RKObjectManager *objectManager = [RKObjectManager sharedManager]; 
objectManager.client.authenticationType = RKRequestAuthenticationTypeHTTPBasic; 
objectManager.client.username = username; 
objectManager.client.password = password; 

我使用蘋果的KeychainItemWrapper存儲/ iOS上檢索憑證:

如果你使用RESTKit,這將是那麼簡單。

//Store Account on Keychain (disk) for persistence. 
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
KeychainItemWrapper *accountItem = appDelegate.accountItem; 
[accountItem setObject:password forKey:(__bridge id)kSecValueData]; 
[accountItem setObject:username forKey:(__bridge id)kSecAttrAccount]; 
username = password = nil; //keep account information only in the keychain. 

我不知道,如果你的代碼是正確的,但它不應該是複雜的處理basic http auth。也許AFNetworking不像RESTKit那樣抽象化它?或者您可能沒有使用AFNetworking的正確方法做basic http auth?基本身份驗證不需要訪問令牌,因此我建議您在實施基本身份驗證之前多閱讀一下基本身份驗證。


認證:Rails

至於對Rails實施認證,它應該不會太複雜,要麼。您只需要將控制器配置爲通過basic http auth向傳入請求提供身份驗證質詢。一種方法是使用before_filter: authenticate。這是一個example

我認爲Railscasts非常棒,他們碰巧有一個教程basic http auth和一個專門爲rails 3.1. authentication

乾杯。