Basic Authentication using Restkit

2019-07-15 08:27发布

问题:

I am trying to do Basic Authentication using RestKit but I need some help with figuring out why I get status code 401(Unauthorized). I tried this, but it did not work for me. I will appreciate if someone can point out my mistake.

This is what my data looks like on the server side: Sample

{
  "Id": "baec6f47",
  "TokenValidity": "00:00:00.1234567",
  "ValidTo": "2013-10-28T23:54:21.2934278+00:00",
  "Parameters": {},
  "Token": "sample string 3"
}

I have a NSObject class called TokenObject, and that where I am need to store this data.

TokenObject.h

#import <Foundation/Foundation.h>

@interface TokenObject : NSObject
@property (nonatomic,strong) NSNumber *tokenId;
@property (nonatomic,strong) NSDate *TokenValidity;
@property (nonatomic,strong) NSDate *ValidTo;
@property (nonatomic,strong)NSString *Parameters;
@property (nonatomic,strong)NSString *Token;

@end

Quick Overview:

In the login screen I only ask for the username, and once the user clicks on sign up, I sent a Post request to create the account and send a get request for the token(and use the RestKit's setAuthorizationHeaderWithUsername right before the get request). For the username:password, I am using the the nickname and hardware id.

This is the method where I am posting the account(works), encode the username and password to base 64(works) and trying to get the token(which is the part i am having problem with).

LoginViewController.h

#import <UIKit/UIKit.h>

@interface LoginViewController : UIViewController<UITextFieldDelegate,NSURLConnectionDelegate>

@property (strong, nonatomic) IBOutlet UITextField *usernameTextField;
@property (strong, nonatomic) IBOutlet UIButton *submitButton;
@property (nonatomic,readonly) NSUUID *identifierForVendor;
@property(nonatomic, readonly, retain) NSString *model;
@property (nonatomic,readonly,retain)NSString *StoreIdentifierForVendor;
@property (nonatomic,readonly,retain)NSString *StoreTheModel;
- (IBAction)submit:(id)sender;
@property (nonatomic,strong)NSString *nickname;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property(nonatomic,strong)NSString *encodedName;
@property(nonatomic,strong)NSString *encodedHardwareId;
@property(nonatomic,strong)NSData *UserNameEncoding;
@property(nonatomic,strong)NSData *HardwareIdEncoding;
@end

LoginViewController.m

    //url for creating the account /Accounts

    //url for getting the token /Accounts/Token

   -(void)loadPostRequest
{

    _StoreIdentifierForVendor = [[[UIDevice currentDevice]identifierForVendor]UUIDString];
    _StoreTheModel = [UIDevice currentDevice].model;
    _nickname = usernameTextField.text;

    // AccountsClass is where I store the login information.
     AccountsClass *AccountInfo = [[AccountsClass alloc] init];
    AccountInfo.NickName = _nickname;
    AccountInfo.HardwareId =[[[UIDevice currentDevice]identifierForVendor]UUIDString];
    AccountInfo.DeviceType =[UIDevice currentDevice].model;

//    NSLog(@"HardwareId test %@",AccountInfo.HardwareId);

                /// ********* I can create accounts successfully ********* ///

    RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[AccountsClass class]];
      [responseMapping addAttributeMappingsFromArray:@[@"NickName", @"HardwareId", @"DeviceType",@"AccountId"]];

    NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx
    RKResponseDescriptor *AccountDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping method:RKRequestMethodAny pathPattern:nil keyPath:nil statusCodes:statusCodes];


    RKObjectMapping *requestMapping = [RKObjectMapping requestMapping]; // objectClass == NSMutableDictionary
    [requestMapping addAttributeMappingsFromArray:@[@"NickName", @"HardwareId", @"DeviceType",@"AccountId"]];

    RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[AccountInfo class] rootKeyPath:nil method:RKRequestMethodAny];
    RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"https://picquizstage.azurewebsites.net"]];
                                [manager addRequestDescriptor:requestDescriptor];
                                [manager addResponseDescriptor:AccountDescriptor];
                                // POST to create
    [manager postObject:AccountInfo path:@"/Accounts" parameters:nil success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
        RKLogConfigureByName("*", RKLogLevelTrace); // set all logs to trace,

        /// This is where I setup data for base 64 encoding ///

//        NSData *UserNameEncoding =[_nickname dataUsingEncoding:NSUTF8StringEncoding];
//        NSData *HardwareIdEncoding = [_StoreIdentifierForVendor dataUsingEncoding:NSUTF8StringEncoding];
        UserNameEncoding =[_nickname dataUsingEncoding:NSUTF8StringEncoding];
        HardwareIdEncoding = [_StoreIdentifierForVendor dataUsingEncoding:NSUTF8StringEncoding];
        //encoding
//        NSString *encodedName = [UserNameEncoding base64EncodedString];
//        NSString *encodedHardwareId = [HardwareIdEncoding base64EncodedString];
        encodedName =[UserNameEncoding base64EncodedString];
        encodedHardwareId =[HardwareIdEncoding base64EncodedString];

        //encoding test
        //    NSAssert([encodedName isEqualToString:@"WVlZWQ=="], @"output id for the nickname is failed");
        //    NSAssert([encodedHardwareId isEqualToString:@"OUU0RDY0NzUtN0UwOS00RDY3LUJBNEItRTcyRjEyMzg5QUZC"], @"output test for the hardware id is failed");
        //    RKLogConfigureByName("*", RKLogLevelTrace); // set all logs to trace,
        /*
         ******* TOKEN OBJECT MAPPING BEGINS HERE (PROBLEMATIC PART) *******

         */
        RKObjectMapping *TokenMapping = [RKObjectMapping mappingForClass:[TokenObject class]];
        [TokenMapping addAttributeMappingsFromDictionary:@{@"tokenId":@"Id",@"TokenValidity":@"TokenValidity",@"ValidTo":@"ValidTo",@"Parameters":@"Parameters",@"Token":@"Token"}];
        NSIndexSet *statusCodes2 = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx
        RKResponseDescriptor *responseDescriptor =[RKResponseDescriptor responseDescriptorWithMapping:TokenMapping method:RKRequestMethodGET pathPattern:@"/Accounts/Token" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
        RKObjectMapping *errorMapping =[RKObjectMapping mappingForClass:[RKErrorMessage class]];
        // The entire value at the source key path containing the errors maps to the message
        [errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@"message"]];

        // Any response in the 4xx status code range with an "errors" key path uses this mapping
        RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:TokenMapping method:RKRequestMethodAny pathPattern:nil keyPath:@"errors" statusCodes:statusCodes2];
        // Add our descriptors to the manager
        RKObjectManager *manager2 = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"https://picquizstage.azurewebsites.net"]];
        [manager2 addResponseDescriptorsFromArray:@[ responseDescriptor, errorDescriptor ]];
        [manager2.HTTPClient setAuthorizationHeaderWithUsername:encodedName password:encodedHardwareId];
        [manager2 getObjectsAtPath:@"/Accounts/Token" parameters:nil success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {

            TokenObject *myObjects =[[TokenObject alloc]init];
            NSLog(@"The tokenId is successful %@",myObjects.tokenId);
            NSLog(@"The ValidTo is successful %@",myObjects.ValidTo);
            NSLog(@"The TokenValidity is successful %@",myObjects.TokenValidity);
            RKLogConfigureByName("*", RKLogLevelTrace); // set all logs to trace,


        }
                           failure:^(RKObjectRequestOperation *operation, NSError *error) {
                               RKLogConfigureByName("*", RKLogLevelTrace); // set all logs to trace,
                           }
         ];

    } failure:nil];

            }

