将文件上传到 OneDrive 会导致 401 错误

问题描述 投票:0回答:2

我正在使用 BITS 协议实现将大文件上传到 OneDrive。这是文档

我遇到以下问题。登录一段时间后,当我尝试创建会话时,我收到 401 错误。这是完整的回复:

<NSHTTPURLResponse: 0x7d6ee0f0> { URL: https://cid-da1cedf5484811a9.users.storage.live.com/items/DA1CEDF5484811A9!373/IMG_0003.JPG } { status code: 401, headers {
"BITS-Packet-Type" = Ack;
"Content-Length" = 0;
Date = "Mon, 22 Dec 2014 09:50:22 GMT";
P3P = "CP=\"BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo\"";
Server = "Microsoft-HTTPAPI/2.0";
"X-AsmVersion" = "UNKNOWN; 19.9.0.0";
"X-ClientErrorCode" = AccessDenied;
"X-MSNSERVER" = "SN3301____PAP101";
"X-QosStats" = "{\"ApiId\":0,\"ResultType\":2,\"SourcePropertyId\":0,\"TargetPropertyId\":42}";
"X-ThrowSite" = "4e1b.c093";
} }

这是我的请求:

{
Authorization = "EwCAAq1DBAAUGCCXc8wU/zFu9QnLdZXy+YnElFkAAYejThC8LqFmGJx4sT4Wv11wzA6Cj9tleMQ+PM4PGXSV/DAorkfRiVnt+KQnZRI90XL/FIfwTvhWmp6jFhsppM39GgwbQ7it0tSVCSp4cIFCFSphmV783o+MKPeuEsw79mRGL5Kz2lcrVUxQAq12vUZXnhkX4qiJj0RqMSdaFYghfVVmhkcGsJITOwIisFq/JyaRoffgcW7vZCu9/9Q1Jm62f+bcGur82sTo5ucwV4M7QNtl77nVjv3tPElcUDgZnTvCLuhIE6QHY6yHS+9blWVbaXJBBD0ZPjDbUdjIlbexto6VvXtjp+vELL8rMSJMYcGjN4AxbGmBDnjLm9Iy1RIDZgAACALsAIEvRqDnUAFmIs8toBlRzfxctJcm1wxfqY/531QSDMVG9pIISpNCPppHAa/blSV8+LbLO/hW5i/36/3RTLpFAiXkPxzbkI8OJSrVRDfKcXiK1x+kTNFtcxXLBlJSWYlMY9OcwT+v/JTQnoGD1z6L56zOX53Gt7SBZQ0of7e3QecxHLX7w9lqs0jBmRvwL9I/n0+r9r6CI3tTEnFODFyED9ToBCRwwjLLD8P4qWRXWC1BUL+20v2QCQYRKzSDxZkYa3WrPLA4PIEFdFp58/limBzDrGW4MaWi1UgaI/QF8gN5n+JLNE4YcZL8KaXFZ76zNWNhJFsg4Lctsu95l7oD9MkezDvpA8bhkoNp39rEvPjBEurrFkdHviv73Z/C1W+IoA4ww3ZAYtIt3THtvPE4wq2fUEcb+MhOIG7abZtu4P07+5Zy2UO1rGlChE/pvmtV4XXrrg3TLG5zAQ==";
"BITS-Packet-Type" = "Create-Session";
"BITS-Supported-Protocols" = "{7df0354d-249b-430f-820d-3d2a9bef4931}";
"X-Http-Method-Override" = "BITS_POST";
}

(我省略了用于打开会话的代码,因为它非常大)。

好吧,我首先想到的是访问令牌已经过时了,我需要以某种方式更新它。但!就在我使用相同的访问令牌执行检索用户 ID 的请求之前:

- (void)retrieveUserId
{
NSString                  *urlString            = [NSString stringWithFormat:@"https://apis.live.net/v5.0/me?access_token=%@", commonAccessToken];
NSURL                     *url                  = [NSURL URLWithString:urlString];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession              *session              = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSURLSessionDataTask      *task                 = [session dataTaskWithURL:url
                                                         completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
                                                             
                                                             NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                                                             
                                                             if (httpResponse.statusCode == 200)
                                                             {
                                                                 NSLog(@"Response retrieving user id %@", httpResponse);
                                                                 
                                                                 NSError      *parsingError     = nil;
                                                                 NSDictionary *parsedDictionary = [NSJSONSerialization JSONObjectWithData:data
                                                                                                                                  options:kNilOptions
                                                                                                                                    error:&parsingError];
                                                                 if (!parsingError)
                                                                 {
                                                                     commonUserId = parsedDictionary[@"id"];
                                                                     [self createUploadRequest];
                                                                 }
                                                                 else
                                                                 {
                                                                     dispatch_async(dispatch_get_main_queue(), ^{
                                                                         
                                                                         [self.delegate bitsClient:self didFailWithError:parsingError];
                                                                         
                                                                     });
                                                                 }
                                                             }
                                                             else
                                                             {
                                                                 dispatch_async(dispatch_get_main_queue(), ^{
                                                                     
                                                                     [self.delegate bitsClient:self didFailWithError:error];
                                                                     
                                                                 });
                                                             }
                                                         }];
