C#微信公众平台开发一网页授权
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>