理解OAuth2.0
首先我們透過一張圖片來了解OAuth2.0的運作模式:
從上圖我們可以看到,整個過程進行了2次“握手”,最終利用授權的AccessToken進行一系列的請求,相關的過程說明如下:
A:由客戶端向伺服器發出驗證請求,請求中一般會攜帶這些參數
ID標識,例如appId
驗證後跳到的URL(redirectUrl)
狀態參數(可選)
##授權作用網域scope(可選)回應類型(可選)B:伺服器傳回一個grant授權標識(微信預設稱為code),類似於一次性的臨時字串密鑰。如果在A中提供了redirectUrl,這裡伺服器會做一次跳轉,帶著grant和狀態參數,存取redirectUrl。 C:客戶端的redirectUrl對應頁面,憑藉grant再次發起請求,這次請求通常會攜帶一些敏感資訊:ID識別#covergrant字串(code)grant類型(可選,微信中預設為code)#D:伺服器驗證ID識別、密碼、grant都正確之後,返回AccessToken(注意,這裡的AccessToken和之前通用介面、高級介面介紹的AccessToken沒有關係,不能交叉使用)E:客戶端憑藉AccessToken請求一系列的API,在此過程中不再會攜帶appId,Secret,grant等敏感的資訊。 F:伺服器傳回請求結果。 微信的OAuth2.0使用 了解了OAuth2.0的基本原理之後,我們來看看OAuth2.0在微信中是如何運用的。 假設一個場景:用戶進入了一個微信公眾帳號,隨後透過訊息中的鏈接,在微信內嵌瀏覽器中打開了一個遊戲網頁,這個遊戲需要用戶登錄並且記錄用戶的遊戲得分。 這種情況下我們有兩種處理方式:讓用戶在網頁中進行註冊、登入(並且每次打開這個網頁都可能要重新進行登錄,因為微信內建瀏覽器的cookie保存時間非常短),這當然是個很坑爹的設計。 利用OAuth2.0。在使用者進入這個頁面的時候,先判斷使用者是否登錄,如果沒有,自動跳到OAuth2.0授權頁面,這個頁面又自動進行了上述ABCD一系列驗證,再透過EF得到使用者的OpenId甚至更詳細的資訊(包括頭像),自動完成登入(或必要的註冊)流程,隨後使用者以登入狀態直接進入遊戲。 可以看出,使用OAuth2.0大幅提高了使用者體驗,並且可以自動綁定識別使用者微信OpenId。 需要注意的是,上面提到的「OAuth2.0授權頁面」還有兩種形式:當請求A中的Scope為snsapi_base時,整個授權過程自動完成,用戶的客戶端不會有任何中間頁面顯示,但是授權的結果僅能獲取用戶的OpenId(不管用戶是否已關注,當然如果用戶是關注用戶,再次利用高級接口中的用戶信息接口,利用OpenId獲取用戶資料也是可以的,只不過繞了幾個彎)當請求A中的Scope為snsapi_userinfo時,需要提供一個授權頁面(類似很多網站利用微博帳號、QQ號登陸的那種授權),僅當用戶同意之後,立即獲取到用戶的詳細信息,這裡的用戶可以是關注用戶,也可以是未關注用戶,返回的內容一致。 也就是說,snsapi_base的方法可以「神不知鬼不覺」地取得使用者OpenId,全自動完成登入註冊過程,但是資訊量有限;snsapi_userinfo需要使用者在指定介面上授權之後,自動完成整個過程,這個授權有一個時間段,超過時間後需要重新詢問使用者。 Senparc.Weixin.MP OAuth2.0介面 來源檔案資料夾:Senparc.Weixin.MP/AdvancedAPIs/OAuth 原始程式碼中相關方法如下:namespace Senparc.Weixin.MP.AdvancedAPIs { //官方文档:http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF#.E7.AC.AC.E4.B8.80.E6.AD.A5.EF.BC.9A.E7.94.A8.E6.88.B7.E5.90.8C.E6.84.8F.E6.8E.88.E6.9D.83.EF.BC.8C.E8.8E.B7.E5.8F.96code /// <summary> /// 应用授权作用域 /// </summary> public enum OAuthScope { /// <summary> /// 不弹出授权页面,直接跳转,只能获取用户openid /// </summary> snsapi_base, /// <summary> /// 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 /// </summary> snsapi_userinfo } public static class OAuth { /// <summary> /// 获取验证地址 /// </summary> /// <param name="appId"></param> /// <param name="redirectUrl"></param> /// <param name="state"></param> /// <param name="scope"></param> /// <param name="responseType"></param> /// <returns></returns> public static string GetAuthorizeUrl(string appId, string redirectUrl, string state, OAuthScope scope, string responseType = "code") { var url = string.Format("https://open.weixin.qq.com/connect/oauth2/authorize?appid={0}&redirect_uri={1}&response_type={2}&scope={3}&state={4}#wechat_redirect", appId, redirectUrl.UrlEncode(), responseType, scope, state); /* 这一步发送之后,客户会得到授权页面,无论同意或拒绝,都会返回redirectUrl页面。 * 如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。这里的code用于换取access_token(和通用接口的access_token不通用) * 若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE */ return url; } /// <summary> /// 获取AccessToken /// </summary> /// <param name="appId"></param> /// <param name="secret"></param> /// <param name="code">code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。</param> /// <param name="grantType"></param> /// <returns></returns> public static OAuthAccessTokenResult GetAccessToken(string appId, string secret, string code, string grantType = "authorization_code") { var url = string.Format("https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type={3}", appId, secret, code, grantType); return CommonJsonSend.Send<OAuthAccessTokenResult>(null, url, null, CommonJsonSendType.GET); } /// <summary> /// 刷新access_token(如果需要) /// </summary> /// <param name="appId"></param> /// <param name="refreshToken">填写通过access_token获取到的refresh_token参数</param> /// <param name="grantType"></param> /// <returns></returns> public static OAuthAccessTokenResult RefreshToken(string appId, string refreshToken, string grantType = "refresh_token") { var url = string.Format("https://api.weixin.qq.com/sns/oauth2/refresh_token?appid={0}&grant_type={1}&refresh_token={2}", appId, grantType, refreshToken); return CommonJsonSend.Send<OAuthAccessTokenResult>(null, url, null, CommonJsonSendType.GET); } public static OAuthUserInfo GetUserInfo(string accessToken,string openId) { var url = string.Format("https://api.weixin.qq.com/sns/userinfo?access_token={0}&openid={1}",accessToken,openId); return CommonJsonSend.Send<OAuthUserInfo>(null, url, null, CommonJsonSendType.GET); } } }
目前官方的授權頁面不是100%穩定,有時候需要多點幾次才能順利通過,如果發現此類情況,需要做一些判斷反覆請求,至少在表面上可以不讓用戶看到錯誤頁面。
出於安全,在使用OAuth2.0之前,需要進入到微信後台的【我的服務】對回呼頁面的網域進行設定:
更多微信公眾平台開發:OAuth2.0說明相關文章請關注PHP中文網!