Swift Alamofire 上的 Django oauth2 令牌请求失败

Posted

技术标签:

【中文标题】Swift Alamofire 上的 Django oauth2 令牌请求失败【英文标题】:Django oauth2 token request fails on Swift Alamofire 【发布时间】:2016-08-27 18:37:33 【问题描述】:

我正在构建一个 ios 客户端和一个 django 后端服务。系统之间的连接是 OAUTH2,由 django-oauth2-toolkit 实现。

尽管在 curl 中完成的以下命令有效(返回访问令牌):

curl -X POST -d "grant_type=password&username=<user>&password=<password>" http://<clientID>:<clientSecret>@localhost:8000/o/token/

以下使用 Alamofire 的 Swift sn-p 接收“invalid_client”作为响应。

let request = "http://\(Authentication.clientId):\(Authentication.clientSecret)@localhost:8000/o/token/"
var URLRequest = NSMutableURLRequest(URL: NSURL(string: request)!)
URLRequest.HTTPMethod = "POST"

let parameters = ["grant_type": "password", "username": in_username.text!, "password": in_password.text!]

let encoding = Alamofire.ParameterEncoding.URL
(URLRequest, _) = encoding.encode(URLRequest, parameters: parameters)
URLRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

Alamofire.request(URLRequest)
    .responseJSON  response in
        let data = response
        print(data)
    

然后我在django-oauth2-toolkit源码中追踪InvalidClientError,发现在以下文件的高亮sn-p中引发了异常: oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py

if self.request_validator.client_authentication_required(request):
    log.debug('Authenticating client, %r.', request)
    print(request) # I included this print message to inspect the request variable in console.
    if not self.request_validator.authenticate_client(request):
        log.debug('Client authentication failed, %r.', request)
        raise errors.InvalidClientError(request=request) # RAISED!

我添加了 print(request) 行来检查 curl 和 Alamofire 发出的请求之间的差异。主要区别在于 curl 版本包含一个授权密钥:

'Authorization': 'Basic Z3ZFSjVXejloUGgybUJmdDNRaGhXZnlhNHpETG5KY3V6djJldWMwcjpSbVNPMkpwRFQ4bHp1UVFDYXN3T3dvVFkzRTBia01YWWxHVHNMcG5JUGZCUHFjbHJSZE5EOXQzd3RCS2xwR09MNWs1bEE4S2hmRUkydEhvWmx3ZVRKZkFXUDM4OERZa1NTZ0RvS0p3WjUyejRSQ29WRkZBS01RS1lydEpsTWNXag=='

Alamofire 请求没有。

我高度怀疑这是罪魁祸首,但从现在开始我真的不知道还能做什么。我真的很感激任何智慧。

【问题讨论】:

【参考方案1】:

找到答案了!

正在阅读有关 HTTP 协议的 RFC 文档时,这部分引起了我的注意。

https://www.rfc-editor.org/rfc/rfc1945#section-11.1

具体来说,

为了接收授权,客户端发送用户 ID 和密码, 由单个冒号 (":") 字符分隔,在 base64 [5] 内 凭据中的编码字符串。

Alamofire 似乎没有像预期的那样将 clientId 和 clientSecret 编码为 64 位。显然,curl 会自动执行此操作。所以我做了以下事情:

第一次编码:

 static let clientData: NSData = "\(clientId):\(clientSecret)".dataUsingEncoding(NSUTF8StringEncoding)!
 static let client64String = clientData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.init(rawValue: 0))

然后使用结果值设置请求头:

let request = "http://localhost:8000/o/token/"
var URLRequest = NSMutableURLRequest(URL: NSURL(string: request)!)
URLRequest.HTTPMethod = "POST"

let parameters = ["grant_type": "password",
                  "username": in_username.text!,
                  "password": in_password.text!,
                  ]

let encoding = Alamofire.ParameterEncoding.URL
(URLRequest, _) = encoding.encode(URLRequest, parameters: parameters)

// SOLUTION!
URLRequest.setValue("Basic \(Authentication.client64String)", forHTTPHeaderField: "Authorization")

Alamofire.request(URLRequest)
    .responseJSON  response in
        let data = response
        print(data)
    

然后我收到了预期的令牌作为响应。

【讨论】:

以上是关于Swift Alamofire 上的 Django oauth2 令牌请求失败的主要内容,如果未能解决你的问题,请参考以下文章

swift上的Alamofire返回状态代码问题

JSON字符串上的Unicode字符与swift 3和Alamofire

使用 django API 和 Alamofire 请求失败

将可编码结构编码为 Alamofire POST 参数 - Swift

Alamofire 的 Swift 扩展方法返回 SwiftyJSON 结果

Swift 3 中的 Alamofire?