OAuth是一個開放協議,允許用戶讓第三方應用以安全且標准的方式獲取該用戶在某一網站上存儲的私密資源(如用戶個人信息、照片、視頻、聯系人列表),而無須將用戶名和密碼提供給第三方應用。本文將詳細介紹OAuth協議以及在微信里的具體實現。
OAuth2.0協議介紹
OAuth2.0是OAuth協議的下一版本,但不向后兼容OAuth 1.0。 OAuth 2.0關注客戶端開發者的簡易性,同時為Web應用,桌面應用和手機,和起居室設備提供專門的認證流程。 OAuth2.0允許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據。每一個令牌授權一個特定的網站(例如,視頻編輯網站)在特定的時段(例如,接下來的2小時內)內訪問特定的資源(例如僅僅是某一相冊中的視頻)。這樣,OAuth允許用戶授權第三方網站訪問他們存儲在另外的服務提供者上的信息,而不需要分享他們的訪問許可或他們數據的所有內容。
OAuth2.0認證和授權的具體過程:
在Oauth2.0認證和授權的過程中涉及的三方包括:
1. 服務提供方,用戶使用服務提供方來存儲受保護的資源,如照片,視頻,聯系人列表。
2. 用戶,存放在服務提供方的受保護的資源的擁有者。
3. 客戶端,要訪問服務提供方資源的第三方應用,通常是網站,如提供照片打印服務的網站。在認證過程之前,客戶端要向服務提供者申請客戶端標識。
使用OAuth進行認證和授權的過程如下所示:
1. 用戶訪問客戶端的網站,想操作用戶存放在服務提供方的資源;
2. 客戶端向服務提供方請求一個臨時令牌;
3. 服務提供方驗證客戶端的身份后,授予一個臨時令牌;
4. 客戶端獲得臨時令牌后,將用戶引導至服務提供方的授權頁面請求用戶授權。在這個過程中將臨時令牌和客戶端的回調連接發送給服務提供方;
5. 用戶在服務提供方的網頁上輸入用戶名和密碼,然后授權該客戶端訪問所請求的資源;
6. 授權成功后,服務提供方引導用戶返回客戶端的網頁;
7. 客戶端根據臨時令牌從服務提供方那里獲取訪問令牌;
8. 服務提供方根據臨時令牌和用戶的授權情況授予客戶端訪問令牌;
9. 客戶端使用獲取的訪問令牌訪問存放在服務提供方上的受保護的資源。
微信網頁OAuth2.0授權:
如果用戶在微信中(Web微信除外)訪問公眾號的第三方網頁,公眾號開發者可以通過此接口獲取當前用戶基本信息(包括昵稱、性別、城市、國家)。利用用戶信息,可以實現體驗優化、用戶來源統計、帳號綁定、用戶身份鑒權等功能。
需要注意的是,獲取用戶基本信息接口(稍后博文會介紹到)是在用戶和公眾號產生消息交互時,才能根據用戶OpenID獲取用戶基本信息,而網頁授權的方式獲取用戶基本信息,則無需消息交互,只是用戶進入到公眾號的網頁,就可彈出請求用戶授權的界面,用戶授權后,就可獲得其基本信息(此過程甚至不需要用戶已經關注公眾號。)
下面我們將通過一個具體的例子來展示開發的詳細過程。
配置授權回調域名:
在微信公眾號請求用戶網頁授權之前,開發者需要先到公眾平台網站的我的服務頁中配置授權回調名,需要注意的是這里的域名不要加http://或者https://。另外,授權回調域名配置規范為全域名,比如需要網頁授權的域名為:www.qq.com,配置以后此域名下的所有頁面例如http://www.qq.com/music.html, http://www.qq.com/login.html都可以進行OAuth2.0鑒權。但http://pay.qq.com, http://music.qq.com無法進行OAuth2.0鑒權。
為此進入到服務頁(使用正式的服務號或認證后的訂閱號后通過我的服務找到,如果是測試賬號直接在首頁即可找到)后找到OAuth2.0網頁授權,點擊右側的修改鏈接:
在彈出窗口里輸入域名后點擊確定按鈕保存:
用戶同意授權,獲取Code:
此步驟相當於前面介紹到的OAuth2.0認證過程的第二步“客戶端向服務提供方請求一個臨時令牌”,這里的Code即是臨時令牌,為此可以請求微信的OAuth2.0接口以獲取該Code,該接口的參數里需要指定一個回調頁面URL,為此我們需要創建一個Apex Page,為此登陸Force.com后找到域名,通常從中國訪問的域名是https://ap1.salesforce.com, 在瀏覽器地址欄中輸入https://ap1.salesforce.com/apex/oauth2test, 此時Force.com將提示頁面不存在,點擊“Create Page oauth2test”鏈接創建頁面:
創建好后的頁面如下,如果啟用了開發者模式,則頁面分為上下兩個部分,上部分是顯示效果,下面是源代碼編輯窗口,默認Force.com會將自身的頂部導航、左側導航以及CSS樣式應用到新創建的頁面:
我們通過在第一行里加入以下代碼告訴Force.com不要使用默認的CSS樣式,不要顯示頂部以及左側的導航欄,同時最后一個controller屬性指定了apex頁面對應的控制器類,類似於aspx頁面對應的aspx.cs類,鼠標保持在編輯欄窗口,按住Ctrl + S組合鍵即可保存編輯后的代碼:
<apex:page standardstylesheets="false" showHeader="false" sidebar="false" controller="oauth2testcontroller">
此時,應為對應的oauth2testcontroller類並不存在,瀏覽器會報以下錯誤:
這里點擊第二個鏈接“Create Apex class ‘public class oauth2testcontroller’”自動創建控制器類。留意這兩個鏈接的唯一區別在於”with sharing”關鍵字,這個關鍵字指定了當前類對各個對對象(相當於數據表)、字段等的訪問權限與當前登錄用戶同,如果不指定,Apex頁面將擁有對所有對象、字段的訪問權限。創建好后在下方代碼編輯欄中將多出一個控制器類Tab:
在該類中添加代碼如下:
1 public class oauth2testcontroller { 2 public String code {get; set;} 3 public oauth2testcontroller(){ 4 code = ApexPages.currentPage().getParameters().get('code'); 5 if(String.isBlank(code)){ 6 code = 'No Code'; 7 } 8 } 9 }
這段代碼中第2行定義了一個公開屬性code,第4行通過ApexPages對象獲得URL中的code參數,並接着判斷是否code值是否為空,如果為空則提示No Code。下面我們會看到微信授權成功回調此URL時會將code參數添加到URL中。
接下來略微修改前台頁面,在頁面中顯示得到的code值:
1 <apex:page standardstylesheets="false" showHeader="false" sidebar="false" controller="oauth2testcontroller"> 2 {!code} 3 </apex:page>
{!對象名}是Force.com Visualforce page里用來顯示對象值的語法,接下來我們需要配置該頁面能夠通過公網進行訪問,為此登陸Force.com后,進入Setup –> Develop –> Sites,點擊站點對應的Site Label標簽如下圖:
進入詳細配置頁面后找到“Site Visualforce Page”,點擊右側的Edit按鈕:
找到左側列表中的oauth2test頁面添加到右側,並保存修改:
此時即可通過http://johnson0001-developer-edition.ap1.force.com/oauth2test公網地址來訪問前面創造的頁面了。接下來我們可以利用微信平台的OAuth2認證接口組拼URL並引導用戶通過微信來訪問,該接口的格式如下:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
若提示“該鏈接無法訪問”,請檢查參數是否填寫錯誤,是否擁有scope參數對應的授權作用域權限,其中每個參數的詳細說明如下:
在我們的例子里URL如下,其中scope我們指定為snsapi_userinfo,彈出授權頁面:
興許是測試賬號的關系,雖然微信接口文檔里提到在制定scope為snsapi_userinfo的情況下會彈出如下圖左所示的授權頁面,但反復嘗試(乃至刪除並重新關注賬號)中也沒有看到該頁面,不過重點是我們得到了臨時令牌,如下圖右所示。右圖實際是http://johnson0001-developer-edition.ap1.force.com/oauth2test頁面,用戶同意授權后跳轉到(或者我遇到的實際情況是直接跳轉)到redirect_uri/?CODE&state=STATE。若用戶禁止授權,則重定向后不會帶上code參數,僅會帶上state參數redirect_uri?state=STATE。
另外特別需要說明的是,code作為換取access_token的臨時票據,每次用戶授權帶上的code都不一樣,code只能使用一次,5分鍾未被使用自動過期。
通過Code換取網頁授權access_token:
首先請注意,這里通過code換取的網頁授權access_token,與基礎支持中的access_token不同。公眾號可通過下述接口來獲取網頁授權access_token。如果網頁授權的作用域為snsapi_base,則本步驟中獲取到網頁授權access_token的同時,也獲取到了 openid,snsapi_base式的網頁授權流程即到此為止。 獲取code后,可以通過以下接口獲取access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
這里的CODE即為通過前面方式獲得的臨時令牌(票據),參數的具體說明如下:
將URL直接輸入到瀏覽器地址欄即可得到返回數據,當然真實場景里更多通過后台代碼來請求,正確返回時的JSON數據包如下:
1 { 2 "access_token":"ACCESS_TOKEN", 3 "expires_in":7200, 4 "refresh_token":"REFRESH_TOKEN", 5 "openid":"OPENID", 6 "scope":"SCOPE" 7 }
參數的具體說明如下:
錯誤時微信會返回JSON數據包如下(示例為Code無效錯誤):
{"errcode":40029,"errmsg":"invalid code"}
在本例中獲得的access_token實例如下:
{"access_token":"OezXcEiiBSKSxW0eoylIeMEUA_AZuBDY8AO0IIw270MMsvemqLvgx1HqemeXIZfzXW2d6yHCPy9cA1yHZ1jHCkwlH5Ct5Jfa1jOQm88M9LzU_O8BCKMNhN7yLlHJfOFLuf4lLTNGOOsoWYxQzYVNGw","expires_in":7200,"refresh_token":"OezXcEiiBSKSxW0eoylIeMEUA_AZuBDY8AO0IIw270MMsvemqLvgx1HqemeXIZfz_Vj5pJZlv2V5wK9EzWmxQmM07cqIAwMXOdqzlQs-NY4hiyENP4WhO4Twpko-3iY_pAPZRnGGmAVt3DirZaWIyg","openid":"ou-37t936RNZEcW0mI75RN2pdxkc","scope":"snsapi_userinfo"}
可以看到上面access_token的默認失效時間是7200秒,也就是2小時,當access_token超時后,可以通過refresh_token進行刷新,refresh_token擁有較長的有效期(7天、30天、60天、90天),當refresh_token失效后,需要用戶重新授權,簡化理解起見,我們在本文的最后再介紹相關技術。
拉取用戶信息(需Scope為snasapi_userinfo):
通過access_token獲取用戶信息的接口如下,使用GET方法:
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
參數具體說明如下:
本例的URL如下:
輸入瀏覽器訪問即可得到相應的用戶信息:
1 {"openid":"ou-37t936RNZEcW0mI75RN2pdxkc","nickname":"王浩","sex":1,"language":"zh_CN","city":"松江","province":"上海","country":"中國","headimgurl":"http:\/\/wx.qlogo.cn\/mmopen\/lqsZNvDqcXe8nBKHBPsp9YHuZXPtkzOD1uq3r3xxDicuDLKGlicNd1b371ODnn9xNBB9y9ChBSfL7tuX6m9FS8koY9Ex1iaJRDI\/0","privilege":[]}
刷新access_token:
可以通過前面在“通過Code換取網頁授權access_token”小節中獲得的refresh_token來調用刷新Token接口獲取更新的access_token,微信在API文檔里介紹refresh_token擁有較長的有效期(7天、30天、60天、90天),但實際微信的refresh_token的有效期是多長沒有具體說明,如果有具體經驗的朋友歡迎分享。微信刷新access_token的接口如下:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
接口的具體參數定義如下:
正確時返回的JSON數據包如下:
1 { 2 "access_token":"ACCESS_TOKEN", 3 "expires_in":7200, 4 "refresh_token":"REFRESH_TOKEN", 5 "openid":"OPENID", 6 "scope":"SCOPE" 7 }
數據包的具體定義如下:
錯誤時微信會返回JSON數據包如下(示例為Code無效錯誤):
{"errcode":40029,"errmsg":"invalid code"}