微服务springboot

【第3期】Springboot:微信公众号分享"朋友圈"

2020-04-19  本文已影响0人  村中一少

原创文章,转载请注明原文章地址,谢谢!

第一步:vue端

1)App.vue类

 import sdk from './router/share'; // 引入sdk.
  
    mounted() {
        /* eslint-disable */
        const url = location.href.split('#')[0];
        console.log(url)
        const dataForWeixin = {
          title: '分享标题',    // 分享标题
          desc: '分享内容',            // 分享内容
          linkurl: "http://nhh.ngrok.ibanzhuan.cn", // 分享链接
          img: 'http://show.1m2.net/sj/static/img/sharre.jpg',        // 分享内容显示的图片(图片必须是正方形的链接)
        };
        const ua = window.navigator.userAgent.toLowerCase();
        if (ua.match(/MicroMessenger/i) === 'micromessenger') {
          sdk.getJSSDK(url,dataForWeixin);           //传入sdk.js需要的参数
        } else {
          console.log('不是微信浏览器')
          sdk.getJSSDK(url,dataForWeixin);
        }
      },

其中,title、desc、linkurl、img这四个为分享的设置,linkurl应动态获取当前前端页面地址

const dataForWeixin = {
          title: '分享标题',    // 分享标题
          desc: '分享内容',            // 分享内容
          linkurl: "http://192.168.0.124", // 分享链接
          img: 'http://show.1m2.net/sj/static/img/sharre.jpg',        // 分享内容显示的图片(图片必须是正方形的链接)
        };

2)share.js

import wx from 'weixin-js-sdk'
import axios from 'axios';
// 要用到微信API
function getJSSDK(url, dataForWeixin) {
  // 调用后台接口换取参数
  axios.get('http://nhc.ngrok.ibanzhuan.cn/app/wechat/initJSSDKConfig', {
    params: {
      url,
    },
  }).then((res) => {
    console.log(res)
    wx.config({
      debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
      appId: res.data.map.appid, // 必填,公众号的唯一标识
      timestamp: res.data.map.timestamp, // 必填,生成签名的时间戳
      nonceStr: res.data.map.nonceStr, // 必填,生成签名的随机串
      signature: res.data.map.signature, // 必填,签名
      jsApiList: ['onMenuShareAppMessage', 'onMenuShareTimeline', 'updateAppMessageShareData', 'updateTimelineShareData','onMenuShareQQ','onMenuShareWeibo'] // 必填,需要使用的JS接口列表
    });
    wx.ready(function () {
      wx.onMenuShareAppMessage({
          title: dataForWeixin.title,
          desc: dataForWeixin.desc,
          link: dataForWeixin.linkurl,
          imgUrl: dataForWeixin.img,
          success: function () { 
              // 用户确认分享后执行的回调函数
              alert('分享成功');
          },
          cancel: function () { 
              // 用户取消分享后执行的回调函数
          }
      });
      wx.error(function(res){
          // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
          alert("errorMSG:"+res);
      });
    });
  });
}
export default {
  // 获取JSSDK
  getJSSDK,
}

其中,接口

http://nhc.ngrok.ibanzhuan.cn/app/wechat/initJSSDKConfig 为后端地址获取公众号基本配置的接口,在第二步讲述。

第二步:springboot接口

Controller类

package com.qh.app.controller;
​
import com.qh.app.annotation.CompanyNum;
import com.qh.app.service.WechatService;
import com.qh.app.utils.WechatUtil;
import com.qh.common.response.BaseResponse;
import com.qh.common.utils.AgrLogUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
​
import java.util.Map;
​
/**
 * @Author :nhchun
 * @Date: 2020/4/16 9:01
 * @Desc:
 **/
@CrossOrigin
@RestController
@RequestMapping("/app/wechat")
public class AppWechatController {
    public final static Logger logger = AgrLogUtils.getLogger(AppWechatController.class);
    @Autowired
    WechatService wechatService;
    /**
     * 获取公众号接入的初始配置
     * @param url
     * @param companyNum
     * @return
     */
    @GetMapping("/initJSSDKConfig")
    public BaseResponse initJSSDKConfig(@RequestParam(name="url")String url,@CompanyNum String companyNum) {
        Map map = wechatService.initJSSDKConfig(url, companyNum);
        return BaseResponse.ok().put("map",map);
    }
}
​

