.NET微信網頁開發之網頁授權獲取使用者基本資訊

追逐時光者發表於2023-12-12

開發背景

當使用者在微信客戶端中訪問第三方網頁,公眾號可以透過微信網頁授權機制,來獲取使用者基本資訊,進而實現業務邏輯。我們一般透過使用者網頁授權來無感實現使用者登入,並獲取使用者的微信資訊。

注意:使用者管理類介面中的“獲取使用者基本資訊介面”,是在使用者和公眾號產生訊息互動或關注後事件推送後,才能根據使用者OpenID來獲取使用者基本資訊。這個介面,包括其他微信介面,都是需要該使用者(即openid)關注了公眾號後,才能呼叫成功的。

開發前配置

需要先到公眾平臺官網中的「設定與開發」-「功能設定」-「網頁授權域名」的配置選項中,修改授權回撥域名。請注意,這裡填寫的是域名(是一個字串),而不是URL,因此請勿加 http:// 等協議頭;

網頁授權的兩種scope的區別說明

  1. 以snsapi_base為scope發起的網頁授權,是用來獲取進入頁面的使用者的openid的,並且是靜默授權並自動跳轉到回撥頁的。使用者感知的就是直接進入了回撥頁(往往是業務頁面)
  2. 以snsapi_userinfo為scope發起的網頁授權,是用來獲取使用者的基本資訊的。但這種授權需要使用者手動同意,並且由於使用者同意過,所以無須關注,就可在授權後獲取該使用者的基本資訊。

第一步、使用者同意授權獲取code

在確保微信公眾賬號擁有授權作用域(scope引數)的許可權的前提下(已認證服務號,預設擁有scope引數中的snsapi_base和snsapi_userinfo 許可權),引導關注者開啟如下頁面:

若提示“該連結無法訪問”,請檢查引數是否填寫錯誤,是否擁有scope引數對應的授權作用域許可權。跳轉回撥redirect_uri,應當使用https連結來確保授權code的安全性,並且是在微信公眾號後臺配置的網頁授權域名的訪問地址。

  • https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

請求引數:

引數是否必須說明
appid 公眾號的唯一標識
redirect_uri 授權後重定向的回撥連結地址, 請使用 urlEncode 對連結進行處理
response_type 返回型別,請填寫code
scope 應用授權作用域,snsapi_base (不彈出授權頁面,直接跳轉,只能獲取使用者openid),snsapi_userinfo (彈出授權頁面,可透過openid拿到暱稱、性別、所在地。並且, 即使在未關注的情況下,只要使用者授權,也能獲取其資訊 )
state 重定向後會帶上state引數,開發者可以填寫a-zA-Z0-9的引數值,最多128位元組
#wechat_redirect 無論直接開啟還是做頁面302重定向時候,必須帶此引數
forcePopup 強制此次授權需要使用者彈窗確認;預設為false;需要注意的是,若使用者命中了特殊場景下的靜默授權邏輯,則此引數不生效

第二步、透過code換取網頁授權access_token

首先請注意,這裡透過code換取的是一個特殊的網頁授權access_token,與基礎支援中的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

請求引數:

引數是否必須說明
appid 公眾號的唯一標識
secret 公眾號的appsecret
code 填寫第一步獲取的code引數
grant_type 填寫為authorization_code

返回引數:

引數描述
access_token 網頁授權介面呼叫憑證,注意:此access_token與基礎支援的access_token不同
expires_in access_token介面呼叫憑證超時時間,單位(秒)
refresh_token 使用者重新整理access_token
openid 使用者唯一標識,請注意,在未關注公眾號時,使用者訪問公眾號的網頁,也會產生一個使用者和公眾號唯一的OpenID
scope 使用者授權的作用域,使用逗號(,)分隔
is_snapshotuser 是否為快照頁模式虛擬賬號,只有當使用者是快照頁模式虛擬賬號時返回,值為1
unionid 使用者統一標識(針對一個微信開放平臺賬號下的應用,同一使用者的 unionid 是唯一的),只有當scope為"snsapi_userinfo"時返回

正確時返回的JSON資料包如下:

{
  "access_token":"ACCESS_TOKEN",
  "expires_in":7200,
  "refresh_token":"REFRESH_TOKEN",
  "openid":"OPENID",
  "scope":"SCOPE",
  "is_snapshotuser": 1,
  "unionid": "UNIONID"
}

錯誤時微信會返回JSON資料包如下(示例為Code無效錯誤):

{"errcode":40029,"errmsg":"invalid code"}

請求示例程式碼:

    public class WeChatLogin : Controller
    {

        /// <summary>
        /// 獲取微信網頁授權access_token
        /// </summary>
        /// <param name="state">自定義引數</param>
        /// <param name="code">透過使用者授權後得到的code</param>
        /// <returns></returns>
        public async Task<Response> GetWeChatAccessToken(string state, string code)
        {
            string appId = "YourAppId";
            string appSecret = "YourAppSecret";
            string requestUrl = $"https://api.weixin.qq.com/sns/oauth2/access_token?appid={appId}&secret={appSecret}&code={code}&grant_type=authorization_code";

            using (var httpClient = new HttpClient())
            {
                var httpRequest = new HttpRequestMessage(HttpMethod.Get, requestUrl);
                using (var response = await httpClient.SendAsync(httpRequest))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        var responseString = await response.Content.ReadAsStringAsync();
                        var responseData = JsonConvert.DeserializeObject<WeChatTokenResponse>(responseString);

                        return new Response
                        {
                            Code = 1,
                            Message = responseData.AccessToken
                        };
                    }
                    else
                    {
                        var errorResponseString = await response.Content.ReadAsStringAsync();
                        var errorData = JsonConvert.DeserializeObject<ErrorResponse>(errorResponseString);

                        return new Response
                        {
                            Code = 0,
                            Message = $"Failed to get access token: {errorData.ErrMsg}"
                        };
                    }
                }
            }
        }
    }

    public class WeChatTokenResponse
    {
        [JsonProperty("access_token")]
        public string AccessToken { get; set; }

        [JsonProperty("expires_in")]
        public int ExpiresIn { get; set; }

        [JsonProperty("refresh_token")]
        public string RefreshToken { get; set; }

        [JsonProperty("openid")]
        public string OpenId { get; set; }

        [JsonProperty("scope")]
        public string Scope { get; set; }

        [JsonProperty("is_snapshotuser")]
        public int IsSnapshotUser { get; set; }

        [JsonProperty("unionid")]
        public string UnionId { get; set; }
    }

    public class ErrorResponse
    {
        [JsonProperty("errcode")]
        public int ErrCode { get; set; }

        [JsonProperty("errmsg")]
        public string ErrMsg { get; set; }
    }

