Hey Folks我的应用程序登录凭据验证有问题。我目前有一个从我的localhost运行的rails web服务,它具有一个管理员用户的主登录屏幕,其中包含用于显示功能的密码凭据。
问题是我正在创建一个与我的rails应用程序一起运行的RESTful iOS应用程序,它只接受这些JSON请求并绕过它们。
我想要的是创建一种方法让我输入管理员和密码而不必使用auth_token?它似乎是唯一的方法,只需在钥匙串中首先验证管理员用户。我使用框架AFNetowrking进行身份验证.SKKeychain是帐户的包装器,SVProgressHUD用于轻量级huds
。我也在i终端中记录了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."}
这是如何存储authClient的凭据,所有想要做的是指定用于登录Web服务客户端的相同信息。
用户名:admin和密码:taliesin
但不确定如何做到这一点?
这些是我对AuthAPIClient,CredentialsStore和LoginViewController的所有内容
更新如果有人知道更简单的方法,请您告诉我,我们将不胜感激。
AuthAPIClient.m
#import "AuthAPIClient.h"
#import "CredentialStore.h"
#define BASE_URL @"http://admin:taliesin@localhost: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:taliesin@localhost: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
任何帮助或更多问题请让我知道欢呼:)
答案 0 :(得分:1)
端点寻址
我不知道AFNetworking
如何处理远程地址,但我认为从localhost
致电iOS
以联系您的网络服务时,您犯了一个简单的错误。问题是iOS
是一个操作系统,它有自己的localhost
。因此,当您认为您正在呼叫远程Web服务时,您实际上要求iOS
查看此服务。因此,当您在本地或实际远程服务器上运行rails Web服务时,请找出运行该服务的服务器的IP地址,并将其用作端点而不是localhost
。
因此,请更新指向您的服务的所有内容以使用IP。不要为127.0.0.1
更改它,这会产生相同的效果。
例如:
#define BASE_URL @"http://admin:taliesin@192.168.0.1:3000"
身份验证: iOS
假设您的网络服务支持,即您已配置基本的http身份验证;然后从iOS
进行身份验证应该不是一个大问题。
如果你使用RESTKit
,那就简单了:
RKObjectManager *objectManager = [RKObjectManager sharedManager];
objectManager.client.authenticationType = RKRequestAuthenticationTypeHTTPBasic;
objectManager.client.username = username;
objectManager.client.password = password;
我使用Apple的KeychainItemWrapper
在iOS上存储/检索凭据。
//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专门的教程。
干杯。