最近被要求制作一个微信小程序用作为工厂的入厂登记,功能大概就是访客来厂前需要扫描二维码进入小程序,授权微信个人资料,以获取昵称以及绑定的手机号码,小程序内有拍照识别来访车牌号功能,以此就需要用到标题中的三个功能,而现在写下笔记记录一下
1 制作进入小程序二维码
1.1 大致流程
制作进入小程序二维码,查看官方文档,发现可以有三个API获取小程序二维码,除了数量的限制与否,并没细究它们的区别,所以最终使用的是 getWXACodeUnlimit
这个API接口。
查看文档发现,它是一个使用 POST
方法请求https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN
获取二维码,其中:
URL
中的ACCESS_TOKEN
需要另外自行获取- 可传入的参数有:
scene
:用户扫描该码进入小程序/小游戏后,可以获取到二维码中的scene
值,再做处理逻辑,scene
的参数值需要进行encodeURIComponent
page
:跳转页面,必须是小程序已经发布的页面,如pages/index/index
,默认是首页width
:数值是Number
,表示二维码的宽度,默认为430px
- 还有一些感觉不常用的就不列出来了,需要的可转到官方文档
其中小程序获取 scene
的值,有些餐厅点餐,座位上会有特定的二维码,扫码后得到特定的座位号从而服务员得知送餐位置,应该就是依靠这个功能实现的。
Page({
onLoad (query) {
// scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene
const scene = decodeURIComponent(query.scene)
}
})
1.2 获取 ACCESS_TOKEN
获取 ACCESS_TOKEN
接口调用凭证,是通过 GET
方法请求 https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
,其中URL
中的 appid
以及 secret
都可以到小程序控制后台中获取,使用浏览器即可获取。
1.3 保存二维码图片
这个 POST
请求我是使用Java 进行请求的,并转为图片,具体源码:
package top.seiei.accessxcx.util
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
public class HttpRequest {
/**
* 向指定 URL 发送POST方法的请求
* @param url 发送请求的 URL
* @param param 请求参数
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param) {
PrintWriter out = null;
InputStream in = null;
FileOutputStream fw = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("content-type","application/json");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
/* 获取URLConnection对象对应的输出流 */
out = new PrintWriter(conn.getOutputStream());
// 中文有乱码的需要将PrintWriter改为如下
//out=new OutputStreamWriter(conn.getOutputStream(),"UTF-8")
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = conn.getInputStream();
fw = new FileOutputStream("F:\\Desert.jpg");
byte[] buf = new byte[1024];
int ln = 0;
while ((ln = in.read(buf)) != -1) {
fw.write(buf, 0, ln);
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!"+e);
e.printStackTrace();
}
//使用finally块来关闭输出流、输入流
finally {
try {
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
if(fw!=null){
fw.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
System.out.println("post推送结果:"+result);
return result;
}
public void main(String[] args) throws Exception {
String access_token = "xxxx";
String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + access_token;
String param = "{ \"scene\":\"1254066020\"}";
HttpRequest.sendPost(url, param);
}
}
2 调用腾讯OCR识别车牌
2.1 大致流程
识别车牌号这边我是调用了腾讯OCR 车牌号识别 这个功能,看官方文档,发现它也是一个 POST
请求,它大致有两种方式申请,一是直接上传图片文件,而是上传图片 URL,由于使用的是微信小程序中的 wx.uploadFile
api,并不太确认上传的具体数据内容,所以我这里一开始选择的是上传图片给后台服务器,再返回图片URL给腾讯OCR识别,获取结果。
2.2 制作 authorization
权限签名
在请求车牌识别时,需要在请求头中设置 authorization
,在 官方文档 中,已经有示例演示如何获取 authorization
,我这边使用的是 Java 示例获取,只需申请账号获取 主要的 appid
,secretId
,secretKey
即可。
package top.seiei.accessxcx.util
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
public class HttpRequest {
/**
* 向指定 URL 发送POST方法的请求
* @param url 发送请求的 URL
* @param param 请求参数
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
String authorization = "xxxxx";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("host", "recognition.image.myqcloud.com");
conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("content-type","application/json");
conn.setRequestProperty("Authorization", authorization);
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
/* 获取URLConnection对象对应的输出流 */
out = new PrintWriter(conn.getOutputStream());
//2.中文有乱码的需要将PrintWriter改为如下
//out=new OutputStreamWriter(conn.getOutputStream(),"UTF-8")
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!"+e);
e.printStackTrace();
}
//使用finally块来关闭输出流、输入流
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
System.out.println("post推送结果:"+result);
return result;
}
public void main(String[] args) throws Exception {
String appid = "xxxx";
String imgurl = "https://www.etscn.com.cn:40443/oa/microapp/20180927102519.jpg"
String url = "http://recognition.image.myqcloud.com/ocr/plate";
String param = "{ \"appid\":\"" + appid + "\", \"url\":\"" + imgurl + "\"}";;
HttpRequest.sendPost(url, param);
}
}
3 获取用户资料
3.1 大致流程
我这里需要获取用户信息有微信昵称和手机号,查看文档发现这两个东西是需要分开获取,分开请求用户授权的,也就是说用户信息并不包括其绑定的手机号码,手机号码需要另外获取,而且还不能在小程序中直接调用API弹出弹窗进行请求授权,要使用独特的按钮让用户点击触发弹窗事件,而且最后还需要解码获取具体数据。
3.2 获取用户信息
查看文档,获取用户信息需要使用 <button open-type="getUserInfo"></button>
这个组件按钮让用户点击,而不能使用直接调用 wx.authorize({scope: "scope.userInfo"})
这个接口来触发弹窗让用户授权。
点击按钮之后,可以在其绑定的事件中以形如 e.detail.userInfo
的形式获取得到一个包含昵称,位置,图像等信息的数据,
与此同时通过调用 wx.getSetting
这个接口,它的成功回调函数,可以获取到 authSetting
这个 用户授权结果 的键值模式,得到用户的授权后,此时可由形如 res.authSetting['scope.userInfo']
是否返回 true
来确认是否授权成功。
授权之后就无需次次让用户点击按钮获取用户信息了,可以直接调用 wx.getUserInfo
接口,它的成功回调函数来获取用户信息,此时就可以成功获取到了。
3.3 获取用户绑定手机号
获取用户绑定的手机号,查看官方文档,发现需要 session_key
,iv
,encryptData
这三个数据,其中:
session_key
:查看文档(《wx.login》,《code2Session》),通过在app.js
的onLauch
生命周期中使用wx.login
,它成功回调返回一个名叫code
的值,再向https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
网页发起GET
请求,从而获取用户的唯一标识(openid
) 及本次登录的 会话密钥(session_key
)iv
以及encryptData
这两个数据则需要定义一个形如<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">获取手机号码</button>
的组件,让用户点击授权之后,在成功回调函数中便可得到这两个数值
获取以上数值后,通过解码便可得到对应的手机号,这里我使用的是后台Java进行解码,以下是对应测试代码:
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
public class DecodeWXxcx {
public static void main(String[] args) {
String session_key = "XXX";
String iv = "XXX";
String encryptData = "XXX";
String decrypt = decrypt(session_key, iv, encryptData);
System.out.println(decrypt);
}
public static String decrypt(String session_key, String iv, String encryptData) {
String decryptString = "";
init();
byte[] sessionKeyByte = Base64.decodeBase64(session_key);
byte[] ivByte = Base64.decodeBase64(iv);
byte[] encryptDataByte = Base64.decodeBase64(encryptData);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
Key key = new SecretKeySpec(sessionKeyByte, "AES");
AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance("AES");
algorithmParameters.init(new IvParameterSpec(ivByte));
cipher.init(Cipher.DECRYPT_MODE, key, algorithmParameters);
byte[] bytes = cipher.doFinal(encryptDataByte);
decryptString = new String(bytes);
} catch (Exception e) {
e.printStackTrace();
}
return decryptString;
}
private static boolean hasInited = false;
public static void init() {
if (hasInited) {
return;
}
Security.addProvider(new BouncyCastleProvider());
hasInited = true;
}
}
引入的依赖有:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.55</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>