微信公衆號開發——網頁授權 (頁面分享 、獲取用戶基本信息)

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

這裏寫圖片描述


第二步:使用網頁服務,公衆號需要如下配置

1.設置 授權回調頁面域名

這裏寫圖片描述

這裏寫圖片描述


2.設置 JS接口安全域名設置(點擊公衆號左邊菜單 公衆號設置 進入設置頁面 點擊 功能設置)

這裏寫圖片描述


上代碼

BLL調用程序先貼上來

系統入口 :指的是進入系統主頁之前的一個請求地址,請求到達這裏 又用調用微信的接口,通過微信來回調系統主頁

/// <summary>
/// 系統入口( http://jnga.sunmen.cn/Home/Entrance )
/// </summary>
/// <returns></returns>
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();
}
 /// <summary>
 /// 系統首頁
 /// </summary>
 /// <param name="code">預授權code</param>
 /// <param name="state">自定義參數</param>
 /// <returns></returns>
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;
}

<!DOCTYPE html>

<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
{
    /// <summary>
    /// 網頁授權
    /// </summary>
    public class WebPageAuthorize
    {
        /// <summary>
        /// 網頁授權:第一步:用戶同意授權,獲取code,該方法在入口處調用
        /// </summary>
        /// <param name="appid">公衆號的唯一標識</param>
        /// <param name="redirect_uri">授權後重定向的回調鏈接地址,請使用urlencode對鏈接進行處理</param>
        /// <param name="scope">應用授權作用域,snsapi_base (不彈出授權頁面,直接跳轉,只能獲取用戶openid),snsapi_userinfo (彈出授權頁面,可通過openid拿到暱稱、性別、所在地。並且,即使在未關注的情況下,只要用戶授權,也能獲取其信息)</param>
        /// <param name="state">重定向後會帶上state參數,開發者可以填寫a-zA-Z0-9的參數值,最多128字節</param>
        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
             * 
             *  通過這一步,回調頁面 只能再微信瀏覽器打開
            */
        }


        /// <summary>
        /// 網頁授權:第二步:通過code換取網頁授權access_token 
        /// </summary>
        /// <param name="APPID">公衆號的唯一標識</param>
        /// <param name="SECRET">公衆號的appsecret</param>
        /// <param name="CODE">填寫第一步獲取的code參數</param>
        /// <returns></returns>
        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;
        }

        /// <summary>
        /// 網頁授權:第二步:通過code換取網頁授權access_token 實體對象
        /// </summary>
        /// <param name="appid"></param>
        /// <param name="secret"></param>
        /// <param name="code"></param>
        /// <returns></returns>
        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
{
    /// <summary>
    /// JsConfig 
    /// </summary>
    public class JsConfig
    {
        /// <summary>
        /// 3.簽名計算 生成簽名字符串
        /// </summary>
        /// <param name="noncestr">隨機字符串</param>
        /// <param name="jsapi_ticket">有效的票據</param>
        /// <param name="timestamp">時間戳</param>
        /// <param name="url">當前網頁的URL,不包含#及其後面部分</param>
        /// <returns></returns>
        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;
        }

        /// <summary>
        /// 4.簽名字符串 SHA1加密
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        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();
        }

        /// <summary>
        /// 5.創建微信jsApi配置 字符串
        /// </summary>
        /// <param name="appid">必填,公衆號的唯一標識</param>
        /// <param name="timestamp">必填,生成簽名的時間戳</param>
        /// <param name="noncestr">必填,生成簽名的隨機串</param>
        /// <param name="signatureStr">簽名字符串 SHA1加密</param>
        /// <param name="weixinJsApiStr">需要註冊的微信js接口 多個接口用,隔開</param>
        /// <returns></returns>
        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;
        }


        /// <summary>
        /// 獲取微信jsApi配置字符串
        /// </summary>
        /// <param name="weixinJsApiStr">需要註冊的微信js接口 多個接口用,隔開</param>
        /// <returns></returns>
        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
    {
        /// <summary>
        /// 獲取客戶端瀏覽器的原始用戶代理信息。
        /// </summary>
        /// <returns></returns>
        public static string GetRequestUserAgent()
        {
            return System.Web.HttpContext.Current.Request.UserAgent.ToString();  //獲取客戶端瀏覽器的原始用戶代理信息。
        }

        /// <summary>
        /// 判斷是否微信瀏覽器
        /// </summary>
        /// <returns></returns>
        public static bool IsWeixinBrowser()
        {
            string userAgent = GetRequestUserAgent();
            return userAgent.IndexOf("MicroMessenger") > 0 ? true : false;
        }


        /// <summary>
        /// 獲取隨機字符串
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        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;
        }

        /// <summary>
        /// 獲取時間戳
        /// </summary>
        /// <returns></returns>
        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
{
    /// <summary>
    /// 基礎接口
    /// 1.判斷當前客戶端版本是否支持指定JS接口
    /// 2.獲取jsapi_ticket
    /// </summary>
    public class BasisApi
    {
        #region 獲取jsapi_ticket
        /// <summary>
        /// 獲得jsapi_ticket 採用http GET方式請求獲得jsapi_ticket (有效期7200秒,開發者必須在自己的服務全局緩存jsapi_ticket) 附錄1-JS-SDK使用權限簽名算法
        /// </summary>
        /// <param name="accesstoken"></param>
        /// <returns></returns>
        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
                }

         */

        /// <summary>
        /// 獲取jsapi_ticket
        /// </summary>
        /// <param name="accesstoken"></param>
        /// <returns></returns>
        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
    }
}

類庫結構

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章