微信公众号开发——网页授权 (页面分享 、获取用户基本信息)

第一步:要用到的接口文档如下

微信公众号开发——网页授权 (页面分享 、获取用户基本信息)_第1张图片


第二步:使用网页服务,公众号需要如下配置

1.设置 授权回调页面域名

这里写图片描述

微信公众号开发——网页授权 (页面分享 、获取用户基本信息)_第2张图片


2.设置 JS接口安全域名设置(点击公众号左边菜单 公众号设置 进入设置页面 点击 功能设置)

微信公众号开发——网页授权 (页面分享 、获取用户基本信息)_第3张图片


上代码

BLL调用程序先贴上来

系统入口 :指的是进入系统主页之前的一个请求地址,请求到达这里 又用调用微信的接口,通过微信来回调系统主页

/// 
/// 系统入口( http://jnga.sunmen.cn/Home/Entrance )
/// 
/// 
public ActionResult Entrance()
{
    string appid, redirect_uri, scope, state;
    appid = ConfigurationManager.AppSettings["appid"];
    redirect_uri = "http://jnga.sunmen.cn/Home/Index";
    scope = "snsapi_userinfo";   //snsapi_base用户无感觉 snsapi_userinfo 弹出授权页面
    state = "";                  //自定义参数
    WebPageAuthorize.RedirectUrl(appid, redirect_uri, scope, state);
    return new EmptyResult();
}
 /// 
 /// 系统首页
 /// 
 /// 预授权code
 /// 自定义参数
 /// 
public ActionResult Index(string code, string state)
{
    if (Common.IsWeixinBrowser())
    {
        string weixinJsApiStr = "onMenuShareTimeline,onMenuShareAppMessage";
        string appid = ConfigurationManager.AppSettings["appid"];
        string secret = ConfigurationManager.AppSettings["secret"];
        string weixinJsConfigStr = JsConfig.GetJsConfig(weixinJsApiStr, appid, secret);
        ViewBag.JsConfigStr = weixinJsConfigStr;

        if (!string.IsNullOrEmpty(code))
        {
            string openid = Session["openid"] == null ? "" : Session["openid"].ToString();

            int userType = 0;
            if (string.IsNullOrEmpty(openid))
            {
                Hashtable ht = WebPageAuthorize.GetWebPageAuthorizeAccessToken(appid, secret, code);
                openid = ht["openid"].ToString();
                Session["openid"] = openid;
            }  

//具体业务省略......
        }
        else
        {
            return Content("授权码不存在");
        }
    }
    else
    {
        return Content("请用微信浏览器打开");
    }
}

前端页面代码

@{
    Layout = null;
}



<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>金华人口流动申报系统title>
    <meta charset="utf-8">
    <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0" />
    <meta name="format-detection" content="telephone=no,email=no,date=no,address=no">
    <link href="~/res/css/aui.2.0.css" rel="stylesheet" />
head>
<body>
   省略.....
body>
html>
<script src="~/Scripts/jquery-1.7.1.min.js">script>
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js">script>
<script type="text/javascript">
    $(function () {

        @Html.Raw(@ViewBag.JsConfigStr);

        //config信息验证后会执行ready方法,
        //所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。
        //对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
        wx.ready(function () {

            //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口
            wx.onMenuShareTimeline({
                title: '金华人口流动申报系统', // 分享标题
                link: 'http://jnga.sunmen.cn',  // 分享链接
                imgUrl: 'http://jnga.sunmen.cn/images/wx.jpg', // 分享图标
                success: function () {
                    // 用户确认分享后执行的回调函数
                    alert("用户确认分享后--执行的回调函数");
                },
                cancel: function () {
                    // 用户取消分享后执行的回调函数
                    alert("用户取消分享后--执行的回调函数")
                }
            });

            wx.onMenuShareAppMessage({
                title: '金华人口流动申报系统', // 分享标题
                desc: '平安金华', // 分享描述
                link: 'http://jnga.sunmen.com', // 分享链接
                imgUrl: 'http://jnga.sunmen.cn/images/wx.jpg', // 分享图标
                type: 'link', // 分享类型,music、video或link,不填默认为link
                dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
                success: function () {
                    // 用户确认分享后执行的回调函数
                    alert("用户确认分享后--执行的回调函数");
                },
                cancel: function () {
                    // 用户取消分享后执行的回调函数
                    alert("用户取消分享后--执行的回调函数")
                }
            });
        });

        wx.error(function (res) {
            //config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
        });
    });
script>

WebPageAuthorize (网页授权类)

using Codeplex.Data;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WeiXinPublic.utils;

namespace WeiXinPublic.web_page_service
{
    /// 
    /// 网页授权
    /// 
    public class WebPageAuthorize
    {
        /// 
        /// 网页授权:第一步:用户同意授权,获取code,该方法在入口处调用
        /// 
        /// 公众号的唯一标识
        /// 授权后重定向的回调链接地址,请使用urlencode对链接进行处理
        /// 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)
        /// 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
        public static void RedirectUrl(string appid, string redirect_uri, string scope, string state)
        {
            string response_type = "code";
            string url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=" + redirect_uri + "&response_type=" + response_type + "&scope=" + scope + "&state=" + state + "#wechat_redirect";
            System.Web.HttpContext.Current.Response.Redirect(url);

            /*  https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
                如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE
                code说明 :code作为换取access_token的票据【网页授权特有的接口调用凭证(网页授权access_token)】,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
                回调页面接收code 和 state
             * 
             *  通过这一步,回调页面 只能再微信浏览器打开
            */
        }


