说明:

1/因为赚麻烦这里没有使用数据库或服务器缓存来存储access_token和jsapi_ticket,为了方便这里使用了本地的xml进行持久化这两个值以及这两个值的创建时间和有限期限。

2/每次请求先检查有没有存在并且在有效期内的access_token和jsapi_ticket,存在的话直接进行加密操作,不存在或过期重新请求wechat接口获得再进行加密。

3/每个分享的页面都需要将当页的url发送到服务器进行签名,且一定要encodeURIComponent,因为在微信中打开会自动给当前链接加个各种参数,从而导致url不一致,导致invalid signature签名错误。

4/分享的图标url( imgUrl )必须是绝对路径。

一 封装的微信授权工具类

WechatJsSdk.cs

using SouthRuiHeH5.Models;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web.Script.Serialization;
using System.Xml.Linq; namespace SouthRuiHeH5.Provider
{
public class WechatJsSdk
{
/// <summary>
/// 模拟get请求获取AccessToken
/// </summary>
/// <param name="appID"></param>
/// <param name="appSecret"></param>
/// <returns></returns>
public static string GetAccessToken(string appID, string appSecret)
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appID, appSecret);
JavaScriptSerializer js = new JavaScriptSerializer();
AccessTokenOutput output = js.Deserialize<AccessTokenOutput>(HttpGet(url));
SaveAccessTokenInXml(output);
return output.access_token;
} /// <summary>
/// 将AccessToken保存进xml
/// </summary>
/// <param name="input"></param>
private static void SaveAccessTokenInXml(AccessTokenOutput input)
{
if (string.IsNullOrWhiteSpace(input?.access_token)) return;
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
string filePath = mappedPath + "Xml/AccessToken.xml";
XDocument inputDoc = XDocument.Load(filePath);
inputDoc.Elements().First().Element("access_token").Value = input.access_token;
inputDoc.Elements().First().Element("expires_in").Value = input.expires_in;
inputDoc.Elements().First().Element("create_time").Value = DateTime.Now.ToString();
inputDoc.Save(filePath);
} /// <summary>
/// 模拟get请求获取JsapiTicket
/// </summary>
/// <param name="accessToken"></param>
/// <returns></returns>
public static string GetJsapiTicket(string accessToken)
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi", accessToken);
JavaScriptSerializer js = new JavaScriptSerializer();
JsapiTicketOutput output = js.Deserialize<JsapiTicketOutput>(HttpGet(url));
SaveJsapiTicketInXml(output);
return output.ticket;
} /// <summary>
/// 将JsapiTicket保存进xml
/// </summary>
/// <param name="input"></param>
private static void SaveJsapiTicketInXml(JsapiTicketOutput input)
{
if (string.IsNullOrWhiteSpace(input?.ticket)) return;
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
string filePath = mappedPath + "Xml/JsapiTicket.xml";
XDocument inputDoc = XDocument.Load(filePath);
inputDoc.Elements().First().Element("ticket").Value = input.ticket;
inputDoc.Elements().First().Element("expires_in").Value = input.expires_in;
inputDoc.Elements().First().Element("create_time").Value = DateTime.Now.ToString();
inputDoc.Save(filePath);
} /// <summary>
/// get模拟请求
/// </summary>
/// <param name="Url"></param>
/// <param name="postDataStr"></param>
/// <returns></returns>
private static string HttpGet(string Url, string postDataStr = "")
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);
request.Method = "GET";
request.ContentType = "text/html;charset=UTF-8"; HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream myResponseStream = response.GetResponseStream();
StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
string retString = myStreamReader.ReadToEnd();
myStreamReader.Close();
myResponseStream.Close();
return retString;
} private static string[] strs = new string[]
{
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"
}; /// <summary>
/// 获取随机字符串
/// </summary>
/// <returns></returns>
public static string CreatenNonce_str()
{
Random r = new Random();
var sb = new StringBuilder();
var length = strs.Length;
for (int i = ; i < ; i++)
{
sb.Append(strs[r.Next(length - )]);
}
return sb.ToString();
} /// <summary>
/// 获取时间戳
/// </summary>
/// <returns></returns>
public static long CreatenTimestamp()
{
return (DateTime.Now.ToUniversalTime().Ticks - ) / ;
} /// <summary>
/// sha1加密string1 获得Signature
/// </summary>
/// <param name="jsapi_ticket"></param>
/// <param name="noncestr"></param>
/// <param name="timestamp"></param>
/// <param name="url"></param>
/// <returns></returns>
public static string GetSignature(string jsapi_ticket, string noncestr, long timestamp, string url)
{
string string1 = string.Format("jsapi_ticket={0}&noncestr={1}&timestamp={2}&url={3}", jsapi_ticket, noncestr, timestamp, url);
return Sha1(string1);
} /// <summary>
/// sha1
/// </summary>
/// <param name="orgStr"></param>
/// <param name="encode"></param>
/// <returns></returns>
private static string Sha1(string orgStr, string encode = "UTF-8")
{
var sha1 = new SHA1Managed();
var sha1bytes = System.Text.Encoding.GetEncoding(encode).GetBytes(orgStr);
byte[] resultHash = sha1.ComputeHash(sha1bytes);
string sha1String = BitConverter.ToString(resultHash).ToLower();
sha1String = sha1String.Replace("-", "");
return sha1String;
} } }