[task resume];
}

而且它有效。当我获取用户 ID 时,此访问令牌就可以了。但您可以看到,与尝试打开会话时不同,访问令牌被放置为 URL 的一部分,而不是标头字段之一。我不确定这是否重要。到目前为止我无法解释这种行为。如果我注销并再次登录,那么就又没问题了,我不会再收到 401 错误,所以也许我真的应该更新令牌,但该怎么办呢?您还需要知道一件事 - 当我从

LiveConnectSession
获取访问令牌时,我会检查此会话是否已过期,但事实并非如此。它的
expires
属性晚于
[NSDate date]

还有一件事你要知道 - 尽管我无法打开会话,并且如果我尝试使用

Live SDK
LiveOperation
上传文件,我的请求会被拒绝并出现 401 错误,但文件上传时没有任何内容错误。

我想恢复:以 OneDrive 用户身份登录一段时间(似乎是 24 小时,但我不确定)后,我无法使用 BITS 协议上传文件,因为收到 401 错误。同时会话似乎没有过期,我仍然可以使用

LiveOperation
上传文件。此外,我可以使用打开上传会话时使用的相同访问令牌轻松获取用户 ID。如果我注销并再次登录,那么一切都会像魅力一样工作,我可以创建会话并上传文件。

ios objective-c cocoa-touch http onedrive
2个回答
0
投票

您在问题中提供的答复明确表明问题与身份验证有关 - 特别是 OneDrive 无法识别所提供的身份验证。过期是最有可能的解释,因此刷新可能就是答案。如果您请求“wl.offline_access”范围以及您需要的其他范围,您应该获得一个“refresh_token”作为 OAuth 2.0 流程的一部分,从而允许在先前的“access_token”过期时创建新的“access_token” 。我相信 Live SDK 本身会处理刷新过程,但我并不完全熟悉这方面的事情 - 现在请确保请求范围,我们将一起学习 SDK 是否处理其余部分:)


0
投票

Andrey,我是 php 开发人员,这就是我在 php 中所做的事情:

const API_URL = 'https://apis.live.net/v5.0/';
const INITAPIURL = 'https://login.live.com/oauth20_token.srf';
const URI_AUTHORIZE = 'https://login.live.com/oauth20_authorize.srf'; 

1)获取访问令牌并保存刷新令牌(下面的函数生成 URL 以重定向用户以进行 oAuth)

public function getAuthorizeUrl($cburl) {
        $cburl=_SKYDRIVE_REDIRECT_URI;
        return self::URI_AUTHORIZE.'?client_id='.urlencode(_SKYDRIVE_API_CLIENT_ID).'&scope='.urlencode('wl.offline_access wl.skydrive wl.skydrive_update wl.emails wl.contacts_skydrive wl.contacts_photos').'&response_type=code&redirect_uri='.urlencode($cburl);
    }

2)每 3300 秒(访问令牌持续 3600 秒)后,我直接在服务器上使用刷新令牌刷新访问令牌(无需用户在浏览器中确认)

$r = $this->http(self::INITAPIURL, 'POST', array("grant_type" => "refresh_token", "client_id" => _SKYDRIVE_API_CLIENT_ID, "client_secret" => _SKYDRIVE_API_SECRET, "redirect_uri" => _SKYDRIVE_REDIRECT_URI, "refresh_token" => $this->refreshtoken));

3)我正在使用curl将访问令牌作为标头发送给我对API的所有请求(上面的令牌刷新除外)

$headers[] = "Authorization: Bearer " . $this->token;

当我使用 BITS 时,上述步骤有效(我刚刚在 Perl 上实现了 BITS 协议,一切正常)

附注感谢您通过 ID 发布了文件夹的 URL,他们在文档中的 URL 错误:)

© www.soinside.com 2019 - 2024. All rights reserved.