Lake's Blog Lake's Blog
首页
HCFrame
  • 博客搭建

    • 搜索引擎
    • SEO优化
    • 问题记录
  • Vue

    • 问题记录
  • uni-app
  • 开发

    • Spring
  • 数据库及中间件

    • Elasticsearch
    • SQL
  • 杂谈

    • 杂谈
  • 微服务

    • nacos
    • CAS
  • 算法说明

    • algorithm
  • leetCode

    • leetCode
  • 代理

    • Nginx
  • Linux

    • ubuntu
  • Docker
  • 数据库
  • 消息队列
  • openwrt
  • 友情链接
关于
  • 网站
  • 资源
  • 分类
  • 标签
  • 归档
GitHub

Lake Liu

很菜的程序员
首页
HCFrame
  • 博客搭建

    • 搜索引擎
    • SEO优化
    • 问题记录
  • Vue

    • 问题记录
  • uni-app
  • 开发

    • Spring
  • 数据库及中间件

    • Elasticsearch
    • SQL
  • 杂谈

    • 杂谈
  • 微服务

    • nacos
    • CAS
  • 算法说明

    • algorithm
  • leetCode

    • leetCode
  • 代理

    • Nginx
  • Linux

    • ubuntu
  • Docker
  • 数据库
  • 消息队列
  • openwrt
  • 友情链接
关于
  • 网站
  • 资源
  • 分类
  • 标签
  • 归档
GitHub
  • 博客搭建

  • VUE

  • UNIAPP

    • uniapp中实现h5扫码功能(微信版)
      • 前言
      • 一、前期准备
      • 二、服务端开发
        • 1. 抽象appId和appSecret到yml文件中。
        • 2. 编写微信工具类(获取accessToken和ticket)
        • 3. 服务端接口
      • 三、H5前端开发
        • 1. 添加微信js-sdk依赖
        • 2. 获取后台的Request
        • 3. 加载页面后初始化wx.config内容(此处需要放到onLoad中执行)
        • 4. 调用微信扫码功能
        • 5. 完整代码
      • 参考内容

uniapp中实现h5扫码功能(微信版)

# uniapp中实现h5扫码功能(微信版)

# 前言

原本是使用uniapp开发微信小程序,扫码功能非常好实现。

但是由于业务原因,需要将小程序转换成H5的方式,发现 uni.scanCode 方法不好用了。

网上查询多个解决方案,但是由于我们的二维码过于复杂。

最终决定使用微信的扫一扫功能,通过js-sdk方式进行调用扫码功能。

扫码主要流程为:

  1. 首先需要通过微信打开H5页面。
  2. 进入扫码页面,扫码页面加载完成时,前端向服务端请求config信息。
  3. 服务端收到信息后,首先获取accessToken,然后通过accessToken换取js-sdk的ticket。(此处的accessToken和ticket可以存入系统缓存中,默认7200秒后超时)
  4. 服务端通过ticket和一些参数配置,生成前端所需信息,返回给前端页面。
  5. 前端页面完成加载。
  6. 点击扫码按钮,进行微信扫一扫的调用,就可以进行扫码了。

# 一、前期准备

  1. 使用此方法前,需要拥有一个公众号为主体。

  2. 获取公众号的appId 和 appSecret,不懂如何获取的请自行百度。

WX20220812-134132

  1. 需要将线上服务器的IP和你开发测试环境的IP,填写到IP白名单中,否则无法调用。

  2. 需要将线上服务器的域名,添加到JS接口安全域名。

WX20220812-134419

# 二、服务端开发

# 1. 抽象appId和appSecret到yml文件中。

此处可以自行通过其他方式实现,仅供参考。

  • yml文件内容
weixin:
  app-id: xxxxx
  app-secret: xxxxx
1
2
3
  • 配置类,用于装载yaml文件内容
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "weixin")
@Data
public class WeixinConfig {
    private String appId;
    private String appSecret;
}

1
2
3
4
5
6
7
8
9
10
11
12
  • 自动装载类,用于在springboot启动时,自动将yaml文件内容装载到bean中。
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(WeixinConfig.class)
@AutoConfigureAfter(WeixinConfig.class)
public class WeixinConfiguration {
}

1
2
3
4
5
6
7
8
9
10

