微信公眾號開發——網頁授權 (頁面分享 、獲取使用者基本資訊)
阿新 • • 發佈:2019-02-09
第一步:要用到的介面文件如下
第二步:使用網頁服務,公眾號需要如下配置
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
}
}
類庫結構