Console Output

  2013-10-29 09:46:47.641 GuessTheImage[9651:70b] I restkit:RKLog.m:33 RestKit logging initialized...
2013-10-29 09:47:20.035 GuessTheImage[9651:70b] LoginViewController - Submit Action 
2013-10-29 09:47:20.108 GuessTheImage[9651:70b] I restkit.network:RKObjectRequestOperation.m:150 POST 'https://picquizstage.azurewebsites.net/Accounts'
2013-10-29 09:47:20.572 GuessTheImage[9651:4d0f] I restkit.network:RKObjectRequestOperation.m:220 POST 'https://picquizstage.azurewebsites.net/Accounts' (201 Created / 1 objects) [request=0.4625s mapping=0.0015s total=0.5414s]
2013-10-29 09:47:20.580 GuessTheImage[9651:70b] T restkit.network:RKObjectRequestOperation.m:148 GET 'https://picquizstage.azurewebsites.net/Accounts/Token':
request.headers={
    Accept = "application/json";
    "Accept-Language" = "en;q=1, fr;q=0.9, de;q=0.8, zh-Hans;q=0.7, zh-Hant;q=0.6, ja;q=0.5";
    Authorization = "Basic Vm05c2EyOW1aZz09Ok5rTkJOa015UXpVdE5qVTJOaTAwTVRFekxVSXpSVGt0TUVWRFJUQTBOak5EUWpJMg==";
    "User-Agent" = "GuessTheImage/1.0 (iPhone Simulator; iOS 7.0.3; Scale/2.00)";
}
request.body=(null)
2013-10-29 09:47:20.677 GuessTheImage[9651:4d0f] E restkit.network:RKObjectRequestOperation.m:547 Object request failed: Underlying HTTP request operation failed with error: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1011 "Expected status code in (200-299), got 401" UserInfo=0x8c546f0 {NSLocalizedRecoverySuggestion=, AFNetworkingOperationFailingURLRequestErrorKey=<NSMutableURLRequest: 0x8ba00e0> { URL: https://quizstage.azurewebsites.net/Acounts/Tokn }, NSErrorFailingURLKey=https://quizstage.azurewebsites.net/Acounts/Tokn, NSLocalizedDescription=Expected status code in (200-299), got 401, AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x8c6fb90> { URL: https://quizstage.azurewebsites.net/Acounts/Tokn } { status code: 401, headers {
    "Cache-Control" = "no-cache";
    "Content-Length" = 0;
    Date = "Tue, 29 Oct 2013 16:47:20 GMT";
    Expires = "-1";
    Pragma = "no-cache";
    Server = "Microsoft-IIS/8.0";
    "Www-Authenticate" = "Basic Scheme='PizQuiz' location=http://{0}/Acount/Tokn";
    "X-AspNet-Version" = "4.0.30319";
    "X-Powered-By" = "ASP.NET";
} }}
2013-10-29 09:47:20.679 GuessTheImage[9651:4d0f] E restkit.network:RKObjectRequestOperation.m:208 GET 'https://picquizstage.azurewebsites.net/Acounts/Tokn' (401 Unauthorized / 0 objects) [request=0.0968s mapping=0.0000s total=0.0986s]:
error=Error Domain=org.restkit.RestKit.ErrorDomain Code=-1011 "Expected status code in (200-299), got 401" UserInfo=0x8c546f0 {NSLocalizedRecoverySuggestion=, AFNetworkingOperationFailingURLRequestErrorKey=<NSMutableURLRequest: 0x8ba00e0> { URL: https://picquizstage.azurewebsites.net/Acounts/Tokn }, NSErrorFailingURLKey=https://picquizstage.azurewebsites.net/Acounts/Tokn, NSLocalizedDescription=Expected status code in (200-299), got 401, AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x8c6fb90> { URL: https://picquizstage.azurewebsites.net/Acounts/Tokn } { status code: 401, headers {
    "Cache-Control" = "no-cache";
    "Content-Length" = 0;
    Date = "Tue, 29 Oct 2013 16:47:20 GMT";
    Expires = "-1";
    Pragma = "no-cache";
    Server = "Microsoft-IIS/8.0";
    "Www-Authenticate" = "Basic Scheme='PizQuiz' location=http://{0}/Acount/Tokn";
    "X-AspNet-Version" = "4.0.30319";
    "X-Powered-By" = "ASP.NET";
} }}
response.body=

回答1:

From your last comment, this line:

[manager2.HTTPClient setAuthorizationHeaderWithUsername:@"encodedName" password:@"encodedHardwareId"];

is the issue because you aren't actually passing the encodedName and encodedHardwareId variables, you're passing 2 constant strings. So it should be:

[manager2.HTTPClient setAuthorizationHeaderWithUsername:encodedName password:encodedHardwareId];