Day 2138 饭否oAuth验证学习笔记

饭否的oAuth认证文档在这里:https://github.com/FanfouAPI/FanFouAPIDoc/wiki/Oauth

如果采用他人已经写好的库,则基本不存在什么问题,而如果认证过程是自己写,并且之前又没有多少网络应用的编程经验,只看这个文档可能会走不少弯路。

1.xAuth的基本认证流程

xAuth是oAuth的简化版本,主要用于客户端一类本地应用程序的验证,oAuth的原生认证过程可以参考RFC 5849号文档(英文)及新浪开放平台的oAuth介绍(中文),这里不再赘述,主要介绍饭否提供的xAuth接口的认证流程:

  1. 使用access_token接口,辅以x_auth系列参数提交客户端认证,换取可用的oauth_token和oauth_token_secret。
  2. 使用获得的oauth_token和oauth_token_secret,结合申请的应用oauth_consumer_key和oauth_consumer_secret对API进行认证调用。

2.oAuth基本知识

  1. Header中的参数
    关于排列:首先对参数进行字母顺序升序排序,参数名相同的,按参数值排序,然后将排序后的参数按顺序用西文逗号(,)连接起来,作为Header中Authorization段放在“OAuth ”(注意有一个空格)段后的内容。对于饭否的oAuth,须注意:xAuth调用时对realm以及参数值是否有引号不敏感,但通过oAuth进行身份验证调用其它API时,要注意一不能有realm参数,二是参数值不能有引号,如果疏忽了这些问题,服务器会返回Invalid Signature的hash-error结构体,按返回的内容调试即可。
  2. 签名
    签名是为了保证数据未经篡改。不同的站点可能支持不同的签名方式,饭否只支持HMAC-SHA1方式的签名。饭否的xAuth阶段签名不需要使用Key(理论上这样是不对的,以后可能会修改),而在通过oAuth认证调用API功能时,则需要使用Key,Key应为consumer_secret和oauth_token_secret的连接结果,用“&”连接,然后附加到Header中OAuth Authorization的参数中。
  3. Base String
    Base String是计算签名的基础,oAuth规范要求的Base String组合方式在下文有介绍。

3.oAuth过程中需要注意的问题

  1. 关于oAuth API的使用
    如果按照oAuth文档中的指示,仅对那些参数附在Header中进行提交,很可能永远也无法通过认证,为何?RFC5849中已有明确提示(第3.5.1条):必须制定Authorization的Scheme为OAuth(大小写不能错),那什么是Authorization Scheme?其实就是告诉服务器认证的类型,也就是说,提交的Header中,Authorization段必须符合以下格式:

    Authorization: OAuth //后面是oAuth的其它参数

    如果这一步忘记了,服务器只会一个劲的返回Invalid Consumer Key,容易使人在Consumer Key的代入值上百思不得其解,其实只是因为服务器不知道这是oAuth认证,从而根本没有去读后面的参数,等于未将Consumer Key传给服务器,自然就会报Invalid Consumer Key了。

  2. 关于Base String的生成
    Base String的基本格式如下,其中&不是为了好看,而是必须存在的连接符号:
    HTTP请求方法(GET或POST,必须为大写)&URL Encode后的目标URL&排序并用西文AND符号(&)连接,然后整体做URLEncode后的参数
    这里许多通不过验证的人主要的问题可能是URL Encode后的结果与服务器期望值不符,原因也很简单:可能是所用的编程语言生成的URL Encode结果不标准。RFC中规定的编码需要满足以下两个关键条件:

    1、所有编码文字(%xx)中,xx的字母必须为大写(例如:%AA可以通过,而%aa就不行)
    2、以下字符绝对不能被编码:数字、字母(大写和小写)、-(短横线,或称西文减号)、.(西文小数点)、_(西文下划线)、~(西文波浪号),而除此之外的所有字符必须参加编码。

    鉴于此,如果所用的语言生成的结果不符合以上两条,请自行编写函数进行编码工作。

    • 关于签名(oauth_signature)
      以下是饭否在API文档中提供的签名规则,请完全照做:
      1、所有OAuth请求中,所有oauth_(含xauth_)开头的参数都要参加签名。
      2、在GET请求中,QueryString的所有参数也要参与签名,如:

      GET http://api.fanfou.com/statuses/user_timeline.xml?id=fanfou&page=2&format=html

      则id=fanfou、page=2、format=html也要参加签名。

      3、在POST请求中,只有当Content-Type为application/x-www-form-urlencoded时,
      所有参数才都全部要参加签名,如(POST数据没有经过编码):

      POST http://api.fanfou.com/account/update_profile.xml(更新用户信息)
      数据:url=http://www.aoisnow.net&location=山东 济南&description=测试上传用户信息&name=霞羽&[email protected]

      则数据中的url、location、description、name、email都要参加签名,且Content-Type需指定。

      4、在POST请求中,除了application/x-www-form-urlencoded的Content-Type,其它POST请求的附加
      参数都不参与签名,包括Content-Type为multipart/form-data时也不需要。例如(数据没有编码):

      POST http://api.fanfou.com/photos/upload.xml
      数据:photo=(照片的二进制数据)&status=测试&source=test

      则数据中的photo、status、source都不参加签名

      5、对于POST请求,经实践证明Content-Type默认为application/x-www-form-urlencoded
      在HTTP协议规定中这是默认值,但在提交给饭否的POST请求中,Content-Type必须指定为此
      否则会出现提交参数正确的情况下返回响应代码为200的空响应,切记。

注:所有评论将在审核通过后显示,请不要在评论内容的任何位置出现链接,否则您的评论将被自动移入回收站,且永远不会被复审。

All comments will be available after being manually reviewed, please do not include any links anywhere in your comment, otherwise your comment will be automatically deleted and are not eligible for review.

9 条评论

  1. 关于Base String的生成
    Base String的基本格式如下,其中&不是为了好看,而是必须存在的连接符号:
    HTTP请求方法(GET或POST,必须为大写)&URL Encode后的目标URL&排序并用西文逗号(,)连接,然后整体做URLEncode后的参数

    这里逗号是不是改为&了?

  2. 博主,看到你的回复真好。不知道有无时间再看看这个
    所以求教,本人刚开始学,在获取未授权的Request Token就卡住了。

    http://fanfou.com 请求用户名和密码。信息为: “password error”

    点取消后出现如下提示

    XML解析错误:未组织好
    位置:http://fanfou.com/oauth/request_token?oauth_consumer_key=3159c9aa1a54b8734d3da88ee60a60ef&oauth_nonce=2659776296&oauth_signature=cDjegef64pHFH9d+7zJsjM2d674=&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1329888148
    行:3,列:94:

    我的疑问,
    oauth_nonce=2659776296,是不是位数不够?要32位?
    这一步HMAC-SHA1加密时的secret后面要不要加&(还没拿到oauth_token_secret)

    这个是我的写法。。
    https://dl.dropbox.com/u/48168238/3.html

    1. 用您提供的链接时而可以成功获取Token,时而不行。
      Nonce在RFC中对长度没有限制,但必须在请求过程中唯一(The nonce value MUST be unique across all requests with the same timestamp, client credentials, and token combinations),所以应该不是这个的问题。
      出错的时候服务器的返回是/hash/error结构,其中有对Expected BaseString的说明,您可以参考返回对提交进行修正。

  3. 万分感谢,调试了一个晚上一直Invalid Consumer Key,加上Content-Type之后终于可以了,再次谢谢^_^

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注