Service类

这里我把公众号的一些参数appid、appsecret、jssdk_accesstoken_url、jssdk_getticket_url配置到数据库中了,当然也可以放置在属性文件application.yml中。

package com.qh.app.service.impl;
​
import com.qh.app.repository.AppConfigRepository;
import com.qh.app.service.WechatService;
import com.qh.app.utils.WechatUtil;
import com.qh.common.entity.ConfigEntity;
import com.qh.common.exception.BizException;
import com.qh.common.exception.ResultEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
​
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
​
/**
 * @Author :nhchun
 * @Date: 2020/4/16 15:08
 * @Desc:
 **/
@Service
public class WechatServiceImpl implements WechatService {
    @Autowired
    AppConfigRepository configRepository;
​
    @Override
    public Map initJSSDKConfig(String url,String companyNum) {
        Map<String,String> map = new HashMap<String,String>();
        try {
            //1 获取数据库配置
            ConfigEntity configEntity = getConfigEntity(companyNum);
​
            //2 获取AccessToken
            String accessToken = WechatUtil.getJSSDKAccessToken(configEntity.getJssdkAccesstokenUrl(),
                    configEntity.getAppID(),configEntity.getAppsecret());
            //3 获取JssdkGetticket
            String jsapiTicket = WechatUtil.getJssdkGetticket(configEntity.getJssdkGetticketUrl(),
                    accessToken);
            //4 签名
            String timestamp = Long.toString(System.currentTimeMillis() / 1000);
            String nonceStr = UUID.randomUUID().toString();
            String signature = WechatUtil.buildJSSDKSignature(jsapiTicket, timestamp, nonceStr, url);
​
            //5 返回
            map.put("url", url);
            map.put("jsapi_ticket", jsapiTicket);
            map.put("nonceStr", nonceStr);
            map.put("timestamp", timestamp);
            map.put("signature", signature);
            map.put("appid", configEntity.getAppID());
        }catch(Exception e){
            throw  new BizException(ResultEnum.FAIL);
        }
        return map;
    }
​
    private ConfigEntity getConfigEntity(String companyNum) {
        ConfigEntity configEntity = configRepository.queryByCompanyNum(companyNum);
        if (configEntity == null) {
            throw new BizException(ResultEnum.COMPANYNUM_NO_CONFIG);
        }
        return configEntity;
    }
}

WechatUtil工具类

package com.qh.app.utils;
​
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.qh.common.utils.RedisUtil;
import com.qh.common.utils.StringUtils;
import me.chanjar.weixin.common.util.crypto.SHA1;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
​
import java.security.MessageDigest;
import java.util.Map;
​
/**
 * @Author :nhchun
 * @Date: 2020/4/16 9:11
 * @Desc:
 **/