第三步、獲取使用者資訊(需scope為 snsapi_userinfo)

如果網頁授權作用域為snsapi_userinfo,則此時開發者可以透過access_token和openid拉取使用者資訊了。

  • 請求方法:https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

請求引數:

引數描述
access_token 網頁授權介面呼叫憑證,注意:此access_token與基礎支援的access_token不同
openid 使用者的唯一標識
lang 返回國家地區語言版本,zh_CN 簡體,zh_TW 繁體,en 英語

返回引數:

引數描述
openid 使用者的唯一標識
nickname 使用者暱稱
sex 使用者的性別,值為1時是男性,值為2時是女性,值為0時是未知
province 使用者個人資料填寫的省份
city 普通使用者個人資料填寫的城市
country 國家,如中國為CN
headimgurl 使用者頭像,最後一個數值代表正方形頭像大小(有0、46、64、96、132數值可選,0代表640*640正方形頭像),使用者沒有頭像時該項為空。若使用者更換頭像,原有頭像URL將失效。
privilege 使用者特權資訊,json 陣列,如微信沃卡使用者為(chinaunicom)
unionid 只有在使用者將公眾號繫結到微信開放平臺賬號後,才會出現該欄位。

正確時返回的JSON資料包如下:

{   
  "openid": "OPENID",
  "nickname": NICKNAME,
  "sex": 1,
  "province":"PROVINCE",
  "city":"CITY",
  "country":"COUNTRY",
  "headimgurl":"https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
  "privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}

錯誤時微信會返回JSON資料包如下(示例為openid無效):

{"errcode":40003,"errmsg":" invalid openid "}

請求示例程式碼:

    public class WeChatLogin : Controller
    {
        /// <summary>
        /// 使用者資訊獲取
        /// </summary>
        /// <param name="accessToken"> 網頁授權介面呼叫憑證,注意:此access_token與基礎支援的access_token不同</param>
        /// <param name="openId">使用者的唯一標識</param>
        /// <returns></returns>
        public async Task<Response> GetWeChatUserInfo(string accessToken, string openId)
        {
            string requestUrl = $"https://api.weixin.qq.com/sns/userinfo?access_token={accessToken}&openid={openId}&lang=zh_CN";

            using (var httpClient = new HttpClient())
            {
                var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
                var response = await httpClient.SendAsync(request);

                if (response.IsSuccessStatusCode)
                {
                    var responseString = await response.Content.ReadAsStringAsync();
                    var responseData = JsonConvert.DeserializeObject<WeChatUserInfoResponse>(responseString);

                    return new Response
                    {
                        Code = 1,
                        Message = $"Nickname: {responseData.Nickname}, Province: {responseData.Province}, City: {responseData.City}"
                    };
                }
                else
                {
                    var errorResponseString = await response.Content.ReadAsStringAsync();
                    var errorData = JsonConvert.DeserializeObject<ErrorResponse>(errorResponseString);

                    return new Response
                    {
                        Code = 0,
                        Message = $"Failed to get user info: {errorData.ErrMsg}"
                    };
                }
            }
        }

        public class WeChatUserInfoResponse
        {
            [JsonProperty("openid")]
            public string OpenId { get; set; }

            [JsonProperty("nickname")]
            public string Nickname { get; set; }

            [JsonProperty("sex")]
            public int Sex { get; set; }

            [JsonProperty("province")]
            public string Province { get; set; }

            [JsonProperty("city")]
            public string City { get; set; }

            [JsonProperty("country")]
            public string Country { get; set; }

            [JsonProperty("headimgurl")]
            public string HeadImgUrl { get; set; }

            [JsonProperty("privilege")]
            public List<string> Privilege { get; set; }

            [JsonProperty("unionid")]
            public string UnionId { get; set; }
        }

        public class ErrorResponse
        {
            [JsonProperty("errcode")]
            public int ErrCode { get; set; }

            [JsonProperty("errmsg")]
            public string ErrMsg { get; set; }
        }
    }

DotNetGuide技術社群交流群

  • DotNetGuide技術社群是一個面向.NET開發者的開源技術社群,旨在為開發者們提供全面的C#/.NET/.NET Core相關學習資料、技術分享和諮詢、專案推薦、招聘資訊和解決問題的平臺。
  • 在這個社群中,開發者們可以分享自己的技術文章、專案經驗、遇到的疑難技術問題以及解決方案,並且還有機會結識志同道合的開發者。
  • 我們致力於構建一個積極向上、和諧友善的.NET技術交流平臺,為廣大.NET開發者帶來更多的價值和成長機會。

歡迎加入DotNetGuide技術社群微信交流群👪

.NET微信網頁開發相關文章教程

https://github.com/YSGStudyHards/DotNetGuide

參考文章

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

相關文章