C#微信公眾平台開發一網頁授權
在開發公眾號時經常會遇到有些功能需要用戶登錄后才能使用,我們自己編寫的程序或許有自帶登錄頁面,但用戶每次都要輸入賬號密碼很是麻煩,所以考慮能否用微信賬號授權登錄,這樣只要授權過了,除非授權過期,否則都不需要重新登錄,接下來我們來研究如何完成授權操作。
一、為了實現網頁授權功能,需要解決的問題
1、微信網頁授權是通過在微信客戶端調用授權接口,用戶同意授權后微信調用我們指定的回調函數進行后續業務操作,在這里比較特殊的是回調函數必須是在某個域名下,所以開發者需要先到公眾平台官網中的“開發 - 接口權限 - 網頁服務 - 網頁帳號 - 網頁授權獲取用戶基本信息”的配置選項中,修改授權回調域名。



2、通過開發者ID及密碼調用獲取access_token接口時,需要設置訪問來源IP(上述域名所在服務器的外網IP地址)為白名單,可在 開發 -> 基本配置 -> 公眾號開發信息 -> IP白名單 進行設置。

二、實現步驟
1 第一步:用戶同意授權,獲取code
在確保微信公眾賬號擁有授權作用域(scope參數)的權限的前提下(服務號獲得高級接口后,默認擁有scope參數中的snsapi_base和snsapi_userinfo),引導關注者打開如下頁面,可以通過微信客戶端打開或使用微信web開發者工具打開:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 若提示“該鏈接無法訪問”,請檢查參數是否填寫錯誤,是否擁有scope參數對應的授權作用域權限。
尤其注意:由於授權操作安全等級較高,所以在發起授權請求時,微信會對授權鏈接做正則強匹配校驗,如果鏈接的參數順序不對,授權頁面將無法正常訪問
參考實例(微信web開發者工具)

參數說明
| 參數 | 是否必須 | 說明 |
|---|---|---|
| appid | 是 | 公眾號的唯一標識(可以點開 開發-> 基本配置 查看) |
| redirect_uri | 是 | 授權后重定向的回調鏈接地址, 請使用 urlEncode 對鏈接進行處理(前面已經說過,我們的回調函數需部署到域名下,這里的地址就回調函數的地址) |
| response_type | 是 | 返回類型,請填寫code |
| scope | 是 | 應用授權作用域,snsapi_base (不彈出授權頁面,直接跳轉,只能獲取用戶openid),snsapi_userinfo (彈出授權頁面,可通過openid拿到昵稱、性別、所在地。並且, 即使在未關注的情況下,只要用戶授權,也能獲取其信息 ) |
| state | 否 | 重定向后會帶上state參數,開發者可以填寫a-zA-Z0-9的參數值,最多128字節 |
| #wechat_redirect | 是 | 無論直接打開還是做頁面302重定向時候,必須帶此參數 |
2 第二步:通過code換取網頁授權access_token
HttpWebRequest 請求方法
/// <summary> /// postweb請求 /// </summary> /// <param name="postUrl"></param> /// <param name="paramData"></param> /// <param name="dataEncode"></param> /// <returns></returns> public static string PostWebRequest(string postUrl, string paramData) { string ret = string.Empty; try { byte[] byteArray = Encoding.UTF8.GetBytes(paramData); HttpWebRequest webReq = WebRequest.Create(postUrl) as HttpWebRequest; webReq.Method = "GET"; HttpWebResponse response = (HttpWebResponse)webReq.GetResponse(); StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); ret = sr.ReadToEnd(); sr.Close(); response.Close(); } catch (Exception ex) { throw ex; } return ret; }
調用微信接口獲取access_token信息
public Dictionary<string, object> get_access_token(string code) { JavaScriptSerializer Jss = new JavaScriptSerializer(); string url = string.Format("https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code", "AppID", "AppSecret", code); Dictionary<string, object> respDic = (Dictionary<string, object>)Jss.DeserializeObject(PostWebRequest(url, "")); return respDic; }
3 第三步:刷新access_token(如果需要)
由於access_token有,涉及access_token的獲取access_token的接口是有次數限制的,所以我推薦大家做一個全局變量存儲,定時刷新,請參考《C#微信公眾平台開發—access_token的獲取存儲與更新》
4 第四步:拉取用戶信息(需scope為 snsapi_userinfo)
根據第二步獲取到的access_token調用微信接口獲取用戶信息
/// <summary> /// 用openid換取用戶信息 /// </summary> /// <param name="openid">微信標識id</param> /// <returns></returns> public Dictionary<string, object> GetUserInfo(string code) { JavaScriptSerializer Jss = new JavaScriptSerializer(); Dictionary<string, object> access_info = get_access_token(code);//獲取access_token string url = string.Format("https://api.weixin.qq.com/sns/userinfo?access_token={0}&openid={1}&lang=zh_CN", access_info["access_token"], access_info["openid"]); Dictionary<string, object> respDic = (Dictionary<string, object>)Jss.DeserializeObject(PostWebRequest(url, "")); return respDic; }
5 第五步:將上述獲取的用戶信息展示到客戶端上
在授權回調函數中完成相關業務
/// <summary> /// 授權回調函數 /// </summary> /// <returns></returns> public ActionResult Index() { try { var code = Request.QueryString["code"]; //獲取回調返回的code if (!string.IsNullOrEmpty(code)) { Dictionary<string, object> DicJson = GetUserInfo(code.ToString()); //獲取用戶信息 ViewBag.nickname = DicJson["nickname"]; ViewBag.openid = DicJson["openid"]; ViewBag.province = DicJson["province"]; ViewBag.city = DicJson["city"]; ViewBag.country = DicJson["country"]; switch (DicJson["sex"].ToString()) { case "1":ViewBag.sex = "男";break; case "2": ViewBag.sex = "女"; break; default: ViewBag.sex = "未知"; break; } ViewBag.Error = "獲取用戶信息成功"; } else { ViewBag.nickname = ""; ViewBag.openid = ""; ViewBag.province = ""; ViewBag.city = ""; ViewBag.unionid = ""; ViewBag.code = ""; ViewBag.Error = "code沒找到!"; } } catch (Exception ex) { ViewBag.nickname = ""; ViewBag.openid = ""; ViewBag.province = ""; ViewBag.city = ""; ViewBag.unionid = ""; ViewBag.Error = ex.Message; } return View(); }
前端代碼參考(這里主要是為了測試,所以沒有細調樣式,大家湊合着看)
@{ ViewBag.Title = "Home Page"; } <div class="jumbotron"> <h1>獲取用戶信息</h1> </div> <div class="row"> <div class="col-md-4"> <h2>用戶名</h2> <p>@ViewBag.nickname</p> </div> <div class="col-md-4"> <h2>OpenID</h2> <p>@ViewBag.openid</p> </div> <div class="col-md-4"> <h2>性別</h2> <p>@ViewBag.sex</p> </div> </div> <div class="row"> <div class="col-md-4"> <h2>國家</h2> <p>@ViewBag.country</p> </div> <div class="col-md-4"> <h2>省份</h2> <p>@ViewBag.province</p> </div> <div class="col-md-4"> <h2>城市</h2> <p>@ViewBag.city</p> </div> </div> <div class="row"> <div class="col-md-12"> <h2>錯誤信息</h2> <p>@ViewBag.Error</p> </div> </div>