@Component
public class WechatUtil {
    public static final String TOKEN ="sckt";
​
    public static boolean validParams(String signature,String timestamp,String nonce){
        try {
            return SHA1.gen(new String[]{TOKEN, timestamp, nonce}).equals(signature);
        } catch (Exception var5) {
            return false;
        }
    }
​
    /**
     * 获取access_token
     * @return
     */
    public static String getJSSDKAccessToken(String JssdkAccesstokenUrl,String AppId,String AppSecret) {
        String token = null;
​
        //1 去緩存取
        token= RedisUtil.getString("ACESSTOKEN"+AppId);
        if(!StringUtils.isNullOrEmpty(token)){
            return token;
        }
        //2 重新获取
        String url = JssdkAccesstokenUrl.replaceAll("APPID",
                AppId).replaceAll("APPSECRET",
                AppSecret);
        String json = postRequestForWeiXinService(url);
        Map map = jsonToMap(json);
        if (map != null) {
            token = (String) map.get("access_token");
        }
        RedisUtil.setString("ACESSTOKEN"+AppId,token,7200);
        return token;
    }
​
    public static String getJssdkGetticket(String JssdkGetticketUrl,String accessToken) {
        String jsapi_ticket = null;
        jsapi_ticket= RedisUtil.getString("jsapi_ticket"+accessToken);
        if(!StringUtils.isNullOrEmpty(jsapi_ticket)){
            return jsapi_ticket;
        }
​
        String url = JssdkGetticketUrl.replaceAll("ACCESS_TOKEN", accessToken);
        String json = postRequestForWeiXinService(url);
        Map map = jsonToMap(json);
​
        if (map != null) {
            jsapi_ticket = (String) map.get("ticket");
        }
        RedisUtil.setString("jsapi_ticket"+accessToken,jsapi_ticket,7200);
        return jsapi_ticket;
    }
​
    /**
     *@Author: nhc
     *@CreateTime: 21:41 2020/2/14
     *@param:ticket 根据accessToken生成的JssdkGetticket
     *@param:timestamp 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
     *@param:nonceStr 随机字符串
     *@param:url 当前网页的URL
     *@Description: 构建分享链接的签名
​
     */
    public static String buildJSSDKSignature(String ticket,String timestamp,String nonceStr ,String url) throws Exception {
        String orderedString = "jsapi_ticket=" + ticket
                + "&noncestr=" + nonceStr + "&timestamp=" + timestamp
                + "&url=" + url;
​
        return sha1(orderedString);
    }
​
    /**
     * sha1 加密JSSDK微信配置参数获取签名。
     *
     * @return
     */
    public static String sha1(String orderedString) throws Exception {
        String ciphertext = null;
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] digest = md.digest(orderedString.getBytes());
        ciphertext = byteToStr(digest);
        return ciphertext.toLowerCase();
    }
    /**
     * 将字节数组转换为十六进制字符串
     *
     * @param byteArray
     * @return
     */
    private static String byteToStr(byte[] byteArray) {
        String strDigest = "";
        for (int i = 0; i < byteArray.length; i++) {
            strDigest += byteToHexStr(byteArray[i]);
        }
        return strDigest;
    }
    /**
     * 将字节转换为十六进制字符串
     * @param mByte
     * @return
     */
    private static String byteToHexStr(byte mByte) {
        char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        char[] tempArr = new char[2];
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
        tempArr[1] = Digit[mByte & 0X0F];
​
        String s = new String(tempArr);
        return s;
    }
    /**
     *@Author: nhc
     *@CreateTime: 21:49 2020/2/14
     *@param:  map
     *@Description:  mapToJson
     */
    public static String mapToJson(Map map){
        Gson gson = new Gson();
        String json = gson.toJson(map);
        return json;
    }
​
    /**
     *@Author:nhc
     *@CreateTime: 21:37 2020/2/14
     *@param:  json
     *@Description: jsonToMap
     */
    private static Map jsonToMap(String json) {
        Gson gons = new Gson();
        Map map = gons.fromJson(json, new TypeToken<Map>(){}.getType());
        return map;
    }
​
    /**
     *@Author: nhchun
     *@CreateTime: 21:36 2020/2/14
     *@param:  * @param null
     *@Description: 调取微信接口
     */
    private static String postRequestForWeiXinService(String getAccessTokenUrl) {
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> postForEntity = restTemplate.postForEntity(getAccessTokenUrl, null, String.class);
        String json = postForEntity.getBody();
        return json;
    }
}

这个工具类中access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值,我把它放在redis缓存中了。

分享成功如下

image

当然,调试过程中,可能还会遇到其它的问题,后续会补充。。。

说明:关注微信公众号【村中一少】,回复“电子书”按照提示信息获取对应书籍目录序号,如回复【1】,即可获得电子书【研磨设计模式.陈臣.王斌.扫描版.pdf】下载地址,所有电子均为免费。

这些电子书仅仅用于学习,如果喜欢请购买正版图书!请支持原版作者!

上一篇 下一篇

猜你喜欢

热点阅读