zoukankan      html  css  js  c++  java
  • 内网或无域名服务器集成微信公众号接口

    公司项目,要集成微信公众号实现登录和消息推送,但没有独立服务器和域名,且由于安全考虑需部署在内网,因此建立以下模型实现

     背景:CRM集成微信用户登录,实现微信关注公众号,从菜单访问CRM系统,获得授权后绑定账号,无账号新建后自动绑定,此后每次微信授权即可直接登录。 
    难点:CRM系统部署在内网环境,且端口号不为80,因此与微信服务器无法直接通讯(主要是指获得微信回调)。
    解决过程:

    1、明确网络架构

    “应用服务器”向微信发起申请code和access_token等请求是不受限制的,但由于端口和域名限制而无法接收其回调,所以使用有绑定域名的“公网服务器”用于接收微信回调请求,并在接收请求后立即原样将ResponseContent传递给“应用服务器”,从而模拟“应用服务器”直接接收回调的效果。

    2、开发过程

    2.1、通过Nuget安装Senparc.Weixin组件:
               "Senparc.Weixin": "4.19.0", //基础库
               "Senparc.Weixin.MP": "14.4.4" //公众号相关功能
    2.2、应用服务器发起微信登录请求
            请求过程基本包括:
                1、用户是否已登录
                2、获取OAuth验证地址(借助appId和srcret)
                3、弹出用户授权确认,回调获取code
                4、使用code换取access_token和用户信息(包含openid)
                5、根据openid数据库查找用户是否存在
                6、存在,直接登录;不存在,向微信服务器申请获取用户信息,回调并跳转到绑定/新建账号界面
                7、登录
            在首页新增“微信登录”按钮,点击后跳转至OAuth验证地址:
            (图片略)

            生成OAuth验证地址的代码实现:
                引用:using Senparc.Weixin.MP.AdvancedAPIs;
            Controller:
           

    [AllowAnonymous]
    [HttpGet("login")]
            public IActionResult Login(string err="")
            {
                string SessionID = Guid.NewGuid().ToString(); // 用state作为参数,存入缓存中,并在回调中加以判断,避免在多次握手过程中泄露redirect_url及参数导致的安全问题,以增强系统安全性
                HttpContext.Session.SetString("SessionID", SessionID);
                _memoryCache.Set<string>(SessionID, "", TimeSpan.FromMinutes(15));//15分钟过期
                ViewBag.UrlUserInfo = OAuthApi.GetAuthorizeUrl(appId, wx_proxy, SessionID, Senparc.Weixin.MP.OAuthScope.snsapi_userinfo);
                ViewBag.error = err;
                return View("Login");
            }


                View: <a class="layadmin-user-jump-change gray" href="@ViewBag.UrlUserInfo" id="weixin" >微信登录</a>

            
    2.3、应用服务器接收回调方法编写
            新建WebAPI,实现接口:

           /// <summary>
            /// 微信用户授权回调,获取用户信息
            /// </summary>
            /// <param name="code"></param>
            /// <param name="state">传递给微信服务器的参数</param>
            /// <param name="proxy_state">从回调代理服务器传来的参数</param>
            /// <returns></returns>
            [HttpGet("userinfocallback")]
            public ActionResult UserInfoCallback(string code, string state, string proxy_state)
            {
                if (string.IsNullOrEmpty(code))
                {
                    return Ok(new { success = false, msg = "您拒绝了授权" });
                }
                if (_memoryCache.Get(state) == null)
                {
                    return Ok(new { success = false, msg = "验证失败!请从正规途径访问" });
                }
                OAuthAccessTokenResult result = null;
                try
                {
                    result = OAuthApi.GetAccessToken(appId, secret, code);
                }
                catch (Exception ex)
                {
                    return Ok(new { success = false, msg = "出错:" + ex.Message });
                }
                if (result.errcode != ReturnCode.请求成功)
                {
                    return Ok(new { success = false, msg = "请求错误:" + result.errmsg });
                }
                try
                {
                    OAuthUserInfo userInfo = OAuthApi.GetUserInfo(result.access_token, result.openid);
                    bool hasReg = _context.WEIXIN_USERs.Where(w => w.IS_DELETE == false && w.OPENID == userInfo.openid).Count() > 0 ? true : false;
                    if (hasReg)
                    {
    
                    }
                    else
                    {
                        //创建微信用户信息
                        WEIXIN_USER wx_user = new WEIXIN_USER();
                        wx_user.GID = Guid.NewGuid().ToString();
                        wx_user.UNIONID = string.IsNullOrEmpty(userInfo.unionid) ? string.Empty : userInfo.unionid;
                        wx_user.SEX = userInfo.sex == 1 ? "" : (userInfo.sex == 2 ? "" : "未知");
                        wx_user.PROVINCE = string.IsNullOrEmpty(userInfo.province) ? string.Empty : userInfo.province;
                        wx_user.OPENID = string.IsNullOrEmpty(userInfo.openid) ? string.Empty : userInfo.openid;
                        wx_user.NICKNAME = string.IsNullOrEmpty(userInfo.nickname) ? string.Empty : userInfo.nickname;
                        wx_user.MODIFY_DATE = DateTime.Now;
                        wx_user.CREATE_DATE = DateTime.Now;
                        wx_user.IS_DELETE = false;
                        wx_user.HEADIMGURL = string.IsNullOrEmpty(userInfo.headimgurl) ? string.Empty : userInfo.headimgurl;
                        wx_user.COUNTRY = string.IsNullOrEmpty(userInfo.country) ? string.Empty : userInfo.country;
                        wx_user.CITY = string.IsNullOrEmpty(userInfo.city) ? string.Empty : userInfo.city;
                        wx_user.BZ1 = appId;
                        wx_user.BZ2 = string.Empty;
                        _context.WEIXIN_USERs.Add(wx_user);
                        _context.SaveChanges();
                    }
                    //将用户openid写入缓存
                    _memoryCache.Set(state, userInfo.openid);
                    return Ok(new { success = true, openid = userInfo.openid });
                }
                catch (Exception ex)
                {
                    return Ok(new { success = false, msg = "获取用户信息错误:" + ex.Message });
                }
    
            }



    2.4、公网服务器(中间服务器)功能编写
             新建ashx一般处理程序,在其中实现接口:
      

    try
                {
                    //获得URL参数
                    string URLParam = HttpContext.Current.Request.Url.Query;
                    string URL = System.Configuration.ConfigurationManager.AppSettings["wx_host"].ToString()+"/api/weixin/userinfocallback";
                    string NewURL = String.Format("{0}{1}", URL, URLParam);//得到新的带参数的URL地址
                    string result;
                    if (context.Request.HttpMethod == "GET")//如果是Get请求,直接以Get方式调用自己的接口,一般用在微信接入验证
                    {
                        result = GetWebData(NewURL);
                    }
                    else//Post请求,一般用在发送消息
                    {
                        context.Request.InputStream.Position = 0;
                        StreamReader reader = new StreamReader(context.Request.InputStream);
                        string sReqData = reader.ReadToEnd();//加密XML信息
                        result = PostWithParm(NewURL, sReqData);
                    }
                     JsonData r= JsonMapper.ToObject(result);
                    if (r["success"].ToString().ToLower()=="true")
                    {
                        string openid = r["openid"].ToString().ToLower();
                        HttpContext.Current.Response.Redirect(System.Configuration.ConfigurationManager.AppSettings["wx_host"].ToString() + "/wxlogin/" + openid, true);
                    }
                    else
                    {
                        string error=HttpContext.Current.Server.UrlEncode(r["msg"].ToString().Trim());
                        HttpContext.Current.Response.Redirect(System.Configuration.ConfigurationManager.AppSettings["wx_host"].ToString() + "/login?err="+error, true);
    
                    }
                }
                catch(Exception ex)
                {
    
                    HttpContext.Current.Response.Write(ex.ToString());
                }


             将其接收微信服务器回调请求后发送的地址设置为应用服务器的回调方法。
    2.5、创建账号、登录方法省略

  • 相关阅读:
    前端开发在手机UC浏览器上遇到的坑
    前端开发在uc浏览器上遇到的坑
    object-fit 解决图片指定大小被压缩问题
    前端新手需要注意的几个问题
    利用apache搭建本地环境
    有简历,为何还要自我介绍?
    移动端项目总结
    JavaScript MVC框架PK:Angular、Backbone、CanJS与Ember(转载)
    Net Core 控制台程序使用Nlog 输出到log文件
    net core服务器缺包,如何在线安装?
  • 原文地址:https://www.cnblogs.com/cdoneiX/p/12258542.html
Copyright © 2011-2022 走看看