二  webapi部分

ConfigController.cs

using SouthRuiHeH5.Models;
using SouthRuiHeH5.Provider;
using System;
using System.Configuration;
using System.Linq;
using System.Web.Http;
using System.Xml.Linq; namespace SouthRuiHeH5.Controllers
{
public class ConfigController : ApiController
{
public ConfigOutput Get([FromUri]string url)
{
string appId = ConfigurationManager.AppSettings.Get("AppId");
string appSecret = ConfigurationManager.AppSettings.Get("AppSecret"); if (string.IsNullOrWhiteSpace(appId)) throw new Exception("AppSeeting:AppId Missed");
if (string.IsNullOrWhiteSpace(appSecret)) throw new Exception("AppSeeting:AppSecret Missed"); string jsapiTicket = getJsapiTicketFromXml();
if (string.IsNullOrEmpty(jsapiTicket))
{
string accessToken = GetAccessTokenFromXmlFirst();
if (string.IsNullOrEmpty(accessToken)) accessToken = WechatJsSdk.GetAccessToken(appId, appSecret);
if (string.IsNullOrEmpty(accessToken)) throw new Exception("Get AccessToken Error");
jsapiTicket = WechatJsSdk.GetJsapiTicket(accessToken);
if (string.IsNullOrEmpty(jsapiTicket)) throw new Exception("Get JsapiTicket Error");
} ConfigOutput output = new ConfigOutput
{
appId = appId,
nonceStr = WechatJsSdk.CreatenNonce_str(),
timestamp = WechatJsSdk.CreatenTimestamp()
};
output.signature = WechatJsSdk.GetSignature(jsapiTicket, output.nonceStr, output.timestamp, url);
return output;
} /// <summary>
/// 检查xml是否有JsapiTicket,并且JsapiTicket在有效期内
/// </summary>
/// <returns></returns>
private string getJsapiTicketFromXml()
{
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
string filePath = mappedPath + "Xml/JsapiTicket.xml";
XDocument inputDoc = XDocument.Load(filePath); string ticket = inputDoc.Elements().First().Element("ticket").Value;
bool expiresInTry = int.TryParse(inputDoc.Elements().First().Element("expires_in").Value, out int expiresIn);
bool createTimeTry = DateTime.TryParse(inputDoc.Elements().First().Element("create_time").Value, out DateTime createTime); if (!string.IsNullOrWhiteSpace(ticket) || !expiresInTry || !createTimeTry)
{
TimeSpan timeSpan = DateTime.Now.Subtract(createTime);
if (timeSpan.TotalSeconds < expiresIn)
return ticket;
}
return string.Empty;
} /// <summary>
/// 检查xml是否有AccessToken,并且AccessToken在有效期内
/// </summary>
/// <returns></returns>
private string GetAccessTokenFromXmlFirst()
{
var mappedPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
string filePath = mappedPath + "Xml/AccessToken.xml";
XDocument inputDoc = XDocument.Load(filePath); string accessToken = inputDoc.Elements().First().Element("access_token").Value;
bool expiresInTry = int.TryParse(inputDoc.Elements().First().Element("expires_in").Value, out int expiresIn);
bool createTimeTry = DateTime.TryParse(inputDoc.Elements().First().Element("create_time").Value, out DateTime createTime); if (!string.IsNullOrWhiteSpace(accessToken) || !expiresInTry || !createTimeTry)
{
TimeSpan timeSpan = DateTime.Now.Subtract(createTime);
if (timeSpan.TotalSeconds < expiresIn)
return accessToken;
}
return string.Empty; }
}
}

