JAVA实现微信公众号扫一扫

准备环境

1.微信测试公众号申请:

微信公众平台http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

2.记录下申请的测试公众号的appid和appsecret

3.配置内网穿透:本篇使用的是natapp NATAPP-内网穿透 基于ngrok的国内高速内网映射工具

设置本机的内网穿透代理,使用免费版。

4.配置js安全域名:其中js安全域名的设置不能携带http://。

5.扫码关注测试公众号:因为测试公众号,在手机端测试时要关注公众号。

 开发环境

1.vue前端框架

2.jdk 1.8

3.springboot

后端代码

1.后端代码逻辑主要是:根据appid及appsecret获取token,再根据token获取jsapi_ticket,然后再根据jsapi_ticket,noncestr、timestamp、url拼接成字符串,然后加密生成签名,然后将url(调用扫一扫的页面地址)、jsapi_ticket、nonceStr、timestamp、signature、appId封装后传给前端进行微信验证。

首先微信获取token实体类

 http工具类

import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class HttpClient {
    public static String doGet(String url, Map<String, String> param) {

        // 创建Httpclient对象
        CloseableHttpClient httpclient = HttpClients.createDefault();

        String resultString = "";
        CloseableHttpResponse response = null;
        try {
            // 创建uri
            URIBuilder builder = new URIBuilder(url);
            if (param != null) {
                for (String key : param.keySet()) {
                    builder.addParameter(key, param.get(key));
                }
            }
            URI uri = builder.build();

            // 创建http GET请求
            HttpGet httpGet = new HttpGet(uri);

            // 执行请求
            response = httpclient.execute(httpGet);
            // 判断返回状态是否为200
            if (response.getStatusLine().getStatusCode() == 200) {
                resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                httpclient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return resultString;
    }

    public static String doGet(String url) {
        return doGet(url, null);
    }

    public static String doPost(String url, Map<String, String> param) {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
            // 创建参数列表
            if (param != null) {
                List<NameValuePair> paramList = new ArrayList<>();
                for (String key : param.keySet()) {
                    paramList.add(new BasicNameValuePair(key, param.get(key)));
                }
                // 模拟表单
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
                httpPost.setEntity(entity);
            }
            // 执行http请求
            response = httpClient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return resultString;
    }

    public static String doPost(String url) {
        return doPost(url, null);
    }

    public static String doPostJson(String url, String json) {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
            // 创建请求内容
            StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
            httpPost.setEntity(entity);
            // 执行http请求
            response = httpClient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return resultString;
    }
}

此工具主要用于请求微信链接获取数据

微信工具类-获取accessToken及获取jsApiTicket

import cn.px.base.pojo.page.LayuiPageFactory;
import cn.px.blh.core.wx.WxTokenEntity;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

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

@Component
public class WxUtil {

    //获取token
    private static final String TOKEN_URL  = "https://api.weixin.qq.com/cgi-bin/token";
    //微信APPID
    public static String APP_ID;
    //微信秘钥
    private static String SECRET;

    //微信公众号token在redis中存储时的key值
    public static final String GZH_TOKEN = "wxgzh-access-token";

    //获取JSAPI_TICKET
    private static final  String JSAPI_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";

    //微信公众号jsapi_ticket在redis中存储的key值
    public static final String JSAPI_KEY = "wxgzh-jsapi-ticket";
    //JSAPI_KEY在redis中的失效时间
    public static final Integer JSAPI_KEY_EXPIRED_TIME = 7000;


    @Value("${wx.appId}")
    public void setAppId(String appId) {
        WxUtil.APP_ID = appId;
    }

    @Value("${wx.secret}")
    public void setSecret(String secret) {
        WxUtil.SECRET = secret;
    }
    /**
     * 获取微信token
     * @return
     */
    public static WxTokenEntity getAccessToken() {

        Map<String, String> param = new HashMap<>(16);
        param.put("grant_type", "client_credential");
        param.put("appid", APP_ID);
        param.put("secret", SECRET);
        String wxResult = HttpClient.doGet(TOKEN_URL,param);
        WxTokenEntity model = JSON.toJavaObject(JSONObject.parseObject(wxResult),WxTokenEntity.class);
        return model;
    }

    /**
     * 获得jsapi_ticket
     */
    public static String getJsApiTicket(String token) {
        String url = JSAPI_TICKET
                + "?access_token=" + token
                + "&type=jsapi";

        String response = HttpClient.doGet(url);
        if (StringUtils.isBlank(response)) {
            return null;
        }
        JSONObject jsonObject = JSONObject.parseObject(response);
        String ticket = jsonObject.getString("ticket");
        return ticket;
    }

}

根据url来获取js_api_ticket然后返回结果的实现类

import cn.px.base.util.CacheUtil;
import cn.px.blh.core.util.WxUtil;
import cn.px.blh.core.wx.WxTokenEntity;
import cn.stylefeng.roses.core.util.ToolUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Service
public class WxJsApiService {

    protected Logger logger = LogManager.getLogger();

    /**
     * 获取签名
     * @param url
     * @return
     */
    public Map<String, String> sign(String url) {
        Map<String, String> resultMap = new HashMap<>(16);
        //这里的jsapi_ticket是获取的jsapi_ticket。
        String jsapiTicket = this.getJsApiTicket();
        //这里签名中的nonceStr要与前端页面config中的nonceStr保持一致,所以这里获取并生成签名之后,还要将其原值传到前端
        if(ToolUtil.isEmpty(jsapiTicket)){
            return resultMap;
        }
        String nonceStr = createNonceStr();
        //nonceStr
        String timestamp = createTimestamp();
        String string1;
        String signature = "";

        //注意这里参数名必须全部小写,且必须有序
        string1 = "jsapi_ticket=" + jsapiTicket +
                "&noncestr=" + nonceStr +
                "&timestamp=" + timestamp +
                "&url=" + url;
        System.out.println("string1:"+string1);

        try
        {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        }
        catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }
        resultMap.put("url", url);
        resultMap.put("jsapi_ticket", jsapiTicket);
        resultMap.put("nonceStr", nonceStr);
        resultMap.put("timestamp", timestamp);
        resultMap.put("signature", signature);
        resultMap.put("appId", WxUtil.APP_ID);
        return resultMap;
    }

    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    private static String createNonceStr() {
        return UUID.randomUUID().toString();
    }

    private static String createTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

    public String getJsApiTicket(){
        String ticket = (String) CacheUtil.getCache().get(WxUtil.JSAPI_KEY);
        if(ToolUtil.isEmpty(ticket)){
            if(ToolUtil.isEmpty(getToken())){
                logger.error(String.format("获取token出错"));
            }else{
                ticket = WxUtil.getJsApiTicket(getToken());
                CacheUtil.getCache().set(WxUtil.JSAPI_KEY,ticket,WxUtil.JSAPI_KEY_EXPIRED_TIME);
            }
        }
        return ticket;
    }

    private String getToken(){
        //此处获取token先判断缓存中是否存在,如果没有则调取放入缓存
        String token = (String) CacheUtil.getCache().get(WxUtil.GZH_TOKEN);
        if(ToolUtil.isEmpty(token)){
            WxTokenEntity wxTokenEntity = WxUtil.getAccessToken();
            if(ToolUtil.isEmpty(wxTokenEntity.getAccess_token())){
                //获取token出错
                logger.error(String.format("获取token出错,错误编码:{%s};错误提示:{%s}",wxTokenEntity.getErrcode(),wxTokenEntity.getErrmsg()));
            }else{
                token = wxTokenEntity.getAccess_token();
                CacheUtil.getCache().set(WxUtil.GZH_TOKEN,token,Integer.parseInt(wxTokenEntity.getExpires_in()));
            }

        }
        return token;
    }

}

其中token及jsApiTicket需放入缓存中,防止过多调用微信链接造成资源浪费,缓存工具类大家可以根据自己项目来实现,这里不再贴出。

Controller控制类

/**
     * 获取微信公众号信息
     */
    @ApiOperation("获取微信公众号信息")
    @PostMapping(value = "/getSign")
    @ResponseBody
    public Object scanJsApi(@Param("tokenUrl") String tokenUrl) {
        Map<String, String> res = wxJsApiService.sign(tokenUrl);
        if(ToolUtil.isEmpty(res)){
            return setModelMap(HttpCode.BAD_REQUEST,"获取公众号信息失败");
        }else{
            return super.setSuccessModelMap(res);
        }

    }

vue前端代码

1.首先使用到微信的wexin-jsapi:

        安装:npm install weixin-jsapi --save

        页面引入:import wx from "weixin-jsapi";

2.初始化微信参数

 //初始化微信参数
      wxLoadConfig:function(){
        var vm = this;
        return new Promise((resolve, reject) => {
          // alert(window.location.href.split("#")[0]);
          vm.$root.api.getSign(window.location.href.split("#")[0]).then(res => {
            //获取数据
            if (res.httpCode == 200 || res.httpCode == '200') {
              var timestamp = res.data.timestamp;
              var noncestr = res.data.nonceStr;
              var signature = res.data.signature;
              var appId = res.data.appId;
              wx.config({
                debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                //                                debug : true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                appId: appId, // 必填,公众号的唯一标识
                timestamp: timestamp, // 必填,生成签名的时间戳
                nonceStr: noncestr, // 必填,生成签名的随机串
                signature: signature, // 必填,签名
                jsApiList: [
                  "scanQRCode"
                ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
              });
              wx.checkJsApi({
                jsApiList:['scanQRCode'],
                success:function (res) {
                      alert(res);
                }
              });
            } else {

            }
            resolve();
          }).catch(err => {
            reject(err);
          })
        })
      },

3.调取扫一扫

    //调取扫一扫
      takeScan:function(){
        wx.ready(function() {
          wx.scanQRCode({
            needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
            scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是一维码,默认二者都有
            success: function(res) {
              var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
              alert(res);
            },
            error: function(res) {
              alert(res);
            }
          });
        });
      },

vue前端页面代码

/* eslint-disable no-undef */
<!--样式引入-->


<!--模版引入-->
<template>
  <div>
    <div>
      <input type="button" value="调取扫一扫" @click="takeScan"/>
    </div>
  </div>
</template>

<script>
  import wx from "weixin-jsapi";
  // 在man.js中注册 weixin-jsapi
  export default {
    name: 'index',
    data() {
      return {

      }
    },
    watch: {
      '$route'(to, from) {
        // 更新当前页面导航

      }
    },
    components: {},
    mounted: function() {
      this.wxLoadConfig();
    },
    methods: {
      //绑定证件,跳转绑定证件页面
      bindCertificate:function(){
        this.$router.push('/bind');
      },
      //初始化微信参数
      wxLoadConfig:function(){
        var vm = this;
        return new Promise((resolve, reject) => {
          // alert(window.location.href.split("#")[0]);
          vm.$root.api.getSign(window.location.href.split("#")[0]).then(res => {
            //获取数据
            if (res.httpCode == 200 || res.httpCode == '200') {
              var timestamp = res.data.timestamp;
              var noncestr = res.data.nonceStr;
              var signature = res.data.signature;
              var appId = res.data.appId;
              wx.config({
                debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                //                                debug : true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                appId: appId, // 必填,公众号的唯一标识
                timestamp: timestamp, // 必填,生成签名的时间戳
                nonceStr: noncestr, // 必填,生成签名的随机串
                signature: signature, // 必填,签名
                jsApiList: [
                  "scanQRCode"
                ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
              });
              wx.checkJsApi({
                jsApiList:['scanQRCode'],
                success:function (res) {
                      alert(res);
                }
              });
            } else {

            }
            resolve();
          }).catch(err => {
            reject(err);
          })
        })
      },
      //调取扫一扫
      takeScan:function(){
        wx.ready(function() {
          wx.scanQRCode({
            needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
            scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是一维码,默认二者都有
            success: function(res) {
              var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
              alert(res);
            },
            error: function(res) {
              alert(res);
            }
          });
        });
      },



    }
  };
</script>

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值