近期实现微信招聘公众号的需求,需要在微信用户同意公众号授权后,获取到微信用户信息。这一步操作在前端无法完成,所以这里我们使用了C# WebApi项目,通过接口实现后台获取微信用户数据再重定向到前端页面。
具体而言,微信网页授权流程分为四步: 1、引导用户进入授权页面同意授权,获取code; 2、通过code换取网页授权access_token(与基础支持中的access_token不同); 3、如果需要,开发者可以刷新网页授权access_token,避免过期;
4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制);
首先,我们需要明确传递给后台的参数,以及后台返回的数据是什么。根据上述思路,前端第一次请求后台是需要引导用户进入授权页面同意授权,获取code,只要这一过程发生在微信端并且用户点击同意,就可以获取到code;此时操作仍然在后台,我们还需要进一步获取用户信息,所以我们在后台需要伪跳转(在后台接口进行跳转至另一信息获取接口),调用微信接口获取到用户信息。这个时候鉴权、获取用户信息都完成了,后台如何把用户信息给前端、如何定位前端页面呢?所以我们需要在前端传递给后台的参数里,加上鉴权、获取用户信息后的重定向URL,并将用户信息拼接到URL上。
看到这里,思路就很清晰了。前端请求后台鉴权并带上重定向URL、后台获取用户信息并将用户信息添加到URL上、后台重定向此URL,最终微信端展示页面的就是每个用户的个人数据了。
下面这种方法可以获取微信OpenID,但不能获取到微信UnionID。
/// <summary> /// 微信公众号用户信息获取 /// </summary> public class AuthController : Controller { private static string appId = ConfigurationManager.AppSettings["appid"]; private static string secret = ConfigurationManager.AppSettings["appsecret"]; /// <summary> /// 微信公众号引导页 /// </summary> /// <returns>成功时,返回带有用户信息的URL并重定向</returns> public ActionResult GetUserInfo() { string code = Request.QueryString["code"]; string state = Request.QueryString["state"]; try { if (!string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(state)) { OAuthToken oauthToken = JsonConvert.DeserializeObject<OAuthToken>(new WXHelper().Request(string.Format("https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code", appId, secret, code), "", "GET")); string accesstoken = string.Empty; AccessToken token = JsonConvert.DeserializeObject<AccessToken>(new WXHelper().Request(string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appId, secret), "", "GET")); if (token != null && !string.IsNullOrEmpty(token.access_token)) { accesstoken = token.access_token; } if (oauthToken != null && !string.IsNullOrEmpty(oauthToken.openid)) { OAuthUserInfo userInfo = JsonConvert.DeserializeObject<OAuthUserInfo>(new WXHelper().Request(string.Format("https://api.weixin.qq.com/cgi-bin/user/info?access_token={0}&openid={1}&lang=zh_CN", accesstoken, oauthToken.openid), "", "GET")); } else { ViewData["errmsg"] = "Token获取失败!"; } } else { ViewData["errmsg"] = "用户code获取失败!"; } } catch (Exception ex) { ViewData["errmsg"] = ex.Message; } return View(); } /// <summary> /// 微信公众号引导页 /// </summary> /// <param name="url">微信前端传递的跳转url</param> /// <returns>成功时,重定向至获取用户信息</returns> public ActionResult Index(string url) { if (!string.IsNullOrEmpty(url)) { url = WXHelper.DecodeBase64(url); string state = EncryptHelper.MD5Encrypt(url); //使用微信接口,重定向地址为本接口中的另一方法 return Redirect(string.Format("https://open.weixin.qq.com/connect/oauth2/authorize?appid={0}&redirect_uri={1}&response_type=code&scope=snsapi_base&state={2}#wechat_redirect", appId, ConfigurationManager.AppSettings["apppath"] + "/**/**/GetUserInfo", state)); } else ViewData["errmsg"] = "重定向url不能为空!"; return View(); } }下面这种方法,使用sns token可以获取微信OpenID、UnionID:
/// <summary> /// 微信公众号用户信息获取 /// </summary> public class AuthController : Controller { private static string appId = ConfigurationManager.AppSettings["appid"]; private static string secret = ConfigurationManager.AppSettings["appsecret"]; /// <summary> /// 微信公众号引导页 /// </summary> /// <returns>成功时,返回带有用户信息的URL并重定向</returns> public ActionResult GetUserInfo() { string code = Request.QueryString["code"]; string state = Request.QueryString["state"]; try { if (!string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(state)) { string strGetSnsToken = new WXHelper().Request(string.Format("https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code", appId, secret, code), "", "GET"); JObject jo1 = JsonConvert.DeserializeObject<JObject>(strGetSnsToken); string strResult = new WXHelper().Request(string.Format("https://api.weixin.qq.com/sns/userinfo?access_token={0}&openid={1}&lang=zh_CN ", jo1["access_token"].ToString(), jo1["openid"].ToString()), "", "GET"); OAuthUserInfo userInfo = JsonConvert.DeserializeObject<OAuthUserInfo>(strResult); } else { ViewData["errmsg"] = "用户code获取失败!"; } } catch (Exception ex) { ViewData["errmsg"] = ex.Message; } return View(); } /// <summary> /// 微信公众号引导页 /// </summary> /// <param name="url">微信前端传递的跳转url</param> /// <returns>成功时,重定向至获取用户信息</returns> public ActionResult Index(string url) { if (!string.IsNullOrEmpty(url)) { url = WXHelper.DecodeBase64(url); string state = EncryptHelper.MD5Encrypt(url); //使用微信接口,重定向地址为本接口中的另一方法 return Redirect(string.Format("https://open.weixin.qq.com/connect/oauth2/authorize?appid={0}&redirect_uri={1}&response_type=code&scope=snsapi_base&state={2}#wechat_redirect", appId, ConfigurationManager.AppSettings["apppath"] + "/**/**/GetUserInfo", state)); } else ViewData["errmsg"] = "重定向url不能为空!"; return View(); } }
在实际项目中做微信网页授权会因为场景不同而产生不同的代码实现,但是基本的微信授权逻辑,在上述代码中都有体现,希望能给各位开发者在类似业务场景下带来些许帮助。微信接口的严格性(局限性),会给开发者带来一些麻烦,但是办法总比困难多。通过不断的发现问题、思考问题、解决问题,才是开发者的生存之道。