三 JS部分

wechat.share.js

var url = window.location.href.split('#')[];

$.get("/api/Config?url=" + encodeURIComponent(url), function (res) {
if (!res) return;
var input = res;
//input.debug = true;
input.jsApiList = ["onMenuShareTimeline", "onMenuShareAppMessage"];
wx.config(input);
}); wx.ready(function () {
onMenuShareTimeline();
onMenuShareAppMessage();
}); function onMenuShareTimeline() {
wx.onMenuShareTimeline({
title: '为态度喝彩!',
desc: '唯有创造价值,才能共享价值。南方瑞合三年定开基金(LOF)盛大发行中。',
link: url,
imgUrl: 'http://southruihe.huiz.cn/image/sharelogo.jpg',
success: function () { }
});
} function onMenuShareAppMessage() {
wx.onMenuShareAppMessage({
title: '为态度喝彩!',
desc: '唯有创造价值,才能共享价值。南方瑞合三年定开基金(LOF)盛大发行中。',
link: url,
imgUrl: 'http://southruihe.huiz.cn/image/sharelogo.jpg',
success: function () { }
});
}

四 其中使用的3个数据传输类

AccessTokenOutput.cs

namespace SouthRuiHeH5.Models
{
public class AccessTokenOutput
{
public string access_token { get; set; }
public string expires_in { get; set; }
public string errcode { get; set; }
public string errmsg { get; set; }
}
}

ConfigOutput.cs

namespace SouthRuiHeH5.Models
{
public class ConfigOutput
{
/// <summary>
/// 必填,公众号的唯一标识
/// </summary>
public string appId { get; set; }
/// <summary>
/// 必填,生成签名的时间戳
/// </summary>
public long timestamp { get; set; }
/// <summary>
/// 必填,生成签名的随机串
/// </summary>
public string nonceStr { get; set; }
/// <summary>
/// 必填,签名
/// </summary>
public string signature { get; set; }
}
}

JsapiTicketOutput.cs

namespace SouthRuiHeH5.Models
{
public class JsapiTicketOutput
{
public string errcode { get; set; }
public string errmsg { get; set; }
public string ticket { get; set; }
public string expires_in { get; set; }
}
}

