我有一个Rails应用程序,我正在设计一个iOS应用程序。 Rails应用程序使用Devise进行用户登录。当用户通过移动应用程序登录时,Rails应用程序应返回一个身份验证令牌,然后在整个应用程序中使用。
我已经有了一个有效的Android应用程序,我按照此页面上的教程启用了通过Android应用程序登录:
这就是我的自定义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一起工作并设计用户登录的例子?或者有没有人对我为什么没有工作有其他想法?
答案 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];
我无法找到这样的例子,所以我希望这可以帮助其他人。