前几天在需求的情况下需要开发一个网站,用户需要点击按钮调用微信中的扫一扫功能,扫描一个只有病案号的二维码,扫描到的结果自动填充至网页中的Text文本框中。
在思考之下,使用过很多开源的二维码识别,很难做到和微信一样的扫描功能(扫到二维码自动识别),于是开始研究如何调用微信APP中的扫一扫功能。
官方开发文档:概述 | 微信开放文档
个人理解:通过h5/aspx页面调用微信扫一扫是调用微信app中的jdk,扫一扫是微信jdk中其中一个功能,官方允许h5/aspx通过JavaScript调用微信中的jdk,但需要进行合法认证后方可调用。
微信官方是允许公众号和官方小程序调用jdk,网页开发是属于公众号开发。
整个过程:加载网页-》身份验证-》获取官方给出的jdk调用权限 -》使用网页触发器-》调用jdk。
身份验证和jdk的调用都是通过JavaScript进行的。
前提准备:
软件:
1、微信开发者工具 稳定版 Stable Build | 微信开放文档
用途:debug,查看微信wx.config发送与回复。
2、花生壳 花生壳-免费内网穿透软件|端口映射工具|DNS免费动态域名解析_花生壳,内网也能用-贝锐花生壳官网 (6元购买99年HTTP映射)
自己有域名的可以跳过,本人是因为没有域名,才使用花生壳将自己的内网访问ip映射为公网的域名。
用途:内网穿透,将IIS发布的局域网网站的ip位置映射为域名。
服务:
1、微信公众号 个人订阅号或企业服务号
注册流程简单,自行百度即可注册。
用途:使用appid和secret获取微信JS-JDK调用权限。
2、SHA-1加密类库
用途:官方要求通过SHA-1生成签名。
开发过程:
一、项目建立
1、在visual studio中创建一个ASP.NET Web应用程序
2、 右击项目-》添加新项-》创建一个web窗体(使用C#语言 文件后缀为.aspx)
3、在界面中拖放一个button按钮和Text文本框,按钮用来触发调用,文本框用来获取扫描结果。
二、在aspx网页头部写入js,引入官方js文件。
<script src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
这里使用了一个偷懒的办法,直接通过网络请求js文件。
三、根据开发文档创建config注入,也在.aspx上部分。
<script>
wx.config({
debug: true,//是否开启调试模式 true会通过alert显示获取到的接口权限或报错
appId: '<%=appid%>', //以下4个是通过.aspx.cs后端传入
timestamp: '<%=time%>',
nonceStr: '<%=randstr%>',
signature: '<%=signstr%>',
jsApiList: ['scanQRCode']//需要调用的权限,这里以扫一扫为例 只获取了扫一扫的接口名
});
</script>
其他接口列表见:概述 | 微信开放文档
四、添加一个.cs类,用来处理获取config文件中所需要的appid、timestamp(时间戳)、nonceStr(随机字符串)、signature(签名)。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;
public static string GetInfo(string link,string appid,string secret) //传入url请求地址
{
//使用appid何secret请求获得accesstoken
string accesstoken = SendRequest("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret, Encoding.UTF8);
//根据accesstoken获得ticket
string url1 = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accesstoken.Substring(accesstoken.IndexOf(':') + 2, accesstoken.IndexOf(',') - 3 - accesstoken.IndexOf(':')) + "&type=jsapi";
string requstStr = SendRequest(url1, Encoding.UTF8);//进行请求
string ticket = requstStr.Substring(requstStr.IndexOf("ticket") + 9, requstStr.LastIndexOf(',') - 1 - requstStr.IndexOf("ticket") - 9);// 获得json参数 然后提出ticket
/*下面是加密获取签名过程 signature
参与签名的字段包括
1、noncestr(随机字符串),
2、有效的jsapi_ticket,
3、timestamp(时间戳),
4、url(当前网页的URL,不包含#及其后面部分) 。
对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=http://mp.weixin.qq.com?params=value
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
&noncestr=Wm3WZYTPz0wzccnW
×tamp=1414587457
&url=http://mp.weixin.qq.com?params=value
*/
DateTime dti = DateTime.Now; //声明存放空间
string dtime = dti.ToString("yyyy-MM-dd HH:mm:ss"); //获取时间
string noncestr = dti.ToString("yyyyMMddHHmmss"); //获取随机字符串 这里是将时间作为随机字符串
int timestamp = 1644481645;// 获取时间戳 这里网页转的当前时间 也可以直接用我的
string timestampstr = "1644481645";
noncestr = "20220212154406";//随机字符串 我是通过时间转的 自定义 可以直接用我的
string string1 = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + link;//拼接连接字符串
string signature = SHA1(string1, Encoding.UTF8);//SHA-1加密过程 可以使用动态库 没有的直接用我的 方法在后面
signature = signature.ToLower(); // 生成后转换小写
//signature = "32ba4a26f1c06f7cd94531745513ac7f96dd9da3"; 这里是我获得的 最好去网上先找sha-1加密验证一下算法是否正确
//返回 时间戳 、 加密字符串 、签名
return timestampstr + "," + noncestr + "," + signature;
}
/// <summary>
/// get方式获取请求结果
/// </summary>
/// <param name="url"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static string SendRequest(string url, Encoding encoding)
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "GET";
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
StreamReader sr = new StreamReader(webResponse.GetResponseStream(), encoding);
string str = sr.ReadToEnd();
return str;
}
/// <summary>
/// sha-1加密算法
/// </summary>
/// <param name="content"></param>
/// <param name="encode"></param>
/// <returns></returns>
public static string SHA1(string content, Encoding encode)
{
try
{
SHA1 sha1 = new SHA1CryptoServiceProvider();
byte[] bytes_in = encode.GetBytes(content);
byte[] bytes_out = sha1.ComputeHash(bytes_in);
sha1.Dispose();
string result = BitConverter.ToString(bytes_out);
result = result.Replace("-", "");
return result;
}
catch (Exception ex)
{
throw new Exception("SHA1加密出错:" + ex.Message);
}
}
五、在.aspx.cs网页后端接受返回参数
/// <summary>
/// 先声明需要的三个返回js变量
/// </summary>
public string time = ""; //时间戳
public string randstr = ""; //加密字符串
public string signstr = ""; //签名
public string appid = "appidappid这里写你自己公众号的"; //appid
public string secret = "公众号中获取的"; //secret
public string jsapi_ticket = "";
protected void Page_Load(object sender, EventArgs e)
{
Response.Cache.SetNoStore();
string[] str = vxHelper.GetInfo(this.Request.Url.ToString(),appid,secret).Split(',');
time = str[0];
randstr = str[1];
signstr = str[2];
}
注:如果这个时候显示报错,前端JavaScript提示没有找到声明的变量,就把错误都注释掉重新生成一下。
如果vxHelper显示错误,这里是刚才写的cs类,建立时候起了什么名字写什么就行。
六、调用
本人是新建了JavaScript文件单独调用
wx.ready(function () {
document.querySelector('#scanQRCode0').onclick = function () {
wx.scanQRCode();
};
// 下面是扫描后返回结果 结果在res里
document.querySelector('#scanQRCode1').onclick = function () {
wx.scanQRCode({
needResult: 1,
desc: 'scanQRCode desc',
success: function (res) {
$("#txt1").textbox('setValue',res);
alert(JSON.stringify(res));
}
});
};
});
scanQRCode0和scanQRCode1是button的控件id,建议改一下button按钮的id。
部署过程:
这里以在本机部署为例,在服务器端部署差不多。
建议先测试后,并且将获取的token和签名存入数据库,在取出时先判断是否存在2小时,若超过2小时后token过期需要重新获取。
入库和取库过程根据自己习惯进行,这里不在赘述。
1、部署至本机IIS服务器,如不会自行百度。
设置aspx网页为默认文档,端口80如果被占用请换一个端口。
这里一定要选择局域网IP地址,方便使用花生壳映射。
2、内网穿透,打开花生壳APP
添加如下映射:
保存成功后点击此域名查看是否可以正常访问
如果可以正常访问,打开公众号设置平台
(1)在公众号设置-》功能设置-》js接口安全域名中添加花生壳映射的域名地址
不需要前面加"http://"
(2)在基本配置中,把本机或者服务器的外网地址(不知道的百度“我的ip”可以看到)添加进ip白名单中:
测试使用:
使用微信打开域名,点击按钮,若在config配置中开启了debug模式,则会有弹窗显示成功与失败,若成功会显示已经获取到的js接口允许调用的列表。
如有错误使用微信开发者工具,选择公众号网页开发,输入域名进行debug调试。
如果不需要弹窗正式上线的时候,记得把config中的debug改为false。