H5+.Net Webapi集成微信分享前后端代码 微信JS-SDK wx.onMenuShareTimeline wx.onMenuShareAppMessage的更多相关文章

  1. 基于数据库的代码自动生成工具,生成JavaBean、生成数据库文档、生成前后端代码等(v6.0.0版)

    TableGo v6.0.0 版震撼发布,此次版本更新如下: 1.UI界面大改版,组件大调整,提升界面功能的可扩展性. 2.新增BeautyEye主题,界面更加清新美观,也可以通过配置切换到原生Jav ...

  2. [Java 开源项目]一款无需写任何代码,即可一键生成前后端代码的工具

    作者:HelloGitHub-小鱼干 JeecgBoot 是一款基于代码生成器的低代码开发平台,零代码开发.JeecgBoot 采用开发模式:Online Coding 模式-> 代码生成器模式 ...

  3. 记node前后端代码共用的一次坑

    项目背景 nodejs项目,webpack打包,用axios请求,Promise封装,nunjucks模板引擎: 之前已将nunjucks模板通过webpack打包策略,做成前后端共用: 目前需要将网 ...

  4. 实战:一键生成前后端代码,Mybatis-Plus代码生成器让我舒服了

    实战:一键生成前后端代码,Mybatis-Plus代码生成器让我舒服了 前言 在日常的软件开发中,程序员往往需要花费大量的时间写CRUD,不仅枯燥效率低,而且每个人的代码风格不统一.MyBatis-P ...

  5. 【SpringSecurity系列3】基于Spring Webflux集成SpringSecurity实现前后端分离无状态Rest API的权限控制

    源码传送门: https://github.com/ningzuoxin/zxning-springsecurity-demos/tree/master/02-springsecurity-state ...

  6. 微信分享自定义图片标题摘要-微信官方API

    我们平时在使用微信内置浏览器打开网页想要分享给好友或者发到朋友圈的时候经常会遇到这样的问题, 别人的网页分享的时候是这样的: 而我们自己的网页分享后这这样的: 看到有人说不做任何设置,微信分享时会自动 ...

  7. 封装微信分享到朋友/朋友圈js

    在页面引入: <script src="/static/lib/jquery-2.2.2.min.js"></script><script src=& ...

  8. layui上传文件组件(前后端代码实现)

    我个人博客系统上传特色图片功能就是用layui上传文件组件做的.另外采用某个生态框架,尽量都统一用该生态框架对应的解决方案,因为这样一来,有这么几个好处?1.统一而不杂糅,有利于制定相应的编码规范,方 ...

  9. SpringCloud微服务实战——搭建企业级开发框架(三十一):自定义MybatisPlus代码生成器实现前后端代码自动生成

      理想的情况下,代码生成可以节省很多重复且没有技术含量的工作量,并且代码生成可以按照统一的代码规范和格式来生成代码,给日常的代码开发提供很大的帮助.但是,代码生成也有其局限性,当牵涉到复杂的业务逻辑 ...

随机推荐

  1. spring boot 错误处理总结

    在boot 中, 对404  和 异常 有了额外的处理. 当然,我们可以定制, 如何做呢? 1 写一个继承 ErrorController 的Controller 注意, 这里一定要继承 ErrorC ...

  2. python基础数据篇

    1. 列表.元组操作 列表是我们最以后最常用的数据类型之一,通过列表可以对数据实现最方便的存储.修改等操作 定义列表 ? 1 names = ['Alex',"Tenglan",' ...

  3. JDK1.7 的 HashMap

    HashMap是一个用于存储key-value的键值对集合,每个键值对都是一个Entry.这些键值对分散存储在一个数组中,这个数组就是HashMap的主干. HashMap每个初始值都为null. 1 ...

  4. 003之MFCSocket异步编程(指针机制)

    002篇是采用传统方式创建,不适应动态的网络环境,服务器为客户端保留着断开连接时的套接字,不够灵活.而采用指针机制不仅可以更加灵活性,而且能使代码更集中,更具有条理性.将其转变成指针机制.功能及运行保 ...

  5. 吴裕雄 python深度学习与实践(7)

    import cv2 import numpy as np img = np.mat(np.zeros((,))) cv2.imshow("test",img) cv2.waitK ...

  6. zend studio mac

    zend studio mac是一款PHP语言集成开发环境(IDE),专为开发人员研发,它包含了所有组件的开发为完整的PHP应用程序生命周期提供条件.zend studio是很多开发人员.程序员等专业 ...

  7. Cocos2dx开发之屏幕适配

    由于各种智能手机的屏幕大小都不一致,会出现同一张图片资源在不同的设备分辨率下显示不一样的问题.为避免这样的情况,需要Cocos引擎能提供多分辨率的支持,也就是说要求实现这样的效果 — 开发者不需要考虑 ...

  8. python 模块定义导入

    1.定义模块:用来从逻辑上组织python代码(变量.函数.类.逻辑:实现一个功能),本质就是:.py结尾的python文件(文件名:test.py,对应的模块名:test)包:本质就是一个目录(必须 ...

  9. 记录----第一次使用BFS(广度搜索)学习经验总结

    学习经验记录与分享—— 最近在学习中接触到了一种解决最短路径的实用方法----BFS(广度搜索),在这里总结并分享一下第一次学习的经验. 首先第一个要了解的是"queue"(队列函 ...

  10. Python面向对象中的classmethod类方法和__getattr__方法介绍

    一.classmethod介绍 介绍:@classmethod修饰符我们从名称就可以知道,这是一个类方法,那么和普通的类中的方法有什么不同的 a.类方法,是由类本身调用的,无需实例化类,直接用类本身调 ...