.netMVC企业微信网页授权+注册全局过滤器
微信网页授权
达到效果:企业应用只能在微信中查看,在浏览器中打开企业页面,显示无权限!
原理,用session记录用户,如果用户已经通过微信授权,记录@Session["UserId"],如果用户没有登录,则采用微信页面跳转的Code去换取UserId,如果能成功换取,则存入session,并登录成功。
逻辑图如下:
(小插曲:1 微信页面重定向时,会发送多个请求,导致只能使用一次的Code失效 40029 报错
处理逻辑是,采用同步锁,页面第一次跳转到我们的服务器时,把code存入session,并且对这段代码加锁,如果微信又发一个请求,先对比session中的Code,如果有了,则不再使用第二个code(因为微信页面授权code只能使用一次))
private static object locker = new object(); //同步锁
try { lock (locker) { if (Session["UserId"] == null)// 1 如果用户未登录 { if (!string.IsNullOrWhiteSpace(code) && Session["Code"] != (object)code)//2 有Code 参数,并且Code参数是第一次 { //处理逻辑Star Session["Code"] = code;//把Code记录session var userid = WechatCheckCode(code).UserId; //调用微信接口,获取用户信息 if (string.IsNullOrWhiteSpace(userid)) { response.Redirect("/Admin/Admin/Error?msg=xxxxxx"); } Session["UserId"] = userid;//用户写入session //处理逻辑End } else { response.Redirect("/Admin/Admin/Error?msg=xxxxxx"); } } } } catch { response.Redirect("/Admin/Admin/Error?msg=xxxxxx"); }
(小插曲:2 为了让所有的Controller都先进行一次校验,采用全局过滤器)
// 1在Global中添加全局过滤器 GlobalFilters.Filters.Add(new AuthAttribute()); //2 AuthAttribute 过滤器逻辑 public class AuthAttribute : AuthorizeAttribute { private readonly string WeChatUrl = ConfigurationManager.AppSettings["WeChatUrl"]; private static object locker = new object();//使用同步锁,防止微信一次跳转多次请求 public override void OnAuthorization(AuthorizationContext filterContext) { var path = filterContext.HttpContext.Request.Path; if (!path.ToLower().StartsWith("/admin/admin/error")) { var response = filterContext.HttpContext.Response; var request = filterContext.HttpContext.Request; var Session = filterContext.HttpContext.Session; var code = request.Params["code"]; try { lock (locker) { if (Session["UserId"] == null) { if (!string.IsNullOrWhiteSpace(code) && Session["Code"] != (object)code)//1 判断session里面是否有用户 { Session["Code"] = code; var userid = WechatCheckCode(code).UserId; if (string.IsNullOrWhiteSpace(userid)) { response.Redirect("/Admin/Admin/Error?msg=xxxxxx"); } Session["UserId"] = userid; } else { response.Redirect("/Admin/Admin/Error?msg=xxxxxx"); } } } } catch { response.Redirect("/Admin/Admin/Error?msg=xxxxxx"); } } } //微信检查Code private Wechat_UserModel WechatCheckCode(string code) { Wechat_UserModel resultBack = null; string WeChat_Address = WeChatUrl; var url = WeChat_Address + "api/ApiGetUser/GetUser?code=" + code; var client = new HttpClient(); var result = client.GetAsync(url).Result; if (result.IsSuccessStatusCode) { LogManager.WriteLog("code日志", string.Format("【获取的用户数据】:{0}", result.Content.ReadAsStringAsync().Result)); var json = result.Content.ReadAsStringAsync().Result; resultBack = JsonConvert.DeserializeObject<Wechat_UserModel>(json);//Json 反序列化成对象 } return resultBack; }
(小插曲:3 webApi,返回动态Json字符串是,不能直接返回string,应该 返回 HttpResponseMessage)
// 1 微信网页授权根据Code,获取用户信息 public HttpResponseMessage GetUser(string code) { string access_token = wxAccess_token.IsExistAccess_Token(wxEnum.供应商系统); //获取access_token var json = UserAdminAPI.GetWebUser(access_token, code);//调用微信接口,返回用户JSon字符串 return new HttpResponseMessage() { Content = new StringContent(json, Encoding.UTF8, "application/json") }; }
/// <summary> /// 2 根据code获取成员信息 /// </summary> /// <returns></returns> public static string GetWebUser(string access_token, string code) { var url = string.Format("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token={0}&code={1}", access_token, code); var client = new HttpClient(); var result = client.GetAsync(url).Result; if (!result.IsSuccessStatusCode) return null; return result.Content.ReadAsStringAsync().Result; }
4 前端页面资源
效果图:
1 母版页 @RenderBody()子页面插入的位置,@RenderSection,子页面自定义填充位置 @{ Layout = null; } <!doctype html> <html> <head> <meta charset="utf-8"> <title>供应商移动端</title> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0"> <link rel="stylesheet" href="~/Content/style/weui.css" /> <link rel="stylesheet" href="~/Content/style/weui2.css" /> <link rel="stylesheet" href="~/Content/style/weui3.css" /> <script src="~/Content/zepto.min.js"></script> @RenderSection("Head", required: false) </head> <body ontouchstart style="background-color: #f8f8f8;"> <div class="weui_tab tab-bottom"> </div> <div class="weui_tabbar "> <a href="/admin/admin/Index_mobile" class="weui_tabbar_item weui_bar_item_on"> <div class="weui_tabbar_icon"> <img src="~/Content/images/icon_nav_button.png" alt=""> </div> <p class="weui_tabbar_label">首页</p> </a> </div> @RenderBody() //子页面插入的地方 @RenderSection("Dialog", required: false) </body> </html> @RenderSection("Foot", required: false) 2 首页 @{ ViewBag.Title = "Index_mobile"; Layout = "~/Views/Shared/_LayMobile.cshtml"; } <div class="page-hd"> <h1 class="page-hd-title"> 供应商系统 </h1> <p class="page-hd-desc">用户:@Session["UserId"]</p> </div> <div class="weui_grids"> <a href="/admin/Reconciliations/Index" class="weui_grid js_grid"> <div class="weui_grid_icon"> <img src="~/Content/images/icon_nav_dialog.png" alt=""> </div> <p class="weui_grid_label"> 供应商对账 </p> </a> <a href="/admin/Purchase/Index" class="weui_grid js_grid"> <div class="weui_grid_icon"> <img src="~/Content/images/icon_nav_cell.png" alt=""> </div> <p class="weui_grid_label"> 采购单查询 </p> </a> </div> @section Foot{ <script> Zepto(function ($) { //$.ajax({ // type: 'POST', // url: 'WechatLogin', // dataType: 'json', // success: function (data) { // alert(data.msg); // console.log(data); // }, // error: function (xhr, type) { // alert('Ajax error!') // } //}) }) </script> }