概述
OAuth即开放授权协议,其提供了一个安全、可靠的框架供第三方应用在一定授权和限制下访问HTTP服务。智慧山师使用OAuth版本为OAuth 1.0a及xAuth,若需要使用我们的OAuth接口,请联系我们获取Consumer Key以及Consumer Secret,其中由于安全性的问题,目前向全校师生开放的仅为OAuth 1.0a接口。
OAuth按使用情况分为服务提供者(Service Provider)和服务使用者(Consumer),其中智慧山师开放平台即为服务提供者,使用智慧山师公共平台的应用均为服务使用者。
使用流程
OAuth 1.0a的授权流程非常简单,总体分为三个步骤:
- 服务使用者(应用)向智慧山师请求未授权的Request Token
- 服务使用者向智慧山师请求用户身份授权,智慧山师引导用户访问智慧山师授权页面(仅第一次需要),经用户同意后Request Token变为已授权
- 服务使用者使用已授权的Request Token向智慧山师换取Access Token
当第三方应用获取到Access Token后就可以使用其获取指定权限的用户信息了。
全过程流程如下:
![OAuth授权流程](img/oauth_process.png)
授权流程
若要使用OAuth接口,首先您需要申请获取Consumer Key和Consumer Secret,其中Consumer Secret是您应用的密钥,在请求过程中需要使用其进行加密,所以请妥善保管。
当用户第一次使用您的应用时(或Access Token过期时),您需要首先获取用户的Access Token,在获取到用户的Access Token后,您才可以根据您的权限获取用户的信息。
获取Request Token
获取Request Token是OAuth 1.0a协议用户认证的第一步,首先您需要准备好以下的参数:
参数名 | 参数说明 |
---|---|
oauth_callback | 第二步用户授权时的回调地址 |
oauth_consumer_key | Consumer Key |
oauth_nonce | 单次值,最长32位字符串 |
oauth_signature | 签名 |
oauth_signature_method | 签名方法,固定为“HMAC-SHA1” |
oauth_timestamp | 时间戳 |
oauth_version | 协议版本,固定为“1.0” |
对于桌面或手机App这类没有服务器的应用来说,也可以使用智慧山师默认的Callback地址http://i.sdnu.edu.cn/oauth/index.html,使用内置浏览器访问授权页后监视页面的跳转即可。
为了方便大家阅读,约定说明文档中所有参数均统一设置为以下内容:
参数名 | 示例内容 |
---|---|
oauth_consumer_key | test_consumer_key |
oauth_consumer_secret | test_consumer_secret |
oauth_nonce | 00000000000000000000000000000000 |
oauth_timestamp | 9999999999 |
oauth_callback | http://fakeurl.com/callback?from=isdnu |
x_auth_username | 2013001001 |
x_auth_password | 123456 |
使用上述参数生成BaseString,需要注意的是callback中的url虽然也包含参数from=isdnu,但该参数属于callback地址的一部分,并非OAuth的认证参数,不需要单独提取出来。生成的BaseString如下:
GET&http%3A%2F%2Fi.sdnu.edu.cn%2Foauth%2Frequest_token&oauth_callback%3Dhttp%253A%252F%252Ffakeurl.com%252Fcallback%253Ffrom%253Disdnu%26oauth_consumer_key%3Dtest_consumer_key%26oauth_nonce%3D00000000000000000000000000000000%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D9999999999%26oauth_version%3D1.0
此时由于还未获取到任何Token,所以此时的密钥只包含oauth_consumer_secret及“&”连字符,即如下所示:
test_consumer_secret&
所以此时生成的签名为:
pXIKYBEoaeAREuOgRAJQpdqAP+A=
接下来就可以向http://i.sdnu.edu.cn/oauth/request_token请求,请求时附带的Authorization Header如下:
OAuth oauth_callback="http%3A%2F%2Ffakeurl.com%2Fcallback%3Ffrom%3Disdnu",oauth_consumer_key="test_consumer_key",oauth_nonce="00000000000000000000000000000000",oauth_signature="pXIKYBEoaeAREuOgRAJQpdqAP%2BA%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="9999999999",oauth_version="1.0"
不论请求是否成功,都会返回文本字符串,返回的ContentType为“text/plain”。其中请求成功后,服务器会返回如下未授权的Request Token ID、Request Token对应的密钥以及设定Callback的确认信息,其中这三个字段分别使用“&”连接,例如返回的内容为:
oauth_token=11111111111111111111111111111111&oauth_token_secret=2222222222222222222222222222222222222222&oauth_callback_confirmed=true
此时请将Request Token ID及对应的密钥记录下来,在第二步中将使用。
如果请求失败则会返回如下内容:
error_code=10006&error_type=auth_error&error_description=signature+is+invalid
其中返回的内容均使用“&”符号进行分割,分割后可以分别获取错误代码、错误类型以及错误描述,其中错误描述使用UrlEncode进行处理。
请求用户授权
这一步使用刚才获取到的Request Token请求服务器以引导到授权页面并获得用户的授权。通常,Web应用程序可以跳转到授权页面,当用户授权成功后再跳转到第一步设置的Callback地址然后完成接下来的操作;而桌面或手机App则需要使用应用内置浏览器引导用户访问智慧山师授权页面,然后监视浏览器页面的跳转。
智慧山师的用户授权地址为:http://i.sdnu.edu.cn/oauth/authorize,访问时需要将上一步获取的Request Token ID附带在oauth_token参数中。如果用户是第一次授权,则会出现一个页面,用户可以在该页面上使用智慧山师账户登录并同意或拒绝授权;如果用户之前已经授权过,则会直接跳转到上一步设置的Callback地址。默认该页面会读取当前已经登录的用户状态,如果需要让用户强制登录,在调用接口时需要附加额外的参数forcelogin=true,默认不填写此参数相当于forcelogin=false。
例如,通过上一步获取到的Request Token,这一步应该访问的地址为:
http://i.sdnu.edu.cn/oauth/authorize?oauth_token=11111111111111111111111111111111
如果用户成功授权,则会带着参数跳转到第一步设置的Callback地址:
http://fakeurl.com/callback?from=isdnu#oauth_token=11111111111111111111111111111111&oauth_verifier=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
其中oauth_token与之前请求的Request Token完全相同,此时请将oauth_verfier验证码记录下来,在第三步中将使用。
换取Access Token
用户完成授权后,可以使用之前的Request Token换取正式的Access Token,首先需要准备以下的参数:
参数名 | 参数说明 |
---|---|
oauth_consumer_key | Consumer Key |
oauth_nonce | 单次值,最长32位字符串 |
oauth_signature | 签名 |
oauth_signature_method | 签名方法,固定为“HMAC-SHA1” |
oauth_timestamp | 时间戳 |
oauth_token | 第一步获得的Request Token ID |
oauth_verifier | 第二步获得的验证码 |
oauth_version | 协议版本,固定为“1.0” |
此时生成的BaseString如下:
GET&http%3A%2F%2Fi.sdnu.edu.cn%2Foauth%2Faccess_token&oauth_consumer_key%3Dtest_consumer_key%26oauth_nonce%3D00000000000000000000000000000000%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D9999999999%26oauth_token%3D11111111111111111111111111111111%26oauth_verifier%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%26oauth_version%3D1.0
由于此时已经有了Request Token,所以此时加密的密钥为:
test_consumer_secret&2222222222222222222222222222222222222222
用该加密密钥对上述的BaseString进行加密,获取到的签名如下:
fUlh49IUIjFKxpR+lSODB3HpYAo=
然后就可以向http://i.sdnu.edu.cn/oauth/access_token请求,请求时附带的Authorization Header如下:
OAuth oauth_consumer_key="test_consumer_key",oauth_nonce="00000000000000000000000000000000",oauth_signature="fUlh49IUIjFKxpR%2BlSODB3HpYAo%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="9999999999",oauth_token="11111111111111111111111111111111",oauth_verifier="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",oauth_version="1.0"
如果请求成功,则服务器就会返回Access Token ID、Access Token对应的密钥以及用户学工号、用户类型以及Access Token的超时时间:
oauth_token=33333333333333333333333333333333&oauth_token_secret=4444444444444444444444444444444444444444&user_id=2013001001&user_type=1&expires_in=604800
其中用户类型分别为0-本科生,1-研究生,2-教职工。返回内容类型与request_token接口一样为文本字符串,失败时返回类型与request_token相同。
信息获取
在获取了Access Token以后,就可以使用信息接口根据权限获取所需要的信息了。其中公共权限接口可以不传oauth_token参数,即不进行OAuth认证。
若要获取信息,首先需要准备以下的参数:
参数名 | 参数说明 |
---|---|
oauth_consumer_key | Consumer Key |
oauth_nonce | 单次值,最长32位字符串 |
oauth_signature | 签名 |
oauth_signature_method | 签名方法,固定为“HMAC-SHA1” |
oauth_timestamp | 时间戳 |
oauth_token | Access Token ID |
oauth_version | 协议版本,固定为“1.0” |
除了OAuth固定的参数外,对于部分服务还需要添加其特定的参数。但这些参数不属于OAuth特定的参数,不需要放在HTTP请求头中,可以放在QueryString或Form中,不过不论放在QueryString还是Form中都需要在生成BaseString时包含在参数集合中。
例如使用GET方法获取用户信息,则请求的地址为http://i.sdnu.edu.cn/oauth/rest/people/get,所以BaseString为:
GET&http%3A%2F%2Fi.sdnu.edu.cn%2Foauth%2Frest%2Fpeople%2Fget&oauth_consumer_key%3Dtest_consumer_key%26oauth_nonce%3D00000000000000000000000000000000%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D9999999999%26oauth_token%3D00000000000000000000000000000000%26oauth_version%3D1.0
此时已经有了Access Code,所以当前密钥应该如下:
test_consumer_secret&4444444444444444444444444444444444444444
使用上述密钥加密后的签名为:
uaaIvtZusfri9s1nixxSBcWb/O0=
所以最终请求时的Authorization Header为:
OAuth oauth_consumer_key="test_consumer_key",oauth_nonce="00000000000000000000000000000000",oauth_signature="uaaIvtZusfri9s1nixxSBcWb%2FO0%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="9999999999",oauth_token="00000000000000000000000000000000",oauth_version="1.0"
不论成功还是失败,均返回Json文本,返回的ContentType为“application/json”。对于成功后返回的信息为实体直接序列化后的Json文本,例如:
{ "identityNumber":"2013001001", "name":"张三", "idCardNumber":"", "sex":"男", "nation":"汉族", "organizationID":"010310", "organizationName":"信息科学与工程学院" }
而若请求失败,则返回错误Json信息,返回的内容与授权接口的内容相同,但表现形式不同,例如:
{ "errorCode":10006, "errorType":"auth_error", "errorDescription":"signature is invalid" }
创建签名
所有使用OAuth接口的请求都需要生成签名,签名生成的流程为:生成签名字符基串(Signature Base String)及使用密钥加密生成密钥两步。
生成BaseString
Base String是把HTTP请求方法名、请求完整URL地址以及请求参数用“&”字符连起来后做RFC3986 Encode编码(类似于各个平台自带的UrlEncode,但是对空格替换为“%20”,而不是“+”)。其中请求参数需要把所有的请求参数(包括POST方法体中的参数),经过排序(按参数名进行文本排序,如果参数名有重复则再按参数值进行重复项目排序),使用%3D替代=号,并且使用%26作为每个参数之间的分隔符,拼接成一个字符串。
这个算法可以简单表示为:
httpMethod + "&" + url_encode( base_uri ) + "&" + sorted_query_params.each { | k, v | url_encode ( k ) + "%3D" + url_encode ( v ) }.join("%26")
例如,使用GET方式请求获取Request Token:http://i.sdnu.edu.cn/oauth/request_token
请求参数为:
oauth_callback=http://fakeurl.com/callback?from=isdnu oauth_consumer_key=test_consumer_key oauth_nonce=00000000000000000000000000000000 oauth_signature_method=HMAC-SHA1 oauth_timestamp=9999999999 oauth_version=1.0
则最终的BaseString为:
GET&http%3A%2F%2Fi.sdnu.edu.cn%2Foauth%2Frequest_token&oauth_callback%3Dhttp%3A%2F%2Ffakeurl.com%2Fcallback%3Ffrom%3Disdnu%26oauth_consumer_key%3Dtest_consumer_key%26oauth_nonce%3D00000000000000000000000000000000%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D9999999999%26oauth_version%3D1.0
加密生成签名
智慧山师的签名统一使用HMAC-SHA1算法进行加密,加密的密钥如下:
oauth_consumer_secret + "&" + oauth_token_secret
需要注意的是,其中的oauth_token_secret始终与当前使用的令牌密钥相同,如果当前未使用密钥则留空,但“&”符号应需要保留。即当使用Request Token换取Access Token的过程时使用的oauth_token_secret为Request Token的密钥;而当获取用户个人信息则使用Access Token的密钥。
对于请求Request Token的过程,由于此时并未获取任何Token,所以此时的密钥为如下:
test_consumer_secret&
使用该密钥加密后则可以获得加密后最终的签名
VDfVbXtO+moqLuqL7MzqRs4Hnc4=
创建HTTP请求头
为了保障安全性,智慧山师开放平台要求所有的OAuth参数都必须在Http Header中进行传输,即将所有参数按键值对的方式放在HTTP的Authorization请求头中(建议按顺序排列),其算法如下:
"OAuth " + sorted_header_params.each { | k, v | url_encode ( k ) + "=\"" + url_encode ( v ) + "\"" }.join(",")
例如使用上一节的参数和创建的签名则可以获取如下的请求头:
OAuth oauth_callback="http%3A%2F%2Ffakeurl.com%2Fcallback%3Ffrom%3Disdnu",oauth_consumer_key="test_consumer_key",oauth_nonce="00000000000000000000000000000000",oauth_signature="VDfVbXtO%2BmoqLuqL7MzqRs4Hnc4%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="9999999999",oauth_version="1.0"
其他参数说明
时间戳 Timestamp
时间戳使用Unix时间,即当前时间转换为UTC时间后,减去1970年1月1日0时0分0秒后的所有秒数。当客户端时间与服务器时间相差超过8分钟时无效,认证失败。
例如,当前时间为2014/1/29 16:56:58(UTC+8),其UTC时间为2014/1/29 8:56:58,减去Unix起始时间为16099天8时56分58秒,共计1390985818秒,故时间戳为1390985818。
单次值 Nonce
唯一值为每次请求时不重复的字符串,防止重放攻击,最长为32个字符。如使用GUID生成唯一值时,可以以十六进制小写字符串表示。
例如,在.NET中使用Guid.NewGuid()生成的Guid为FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF。所以最终提交的Nonce为ffffffffffffffffffffffffffffffff。
错误代码
错误类型 | 错误代码 | 错误描述 | 适用范围 |
---|---|---|---|
auth_error | 10001 | protocol version not supported |
令牌请求接口 request_token 令牌授权接口 access_token 服务提供接口 rest |
10002 | timestamp invalid | ||
10003 | nonce invalid | ||
10004 | nonce repeated | ||
10005 | signature method not supported | ||
10006 | signature invalid | ||
10007 | callback url empty | ||
10008 | http method invalid | ||
10009 | duplicated parameter | ||
10101 | consumer key invalid | ||
10102 | consumer not allow user auth | ||
10103 | consumer no permission | ||
10104 | consumer not enabled | ||
10201 | consumer no xauth permission | 令牌授权接口 access_token | |
token_error | 11001 | request token owner invalid | 令牌授权接口 access_token |
11002 | request token empty | ||
11003 | request token invalid | ||
11004 | request token not authorized | ||
11005 | request token verifier empty | ||
11006 | request token verifier invalid | ||
11011 | user not authorized | ||
11101 | access token owner invalid | 服务提供接口 rest | |
11102 | access token empty | ||
11103 | access token invalid | ||
11104 | permission not authorized | ||
xauth_error | 12001 | auth mode not supported | 令牌授权接口 access_token |
12101 | username empty | ||
12102 | password empty | ||
12103 | username or password wrong | ||
12104 | user has not set an email address | ||
rest_error | 20001 | rest method invalid | 服务提供接口 rest |
29999 | 各服务的特定错误(中文) |