# 2. 编写微信工具类(获取accessToken和ticket)

  • 获取accessToken
    public String getAccessToken() {
        // 从redis获取token
        String token = (String) redisUtil.get(RedisConstant.WEIXIN + RedisConstant.ACCESS_TOKEN);
        // 若redis为空,则需要从微信处获取token
        if (StringUtils.isEmpty(token)) {
            String requestUrl = ACCESS_TOKEN_URL.replace("APPID", weixinConfig.getAppId()).replace("SECRET", weixinConfig.getAppSecret());
            String response = HttpUtil.get(requestUrl);
            JSONObject jsonObject = JSONUtil.parseObj(response);
            // 判断微信返回结果是否异常
            if (StringUtils.isEmpty(jsonObject.get(ERRCODE)) || Integer.parseInt(String.valueOf(jsonObject.get(ERRCODE))) == 0) {
                // 持久化到redis中
                redisUtil.set(RedisConstant.WEIXIN + RedisConstant.ACCESS_TOKEN, jsonObject.getStr(ACCESS_TOKEN), 7000);
                return jsonObject.getStr(ACCESS_TOKEN);
            } else {
                logger.error(jsonObject.toStringPretty());
                throw new ServiceException("获取公众号token信息异常");
            }
        } else {
            return token;
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • 通过accessToken获取Ticket
    public String jsapiTicket() {
        // 从redis获取ticket
        String ticket = (String) redisUtil.get(RedisConstant.WEIXIN + RedisConstant.JS_TICKET);
        // 若为空,则从微信服务器获取
        if (StringUtils.isEmpty(ticket)) {
            String accessToken = getAccessToken();
            String requestUrl = ACCESS_TICKET_URL.replace("ACCESS_TOKEN", accessToken);
            String response = HttpUtil.get(requestUrl);
            JSONObject jsonObject = JSONUtil.parseObj(response);
            // 判断微信返回结果是否异常
            if (StringUtils.isEmpty(jsonObject.get(ERRCODE)) || Integer.parseInt(String.valueOf(jsonObject.get(ERRCODE))) == 0) {
                // 持久化到redis中
                redisUtil.set(RedisConstant.WEIXIN + RedisConstant.JS_TICKET, jsonObject.getStr(TICKET), 7000);
                return jsonObject.getStr(TICKET);
            } else {
                logger.error(jsonObject.toStringPretty());
                throw new ServiceException("获取公众号ticket信息异常");
            }
        }
        return ticket;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • SHA1方法
    public static String SHA1(String decript) {
        try {
            MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
            digest.update(decript.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • 完整代码
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.hcframe.base.common.ServiceException;
import com.hcframe.redis.RedisUtil;
import com.repchain.nfr.common.config.RedisConstant;
import com.repchain.nfr.common.config.WeixinConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


@Component
public class WeixinUtil {

    final WeixinConfig weixinConfig;
    final RedisUtil redisUtil;
    private final Logger logger = LoggerFactory.getLogger(WeixinUtil.class);
    public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";
    public final static String ACCESS_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
    public final static String ERRCODE = "errcode";
    public final static String ACCESS_TOKEN = "access_token";
    public final static String TICKET = "ticket";

    public WeixinUtil(WeixinConfig weixinConfig,
                      RedisUtil redisUtil) {
        this.weixinConfig = weixinConfig;
        this.redisUtil = redisUtil;
    }

    /**
     * @return java.lang.String
     * @author lhc
     * @description // 获取微信accessToken
     * @date 2022/8/11 10:29
     * @params []
     **/
    public String getAccessToken() {
        // 从redis获取token
        String token = (String) redisUtil.get(RedisConstant.WEIXIN + RedisConstant.ACCESS_TOKEN);
        // 若redis为空,则需要从微信处获取token
        if (StringUtils.isEmpty(token)) {
            String requestUrl = ACCESS_TOKEN_URL.replace("APPID", weixinConfig.getAppId()).replace("SECRET", weixinConfig.getAppSecret());
            String response = HttpUtil.get(requestUrl);
            JSONObject jsonObject = JSONUtil.parseObj(response);
            // 判断微信返回结果是否异常
            if (StringUtils.isEmpty(jsonObject.get(ERRCODE)) || Integer.parseInt(String.valueOf(jsonObject.get(ERRCODE))) == 0) {
                // 持久化到redis中
                redisUtil.set(RedisConstant.WEIXIN + RedisConstant.ACCESS_TOKEN, jsonObject.getStr(ACCESS_TOKEN), 7000);
                return jsonObject.getStr(ACCESS_TOKEN);
            } else {
                logger.error(jsonObject.toStringPretty());
                throw new ServiceException("获取公众号token信息异常");
            }
        } else {
            return token;
        }
    }

    /**
     * @return cn.hutool.json.JSONObject
     * @author lhc
     * @description // 获取微信jssdk授权
     * @date 2022/8/11 10:30
     * @params []
     **/
    public String jsapiTicket() {
        // 从redis获取ticket
        String ticket = (String) redisUtil.get(RedisConstant.WEIXIN + RedisConstant.JS_TICKET);
        // 若为空,则从微信服务器获取
        if (StringUtils.isEmpty(ticket)) {
            String accessToken = getAccessToken();
            String requestUrl = ACCESS_TICKET_URL.replace("ACCESS_TOKEN", accessToken);
            String response = HttpUtil.get(requestUrl);
            JSONObject jsonObject = JSONUtil.parseObj(response);
            // 判断微信返回结果是否异常
            if (StringUtils.isEmpty(jsonObject.get(ERRCODE)) || Integer.parseInt(String.valueOf(jsonObject.get(ERRCODE))) == 0) {
                // 持久化到redis中
                redisUtil.set(RedisConstant.WEIXIN + RedisConstant.JS_TICKET, jsonObject.getStr(TICKET), 7000);
                return jsonObject.getStr(TICKET);
            } else {
                logger.error(jsonObject.toStringPretty());
                throw new ServiceException("获取公众号ticket信息异常");
            }
        }
        return ticket;
    }

    public static String SHA1(String decript) {
        try {
            MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
            digest.update(decript.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

# 3. 服务端接口

  • controller接口
@RestController
@RequestMapping("/weixintest")
public class WeixinController {

    final WeixinService weixinService;

    public WeixinController(WeixinService weixinService) {
        this.weixinService = weixinService;
    }

    @GetMapping("jssdktest")
    public ResultVO<Object> getJsSdkInfo(String url) {
        return weixinService.getJsSdkInfo(url);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • service实现类,通过微信工具类,构建前端所需要的参数。
import com.hcframe.base.common.ResultVO;
import com.repchain.nfr.common.config.WeixinConfig;
import com.repchain.nfr.common.utils.WeixinUtil;
import com.repchain.nfr.module.weixin.service.WeixinService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


@Service
public class WeixinServiceImpl implements WeixinService {

    final WeixinUtil weixinUtil;
    final WeixinConfig weixinConfig;

    public WeixinServiceImpl(WeixinUtil weixinUtil,WeixinConfig weixinConfig) {
        this.weixinUtil = weixinUtil;
        this.weixinConfig = weixinConfig;
    }
    @Override
    public ResultVO<Object> getJsSdkInfo(String url) {
        if (StringUtils.isEmpty(url)) {
            url = weixinConfig.getJsUrl();
        }
        String ticket = weixinUtil.jsapiTicket();
        // 随机字符串
        String noncestr = UUID.randomUUID().toString().replaceAll("-", "");
        // 时间戳
        String timestamp = String.valueOf(System.currentTimeMillis());
        String str = "jsapi_ticket="+ticket+"&noncestr="+noncestr+"&timestamp="+timestamp+"&url="+url;
        // SHA1签名字符串
        String signature = WeixinUtil.SHA1(str);
        // 返回前端结果
        Map<String,String> map=new HashMap<>(5);
        map.put("timestamp",timestamp);
        map.put("appId",this.weixinConfig.getAppId());
        map.put("noncestr",noncestr);
        map.put("signature",signature);
        return ResultVO.getSuccess(map);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 三、H5前端开发

# 1. 添加微信js-sdk依赖

yarn add weixin-js-sdk
1

# 2. 获取后台的Request

此处可以自行实现,主要目的就是从服务端拉去数据。

import { $get } from "@/common/http";

export const getJsSdkInfo = (url: string) =>
  $get(`/weixintest/jssdktest`, { url }, true);

1
2
3
4
5

# 3. 加载页面后初始化wx.config内容(此处需要放到onLoad中执行)

getConfig() {
  	// 此处为域名
    const url = "https://xxxx.xxx.com/";
  	// 通过服务端获取所需信息
    getJsSdkInfo(url)
      .then(res => {
        this.configInfo = res.data;
        this.wxConfig(
          res.data.appId,
          res.data.timestamp,
          res.data.noncestr,
          res.data.signature
        );
      })
      .catch(() => {
        uni.hideLoading();
        uni.showToast({
          title: "请稍后再试",
          icon: "error"
        });
      });
  }

// 微信的js-sdk初始化
wxConfig(appId: any, timestamp: any, nonceStr: any, signature: any) {
    wx.config({
      debug: false, // 开启调试模式,
      appId: appId, // 必填,企业号的唯一标识
      timestamp: timestamp, // 必填,生成签名的时间戳
      nonceStr: nonceStr, // 必填,生成签名的随机串
      signature: signature, // 必填,签名
      jsApiList: ["scanQRCode", "checkJsApi"] // 必填,需要使用的JS接口列表
    });
    wx.ready(() => {
      uni.hideLoading();
    });
    wx.error(function (res: any) {
      console.log(res);
      uni.hideLoading();
      uni.showToast({
        title: "请使用微信打开,或稍后再试",
        icon: "error"
      });
    });
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 4. 调用微信扫码功能

 scanClick() {
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const that = this;
    wx.scanQRCode({
      needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
      scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是一维码,默认二者都有
      success: function (res: any) {
        console.log(res.resultStr); //返回结果
      },
      fail: function () {
        uni.showToast({
          title: "识别二维码失败!",
          duration: 2000,
          icon: "none"
        });
      }
    });
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 5. 完整代码

<template>
  <view>
    <view class="auth-icon-bind">
      <view>
        <u-icon name="scan" size="200" color="#50416f"></u-icon>
      </view>
    </view>
    <view class="bind-title">
      <uni-title
        type="h1"
        title="请扫描二维码绑定藏品"
        align="center"
        color="#50416f"
      ></uni-title>
    </view>
    <view class="auth-button">
      <button class="btn-logout" @click="scanClick">点击扫描二维码</button>
    </view>
  </view>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import Setting from "@/settings";
import Settings from "@/settings";
import wx from "weixin-js-sdk";
import { getJsSdkInfo } from "@/api/wxApi";
import { isInWeixin } from "@/utils/agent";
import { router } from "@/router/router";

@Component({
  name: "BlockList",
  components: { hcPass }
})
export default class extends Vue {
  theme = Setting.theme;
  showBindView = false;
  tranJson = "";
  result = "";
  error = "";

  onLoad() {
    uni.showLoading({ title: "加载中" });
  }

  getConfig() {
    const url = "https://scicollection.las.ac.cn/";
    getJsSdkInfo(url)
      .then(res => {
        this.configInfo = res.data;
        this.wxConfig(
          res.data.appId,
          res.data.timestamp,
          res.data.noncestr,
          res.data.signature
        );
      })
      .catch(() => {
        uni.hideLoading();
        uni.showToast({
          title: "请稍后再试",
          icon: "error"
        });
      });
  }

  wxConfig(appId: any, timestamp: any, nonceStr: any, signature: any) {
    wx.config({
      debug: false, // 开启调试模式,
      appId: appId, // 必填,企业号的唯一标识
      timestamp: timestamp, // 必填,生成签名的时间戳
      nonceStr: nonceStr, // 必填,生成签名的随机串
      signature: signature, // 必填,签名
      jsApiList: ["scanQRCode", "checkJsApi"] // 必填,需要使用的JS接口列表
    });
    wx.ready(() => {
      uni.hideLoading();
    });
    wx.error(function (res: any) {
      console.log(res);
      uni.hideLoading();
      uni.showToast({
        title: "请使用微信打开,或稍后再试",
        icon: "error"
      });
    });
  }

  onShow() {
    // 判断是否为微信环境
    if (isInWeixin()) {
      this.getConfig();
    } else {
      uni.hideLoading();
      uni.showModal({
        title: "提示",
        content: "请在微信中打开网站,使用扫一扫功能",
        confirmColor: Settings.theme,
        success: function () {
          router.pushTab("/xxxx");
        }
      });
    }
  }

  scanClick() {
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const that = this;
    wx.scanQRCode({
      needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
      scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是一维码,默认二者都有
      success: function (res: any) {
      	console.log(res.resultStr); //返回结果
      },
      fail: function () {
        uni.showToast({
          title: "识别二维码失败!",
          duration: 2000,
          icon: "none"
        });
      }
    });
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

# 参考内容

https://blog.csdn.net/qq_40230848/article/details/123003012

https://blog.csdn.net/acitylion/article/details/108981375?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-1-108981375-blog-124504330.pc_relevant_multi_platform_whitelistv3&spm=1001.2101.3001.4242.2&utm_relevant_index=4

编辑
#前端#Vue#uniapp
上次更新: 2023/03/24, 08:53:10
Vue typescript 如何极限压缩编译静态资源

← Vue typescript 如何极限压缩编译静态资源

最近更新
01
IDEA行号太宽
03-11
02
Docker安装Rabbitmq
07-22
03
OpenWrt 路由器如何让 lan 口主机获得 ipv6 网络访问
04-19
更多文章>
本站总访问量次 | 您是本站第位访问者
Theme by Vdoing | Copyright © 2020-2024 Lake Liu | MIT License | 背景图、Logo、头像设计@Drrizzee
  • 跟随系统
  • 深色模式
  • 浅色模式
  • 阅读模式