微信公众号OAuth2页面授权
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
微信公众号网页授权获取Code和OpenId一共分为以下几个步骤:
- 重定向微信授权页面,通过回调链接地址获取Code
- 请求微信服务器获取网页授权access_token
微信网页授权是通过OAuth2.0机制实现的,封装对微信提供的Url
的请求:
public class OAuth2Api
{
/// <summary>
/// 获取验证地址
/// </summary>
/// <param name="appId">公众号的唯一标识</param>
/// <param name="redirectUrl">授权后重定向的回调链接地址</param>
/// <param name="state">重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节</param>
/// <param name="scope">应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )</param>
/// <param name="responseType">返回类型,请填写code</param>
/// <returns></returns>
public static string GetAuthorizeUrl(string appId, string redirectUrl, string state, OAuthScope scope = OAuthScope.snsapi_base, string responseType = "code")
{
var url = HttpUtility.UrlEncode(redirectUrl);// 使用 urlEncode 对链接进行处理
return $"https://open.weixin.qq.com/connect/oauth2/authorize?appid={appId}&redirect_uri={url}&response_type={responseType}&scope={scope:g}&state={state}&connect_redirect=1#wechat_redirect";
}
/// <summary>
/// 获取网页授权用户信息
/// </summary>
/// <param name="appId">公众号的唯一标识</param>
/// <param name="appSecret">公众号的appsecret</param>
/// <param name="code">code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期</param>
/// <param name="grantType">填写为authorization_code</param>
/// <returns></returns>
public async static Task<OAuthAccessTokenResult> GetAccessToken(string appId, string appSecret, string code, string grantType = "authorization_code")
{
var url = $"https://api.weixin.qq.com/sns/oauth2/access_token?appid={appId}&secret={appSecret}&code={code}&grant_type={grantType}";
var result = await HttpHelper.HttpGetJsonAsync<OAuthAccessTokenResult>(url);
return result;
}
/// <summary>
/// 拉取用户信息(需scope为 snsapi_userinfo)
/// </summary>
/// <param name="accessToken">网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同</param>
/// <param name="openId">用户的唯一标识</param>
/// <param name="lang">返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语</param>
/// <returns></returns>
public async static Task<WechatUserInfoResult> GetWechatUserInfo(string accessToken, string openId, string lang = "zh_CN")
{
var url = $"https://api.weixin.qq.com/sns/userinfo?access_token={accessToken}&openid={openId}&lang={lang}";
var result = await HttpHelper.HttpGetJsonAsync<WechatUserInfoResult>(url);
return result;
}
}
封装返回的JSON数据包对象基类:
public class MpJsonResult
{
/// <summary>
/// 错误码
/// </summary>
public MpJsonResultErrCode ErrCode { get; set; }
/// <summary>
/// 错误信息
/// </summary>
public string ErrMsg { get; set; }
}
封装错误返回码对象:
public enum ReturnCode
{
系统繁忙 = -1,
请求成功 = 0,
Code无效 = 40029,
频率限制 = 45011,
Code已被使用 = 40163
}
封装网页授权access_token返回的JSON数据包对象:
/// <summary>
/// code换取网页授权access_token返回的JSON数据包
/// </summary>
public class AccessToken
{
/// <summary>
/// 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
/// </summary>
public string OpenId { get; set; }
/// <summary>
/// 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
/// </summary>
public string Access_Token { get; set; }
/// <summary>
/// access_token接口调用凭证超时时间,单位(秒)
/// </summary>
public string Expires_In { get; set; }
/// <summary>
/// 用户刷新access_token
/// </summary>
public string Refresh_Token { get; set; }
/// <summary>
/// 用户授权的作用域,使用逗号(,)分隔
/// </summary>
public string Scope { get; set; }
}
封装拉取用户信息返回的JSON数据包对象:
public class WechatUserInfoResult
{
/// <summary>
/// 用户的唯一标识
/// </summary>
public string OpenId { get; set; }
/// <summary>
/// 用户昵称
/// </summary>
public string NickName { get; set; }
/// <summary>
/// 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
/// </summary>
public int Sex { get; set; }
/// <summary>
/// 用户个人资料填写的省份
/// </summary>
public string Province { get; set; }
/// <summary>
/// 普通用户个人资料填写的城市
/// </summary>
public string City { get; set; }
/// <summary>
/// 国家,如中国为CN
/// </summary>
public string Country { get; set; }
/// <summary>
/// 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
/// </summary>
public string HeadimgUrl { get; set; }
/// <summary>
/// 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
/// </summary>
public string[] Privilege { get; set; }
/// <summary>
/// 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
/// </summary>
public string UnionId { get; set; }
}
Controller获取Code与OpenId:
public IActionResult Index()
{
var redirectUrl = "https://wxtest.niansi.com/Home/AuthCallback";
var state = "123";
var url = OAuth2Api.GetAuthorizeUrl(Config.AppId, redirectUrl, state);
return Redirect(url);
}
public IActionResult AuthCallback(string code, string state)
{
if (string.IsNullOrWhiteSpace(code)) return Content("授权失败");
var accessToken =
OAuth2Api.GetAccessToken(Config.AppId, Config.AppSecret, code).Result;
if (accessToken.ErrCode != ReturnCode.请求成功) return Content(accessToken.ErrMsg);
var wechatUserInfo =
OAuth2Api.GetWechatUserInfo(accessToken.Access_Token, accessToken.OpenId).Result;
var openId = accessToken.OpenId;// 微信用户唯一标识
var headimgUrl = wechatUserInfo.HeadimgUrl;// 微信用户头像
return View();
}