頁面:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko" lang="ko">
<head>
<title>生成二維碼</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<script src="/Scripts/jquery.min.js"></script>
<style>
.WxLoginPage {
width: 304px;
height: 412px;
}
.modal-header {
padding: 15px;
border-bottom: 1px solid #e5e5e5;
}
.modal-body {
position: relative;
padding: 15px;
}
.form-group {
margin-bottom: 15px;
}
.img-thumbnail {
display: inline-block;
max-width: 100%;
height: auto;
padding: 3px;
line-height: 1.42857143;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 4px;
transition: all .2s ease-in-out;
}
.wx_close {
float: right;
font-size: 21px;
font-weight: 700;
line-height: 1;
color: #000;
text-shadow: 0 1px 0 #fff;
opacity: .2;
cursor: pointer;
border: 0;
background-color: white;
}
h4 {
font-size: 18px;
}
hr {
border: 0.7px solid #e5e5e5;
font-size: 0;
width: 265px;
}
</style>
</head>
<body>
<a href="javascript:void(0);" style="margin-top:500px;" id="btn">微信登錄</a>
<div class="WxLoginPage" style="display:none;">
<div class="modal-header">
<button type="button" class="wx_close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">請使用微信掃碼登錄</h4>
</div>
<div class="modal-body">
<div class="form-group ">
<img id="qrcode" src="" class="img-thumbnail" alt="正在加載請稍后..." />
</div>
</div>
<hr />
</div>
<script type="text/javascript">
$(function () {
$("#btn").on("click", function () {
//彈窗
layer.open({
type: 1,
shade: 0.8,
title: false, //不顯示標題
closeBtn: 0,
area: ['304px', '412px'],
content: $('.WxLoginPage') //捕獲的元素,注意:最好該指定的元素要存放在body最外層,否則可能被其它的相對元素所影響
});
$.ajax({
url: "/Home/GetAccessToken",
dataType: "json",
type: "post",
success: function (data) {
$("#qrcode").attr("src", data);
//是否掃碼關注公眾號監聽事件
GeLoginQrcode(data.Message);
}
});
});
//關閉
$(".wx_close").click(function () {
//關閉彈窗
layer.closeAll();
});
});
//是否掃碼關注公眾號監聽事件,ajax長輪詢
var timeout1 = 0;
function GeLoginQrcode(Tiket) {
$.ajax({
url: "/Home/WxGzhLogin",
//dataType: "json",
timeout:15000,
data: { "Tiket": Tiket },
type: "post",
success: function (data) {
if (!data.Success) {
//等待1秒后執行監聽事件
setTimeout(function () {
GeLoginQrcode(Tiket);
}, 1000);
} else {
window.location.href = data.Message;
}
},
complete: function (XMLHttpRequest, textStatus) {
//請求完成
if (XMLHttpRequest.readyState == "4") {
// alert(XMLHttpRequest.responseText);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
if (textStatus == "timeout") {
timeout1 = timeout1 + 1;
if (timeout1 <= 10) {
GeLoginQrcode(Tiket);
}
}
}
});
}
</script>
</body>
</html>
控制器:
//得到測試的appid和AppSecret:http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
string AppID = "xxxx";
string AppSecret = "xxxx";
ResultBase result = new ResultBase();
public ActionResult WxGzh()
{
return View();
}
#region 得到二維碼
public ActionResult CreateQrcode()
{
var AccessToken = GetAccessToken();
var qrcode = GetQrcode(AccessToken);
result.Data = qrcode;
result.Message = qrcode.Substring(qrcode.IndexOf('=') + 1);
return result;
}
//第一步:獲取accesstoken,使用AppId和AppSecret:獲取access_token
public string GetAccessToken()
{
string Url = "https://api.weixin.qq.com/cgi-bin/token";
string strUrl = Url + $"?grant_type=client_credential&appid={AppID}&secret={AppSecret}";
var request = (HttpWebRequest)WebRequest.Create(strUrl);
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
var info = JsonConvert.DeserializeObject<WxToken>(responseString);
return info.access_token;
}
//第二步:生成帶參數的二維碼,http請求方式: POST,參數access_token是第三步獲取的access_token
public string GetQrcode(string access_token)
{
string url = "https://api.weixin.qq.com/cgi-bin/qrcode/create";
var strUrl = url + $"?access_token={access_token}";
var dic = new Dictionary<string, object>();
dic.Add("expire_seconds", "604800");
dic.Add("action_name", "QR_SCENE");
var dic2 = new Dictionary<string, string>();
dic2.Add("scene_id", "123");
var dic3 = new Dictionary<string, object>();
dic3.Add("scene", dic2);
dic.Add("action_info", dic3);
var result = HttpService.Post(strUrl, dic.ToJson(), false, 30);//普通的post請求
var info = JsonConvert.DeserializeObject<Qrcode>(result);
return GetTicket(info.ticket);
}
//第三步:通過ticket得到圖片二維碼
public string GetTicket(string ticket)
{
string url = "https://mp.weixin.qq.com/cgi-bin/showqrcode";
var strUrl = url + $"?ticket={HttpUtility.UrlEncode(ticket)}";
return strUrl;
}
#endregion
#region 接口配置信息測試
/// <summary>
/// 在公眾平台測試號測試代碼才需要以下步驟,正式項目不需要域名映射,直接發布就可以
/// 第一步:
/// 測試接口http://s2691815y6.zicp.vip/Home/InterfaceTest(映射后的地址),使用花生殼進行域名映射,自己下載
/// ★★★項目不需要發布,直接部署到IIS上,IIS上網站路徑對應的是項目文件夾里面的項目文件名(就是和.sln解決方案同級的項目文件夾)
/// IP:* ,主機名:s3691815y6.zicp.vip(映射后的域名),端口:80
/// 第二步:
/// 進入公眾平台測試號管理:http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
/// 第三步:(必須先發布再配置)
/// 接口配置信息修改:URL:http://s3691815y6.zicp.vip/Home/InterfaceTest Token:nmslwsnd(隨便寫,但是要和InterfaceTest方法里的Token一致)
/// 第四步:
/// 如果要調試就打斷點:項目--》調試--》附加到進程--》顯示所有用戶的進程打勾--》找到w3wp.exe進程附加
/// </summary>
public void InterfaceTest()
{
if (Request.RequestType == "GET")
{
#region 驗證請求來源是否是微信
string signature = Request["signature"].ToString();
string timestamp = Request["timestamp"].ToString();
string nonce = Request["nonce"].ToString();
string echostr = Request["echostr"].ToString();
string token = "nmslwsnd";//公眾平台測試號管理--》接口配置信息修改的token,必須先把項目部署到IIS上再提交
List<string> list = new List<string>() { token, timestamp, nonce };
list.Sort();
string data = string.Join("", list);
byte[] temp1 = Encoding.UTF8.GetBytes(data);
SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
byte[] temp2 = sha.ComputeHash(temp1);
var hashCode = BitConverter.ToString(temp2);
hashCode = hashCode.Replace("-", "").ToLower();
if (hashCode == signature)
{
Response.Write(echostr);
Response.End();
}
#endregion
}
else
{
ProcessRequest(Request);
}
}
/// <summary>
/// 接收消息
/// </summary>
/// <param name="requset"></param>
public void ProcessRequest(HttpRequestBase requset)
{
string postString = string.Empty;
if (requset.HttpMethod.ToUpper() == "POST")
{
using (Stream stream = requset.InputStream)
{
byte[] postBytes = new byte[stream.Length];
stream.Read(postBytes, 0, (int)stream.Length);
postString = Encoding.UTF8.GetString(postBytes);
Handle(postString);
}
}
}
/// <summary>
/// 處理消息並應答
/// </summary>
/// <param name="postStr"></param>
public void Handle(string postStr)
{
MessageHelper messageHelper = new MessageHelper();
string responseString = messageHelper.ReturnMessage(postStr);
Response.ContentEncoding = Encoding.UTF8;
Response.Write(responseString);
}
#endregion
//是否掃碼關注公眾號監聽事件
[HttpPost]
public ActionResult WxGzhLogin(string Tiket)
{
//用二維碼的ticke值獲取openid,取緩存的值,Tiket的值為key,取緩存的值
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");//引用StackExchange.Redis
IDatabase db = redis.GetDatabase();
string wxopendid = db.StringGet(Tiket);
if (wxopendid != null)
{
var access_token = GetAccessToken();
string url = "https://api.weixin.qq.com/cgi-bin/user/info";
var strUrl = url + $"?access_token={access_token}&openid={wxopendid}&lang=zh_CN";
var request = (HttpWebRequest)WebRequest.Create(strUrl);
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
var info = responseString.FormJson<UserInfo>();
if (info != null)
{
//根據openid 判斷vipinfo是否存在
//VipInfo vip = new VipInfo();
//vip.WeiXinOpenId = wxopendid;
//var isExitvip = vipbll.isExitVipInfo(vip);
//if (isExitvip == null)
//{
// result.Success = true;
// result.Message = "/home/phonebind?OpenId=" + wxopendid + "&Photo=" + info.headimgurl;//為空就跳到手機綁定頁面
// return result;
//}
//else
//{
// //登錄用戶存入緩存,自己寫存入緩存的代碼
//}
}
result.Success = true;
result.Message = "";//回調地址,登錄成功地址
return result;
}
else
{
result.Success = false;
result.Message = "用戶未關注公眾號";
return result;
}
}
[Serializable]
public class ResultBase : ActionResult
{
public ResultBase()
{
Success = true;
Code = "00";
Message = "成功";
}
/// <summary>
/// 是否成功
/// </summary>
public bool Success { get; set; }
/// <summary>
/// 消息代碼
/// </summary>
public string Code { get; set; }
/// <summary>
/// 消息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 數據
/// </summary>
public object Data { get; set; }
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.ContentType = "application/json";
context.HttpContext.Response.Write(this.ToJson());
}
}
public class WxToken
{
public string access_token { get; set; }
public int expires_in { get; set; }
}
public class UserInfo
{
public int subscribe { get; set; }
// 關注狀態(1是關注,0是未關注),未關注時獲取不到其余信息
public string openid { get; set; }
public string nickname { get; set; }
public int sex { get; set; }
public string city { get; set; }
public string country { get; set; }
public string province { get; set; }
public string language { get; set; }
public string headimgurl { get; set; }
public string subscribe_time { get; set; }
public string unionid { get; set; }
public string remark { get; set; }
public string groupid { get; set; }
public string tagid_list { get; set; }
public string subscribe_scene { get; set; }
public string qr_scene { get; set; }
public string qr_scene_str { get; set; }
}
public class Qrcode
{
public string ticket { get; set; }
public int expire_seconds { get; set; }
public string url { get; set; }
}
/// <summary>
/// 回復類型
/// </summary>
public class ReplyType
{
/// <summary>
/// 普通文本消息
/// </summary>
public static string Message_Text
{
get
{
return @"<xml>
<ToUserName><![CDATA[{0}]]></ToUserName>
<FromUserName><![CDATA[{1}]]></FromUserName>
<CreateTime>{2}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[{3}]]></Content>
</xml>";
}
}
}
/// <summary>
/// 接受/發送消息幫助類
/// </summary>
public class MessageHelper
{
private static ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");//引用StackExchange.Redis
IDatabase db = redis.GetDatabase();
/// <summary>
/// 返回消息
/// </summary>
/// <returns></returns>
public string ReturnMessage(string postStr)
{
string responseContent = string.Empty;
XmlDocument document = new XmlDocument();
document.Load(new MemoryStream(Encoding.GetEncoding("UTF-8").GetBytes(postStr)));
XmlNode MsgType = document.SelectSingleNode("/xml/MsgType");
if (MsgType != null)
{
switch (MsgType.InnerText)
{
case "event":
responseContent = EventHandle(document);//事件處理
break;
default:
break;
}
}
return responseContent;
}
/// <summary>
/// 處理事件
/// </summary>
/// <param name="xmldoc"></param>
/// <returns></returns>
private string EventHandle(XmlDocument xmldoc)
{
string responseContent = "";
XmlNode Event = xmldoc.SelectSingleNode("/xml/Event");
XmlNode EventKey = xmldoc.SelectSingleNode("/xml/EventKey");
XmlNode ToUserName = xmldoc.SelectSingleNode("/xml/ToUserName");
XmlNode FromUserName = xmldoc.SelectSingleNode("/xml/FromUserName");
XmlNode Ticket = xmldoc.SelectSingleNode("/xml/Ticket");
if (Event != null)
{
if (Event.InnerText.Equals("subscribe"))//用戶點擊關注時的事件推送
{
//把openid存入緩存,必須存入緩存,Ticket.InnerText的值為key,把FromUserName.InnerText的值存在Ticket.InnerText里
db.StringSet(Ticket.InnerText, FromUserName.InnerText);
//weixin://,和http://有一樣的格式,表達的意思類似,就是告知瀏覽器,采取什么協議解析URL內容,只是這里的weixin://只能是微信瀏覽器才能識別。
responseContent = string.Format(ReplyType.Message_Text, FromUserName.InnerText, ToUserName.InnerText, DateTime.Now.Ticks, "歡迎您關注我的公眾號");
}
else if (Event.InnerText.Equals("unsubscribe"))//未關注
{
//do something..
}
else if (Event.InnerText.Equals("SCAN"))//用戶已關注時的事件推送
{
//把openid存入緩存,必須存入緩存
db.StringSet(Ticket.InnerText, FromUserName.InnerText);
}
}
return responseContent;
}
}
有些類自己封裝一下