當用戶在微信使用者端中存取第三方網頁,公眾號可以通過微信網頁授權機制,來獲取使用者基本資訊,進而實現業務邏輯。我們一般通過使用者網頁授權來無感實現使用者登入,並獲取使用者的微信資訊。
注意:使用者管理類介面中的「獲取使用者基本資訊介面」,是在使用者和公眾號產生訊息互動或關注後事件推播後,才能根據使用者OpenID來獲取使用者基本資訊。這個介面,包括其他微信介面,都是需要該使用者(即openid)關注了公眾號後,才能呼叫成功的。
需要先到公眾平臺官網中的「設定與開發」-「功能設定」-「網頁授權域名」的設定選項中,修改授權回撥域名。請注意,這裡填寫的是域名(是一個字串),而不是URL,因此請勿加 http:// 等協定頭;
在確保微信公眾賬號擁有授權作用域(scope引數)的許可權的前提下(已認證服務號,預設擁有scope引數中的snsapi_base和snsapi_userinfo 許可權),引導關注者開啟如下頁面:
若提示「該連結無法存取」,請檢查引數是否填寫錯誤,是否擁有scope引數對應的授權作用域許可權。跳轉回撥redirect_uri,應當使用https連結來確保授權code的安全性,並且是在微信公眾號後臺設定的網頁授權域名的存取地址。
引數 | 是否必須 | 說明 |
---|---|---|
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,與基礎支援中的access_token(該access_token用於呼叫其他介面)不同。公眾號可通過下述介面來獲取網頁授權access_token。如果網頁授權的作用域為snsapi_base,則本步驟中獲取到網頁授權access_token的同時,也獲取到了openid,snsapi_base式的網頁授權流程即到此為止。
引數 | 是否必須 | 說明 |
---|---|---|
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; }
}
如果網頁授權作用域為snsapi_userinfo,則此時開發者可以通過access_token和openid拉取使用者資訊了。
引數 | 描述 |
---|---|
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; }
}
}