        /// 
        /// 网页授权:第二步:通过code换取网页授权access_token 
        /// 
        /// 公众号的唯一标识
        /// 公众号的appsecret
        /// 填写第一步获取的code参数
        /// 
        private static string get_web_page_authorize_accesstoken(string appid, string secret, string code)
        {
            /*
                首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。
                公众号可通过下述接口来获取网页授权access_token。
                如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。
             */

            //获取code后,请求以下链接获取access_token: 

            string res = string.Empty;

            string url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code";
            res = Sender.Get(url);
            return res;
        }

        /// 
        /// 网页授权:第二步:通过code换取网页授权access_token 实体对象
        /// 
        /// 
        /// 
        /// 
        /// 
        public static Hashtable GetWebPageAuthorizeAccessToken(string appid, string secret, string code)
        {
            string res = get_web_page_authorize_accesstoken(appid, secret, code);
            var json = DynamicJson.Parse(res);
            var access_token = json.access_token;
            var expires_in = json.expires_in;
            var refresh_token = json.refresh_token;
            var openid = json.openid;
            var scope = json.scope;
            //var unionid = json.unionid;

            //万能字典
            Hashtable ht = new Hashtable();
            ht.Add("access_token", access_token);  //【网页授权】接口调用凭证,注意:此access_token与基础支持的access_token不同
            ht.Add("expires_in", expires_in);      //access_token接口调用凭证超时时间,单位(秒)

            // 刷新access_token
            // 由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token拥有较长的有效期(7天、30天、60天、90天),当refresh_token失效的后,需要用户重新授权。
            ht.Add("refresh_token", refresh_token);

            //用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
            ht.Add("openid", openid);

            //用户授权的作用域,使用逗号(,)分隔
            ht.Add("scope", scope);

            //只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。详见:获取用户个人信息(UnionID机制)
           // ht.Add("unionid", unionid);
            return ht;

        }
    }
}

JsConfig (Js配置生产类)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WeiXinPublic.dialog_service;
using WeiXinPublic.utils;

namespace WeiXinPublic.web_page_service
{
    /// 
    /// JsConfig 
    /// 
    public class JsConfig
    {
        /// 
        /// 3.签名计算 生成签名字符串
        /// 
        /// 随机字符串
        /// 有效的票据
        /// 时间戳
        /// 当前网页的URL,不包含#及其后面部分
        /// 
        private static string SignatureCompute(string noncestr, string jsapi_ticket, string timestamp, string url)
        {

            //3.签名算法

            /*签名生成规则如下:
            参与签名的字段包括          noncestr(随机字符串),
                                        jsapi_ticket(有效的票据), 
                                        timestamp(时间戳), 
                                        url(当前网页的URL,不包含#及其后面部分) 
            。对所有待 签名参数 按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。
            这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。*/



            #region 3-1.签名参数 按照字段名的ASCII 码从小到大排序(字典序)后
            Dictionary<string, string> dic = new Dictionary<string, string>();
            dic["timestamp"] = timestamp;
            dic["noncestr"] = noncestr;
            dic["url"] = url;
            dic["jsapi_ticket"] = jsapi_ticket;

            string[] arrkey = new string[] { "timestamp", "noncestr", "url", "jsapi_ticket" };
            arrkey = arrkey.OrderBy(n => n).ToArray();
            #endregion

            #region 3-2.使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1
            string signatureStr = "";
            foreach (string key in arrkey)
            {
                signatureStr += key + "=" + dic[key] + "&";
            }
            signatureStr = signatureStr.Substring(0, signatureStr.Length - 1);
            #endregion

            return signatureStr;
        }

        /// 
        /// 4.签名字符串 SHA1加密
        /// 
        /// 
        /// 
        private static string SignatureSha1(string str)
        {
            var sha1 = System.Security.Cryptography.SHA1.Create();

            byte[] bytes = Encoding.UTF8.GetBytes(str);
            byte[] bytesArr = sha1.ComputeHash(bytes);
            StringBuilder sb = new StringBuilder();
            foreach (var item in bytesArr)
            {
                sb.AppendFormat("{0:x2}", item);
            }

            return sb.ToString();
        }

        /// 
        /// 5.创建微信jsApi配置 字符串
        /// 
        /// 必填,公众号的唯一标识
        /// 必填,生成签名的时间戳
        /// 必填,生成签名的随机串
        /// 签名字符串 SHA1加密
        /// 需要注册的微信js接口 多个接口用,隔开
        /// 
        private static string CreateWeiXinJsConfig(string appid, string timestamp, string noncestr, string signatureStr, string weixinJsApiStr)
        {
            //"onMenuShareTimeline,onMenuShareAppMessage,onMenuShareQQ";

            string jsStr = "wx.config({";
            jsStr += "debug: false,";
            jsStr += "appId: '" + appid + "',";
            jsStr += "timestamp: " + timestamp + ",";
            jsStr += "nonceStr: '" + noncestr + "',";
            jsStr += "signature: '" + signatureStr + "',";
            jsStr += "jsApiList: ['" + weixinJsApiStr.Replace(",", "','") + "']";
            jsStr += "});";

            return jsStr;
        }


