微信公众号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();
}

原文地址:https://www.jianshu.com/p/40e7e39c866e

posted @ 2019-03-01 14:47  huayueniansi  阅读(551)  评论(0编辑  收藏  举报