        /// 
        /// 获取微信jsApi配置字符串
        /// 
        /// 需要注册的微信js接口 多个接口用,隔开
        /// 
        public static string GetJsConfig(string weixinJsApiStr, string appid, string secret)
        {
            //获取随机字符串 必填,生成签名的随机串
            string noncestr = Common.GetRandomStr(32);

            //获取时间戳  必填,生成签名的时间戳
            string timestamp = Common.GetTimeStamp();

            //获取当前网页地址 可以带参数
            string url = System.Web.HttpContext.Current.Request.Url.AbsoluteUri;

            //1.获取access_token
            Hashtable ht_access_token = BasisSupport.GetAccessToken(appid, secret);
            string access_token = ht_access_token["access_token"].ToString();

            //2.用第一步拿到的access_token 
            Hashtable ht_jsapi_ticket = BasisApi.GetJsApiTicket(access_token);
            string ticket = ht_jsapi_ticket["ticket"].ToString();


            //3.签名计算 生成签名字符串
            string signatureStr = SignatureCompute(noncestr, ticket, timestamp, url);

            //4.签名字符串 SHA1加密   必填,签名,见附录1
            signatureStr = SignatureSha1(signatureStr);

            return CreateWeiXinJsConfig(appid, timestamp, noncestr, signatureStr, weixinJsApiStr);
        }


    }
}

Common 公共类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WeiXinPublic.utils
{
    public class Common
    {
        /// 
        /// 获取客户端浏览器的原始用户代理信息。
        /// 
        /// 
        public static string GetRequestUserAgent()
        {
            return System.Web.HttpContext.Current.Request.UserAgent.ToString();  //获取客户端浏览器的原始用户代理信息。
        }

        /// 
        /// 判断是否微信浏览器
        /// 
        /// 
        public static bool IsWeixinBrowser()
        {
            string userAgent = GetRequestUserAgent();
            return userAgent.IndexOf("MicroMessenger") > 0 ? true : false;
        }


        /// 
        /// 获取随机字符串
        /// 
        /// 
        /// 
        public static string GetRandomStr(int length)
        {
            string str = "0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
            Random random = new Random();
            string res = "";

            for (int i = 0; i < length; i++)
            {
                int index = random.Next(str.Length);
                res += str.Substring(index, 1);
            }

            return res;
        }

        /// 
        /// 获取时间戳
        /// 
        /// 
        public static string GetTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }
    }
}

BasisApi (基础接口类)

using Codeplex.Data;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WeiXinPublic.utils;

namespace WeiXinPublic.web_page_service
{
    /// 
    /// 基础接口
    /// 1.判断当前客户端版本是否支持指定JS接口
    /// 2.获取jsapi_ticket
    /// 
    public class BasisApi
    {
        #region 获取jsapi_ticket
        /// 
        /// 获得jsapi_ticket 采用http GET方式请求获得jsapi_ticket (有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket) 附录1-JS-SDK使用权限签名算法
        /// 
        /// 
        /// 
        private static string get_jsapi_ticket(string accesstoken)
        {
            string res = string.Empty;
            object obj = CacheHelper.GetCache("jsapi_ticket");
            if (obj != null)
            {
                res = obj.ToString();
            }
            else
            {
                string url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accesstoken + "&type=jsapi";  //GET方式请求
                res = Sender.Get(url);
                CacheHelper.AddCache("jsapi_ticket", res, DateTime.Now.AddMinutes(100));   //jsapi_ticket的有效期目前为2个小时  
            }
            return res;
        }

        /* jsapi_ticket

                {
                    "errcode":0,
                    "errmsg":"ok",
                    "ticket":"sM4AOVdWfPE4DxkXGEs8VFg9ITyPm9rZNXtk60kDcOW37rgw8PSTCg3DR9CEHATN4k4oIC8uzsCGbhKHykHjdA",
                    "expires_in":7200
                }

         */

        /// 
        /// 获取jsapi_ticket
        /// 
        /// 
        /// 
        public static Hashtable GetJsApiTicket(string accesstoken)
        {
            string res = get_jsapi_ticket(accesstoken);
            var json = DynamicJson.Parse(res);
            var errcode = json.errcode;
            var errmsg = json.errmsg;
            var ticket = json.ticket;
            var expires_in = json.expires_in;

            //万能字典
            Hashtable ht = new Hashtable();
            ht.Add("errcode", errcode);
            ht.Add("errmsg", errmsg);
            ht.Add("ticket", ticket);
            ht.Add("expires_in", expires_in);
            return ht;
        }  
        #endregion
    }
}

类库结构

微信公众号开发——网页授权 (页面分享 、获取用户基本信息)_第4张图片

你可能感兴趣的:(微信公众号开发)