微信分享自定义链接

  • 目录
    • 问题与需求
    • 准备
    • 公众号设置
    • 代码
      • 后端代码(完整代码在附录)
        • application.properties文件配置
        • (1)获取accessToken
        • (2)获取jsapiTicket
        • (3)生成签名
        • 生成wx.config
        • 接口返回wx.config
      • 前端代码(完整代码在附录)
    • 测试
    • 附录
      • 后端完整代码
        • 微信工具类.java
        • 网络工具类.java
        • controler交互
        • js
      • 常见问题
        • 获取JSSDK权限成功但分享无效果
        • 本地调试无效果
        • 微信页面缓存

目录

问题与需求

问题:

需求:

准备

认证的公众号或者服务号(记住开发者ID、开发者密码)

备案过的域名

公众号设置

设置-开发-基本配置-IP白名单-添加网站服务器IP地址
设置-公众号设置-功能设置-设置业务域名与JS接口安全域名(即网站的域名)(这里需要加入认证TXT文件到项目)

代码

后端代码(完整代码在附录)

根据官方文档,可以得知大步骤为->绑定域名->引入JS文件->通过config接口注入权限验证配置。

也就是说后端只要返回wx.config所需参数即可。

wx.config({debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。appId: '', // 必填,公众号的唯一标识timestamp: , // 必填,生成签名的时间戳nonceStr: '', // 必填,生成签名的随机串signature: '',// 必填,签名jsApiList: [] // 必填,需要使用的JS接口列表
});

除了签名,其他参数都可直接获取或生成,查阅官方签名文档,可知需要先生成jsapi_ticket,然后再根据文档需求生成签名。

根据文档可知生成签名步骤:获取accessToken -> 生成jsapi_ticket ->签名算法 -> sha1 加密

application.properties文件配置

#微信配置
WX_APPID=xxxx          #appId
WX_APPSECRET=xxxxx #密钥
WX_GRANTTYPE=client_credential

(1)获取accessToken

@Scheduled(initialDelay = 1000, fixedDelay = 100*60*1000)public String getAccessToken() {Map<String,String> resultMap=new HashMap<>();//获取access_tokenString url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}";//设置参数Map<String, Object> map = new HashMap<>();map.put("appid", WX_APPID);map.put("secret", WX_APPSECRET);//发送get请求String accessTokenResult = ((Map<String, String>) JSON.parse(httpUtil.GETclient(url, map))).get("access_token");tokenAll = accessTokenResult;return accessTokenResult;}

(2)获取jsapiTicket

@Scheduled(initialDelay = 1000, fixedDelay = 100*60*1000)public String getJsapiTicket() {Map<String,Object> map=new HashMap<>();String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={ACCESS_TOKEN}&type=jsapi";//设置参数map.put("ACCESS_TOKEN", getAccessToken());//发送get请求String ticket = ((Map<String, String>) JSON.parse(httpUtil.GETclient(url, map))).get("ticket");ticketAll = ticket;return ticket;}

(3)生成签名

这个方法是参考官方的签名算法文档

/*** 签名* @return*/public Map<String, String> sign(String jsapi_ticket, String url){Map<String,String> ret = new HashMap<String, String>();String nonce_str = create_nonce_str();String timestamp = create_timestamp();String stringAppend;String signature = "";//注意这里参数名必须全部小写,且必须有序stringAppend = "jsapi_ticket=" + jsapi_ticket +"&noncestr=" + nonce_str +"&timestamp=" + timestamp +"&url=" + url;System.out.println(stringAppend);try{MessageDigest crypt = MessageDigest.getInstance("SHA-1");crypt.reset();crypt.update(stringAppend.getBytes("UTF-8"));signature = byteToHex(crypt.digest());}catch (NoSuchAlgorithmException e){e.printStackTrace();}catch (UnsupportedEncodingException e){e.printStackTrace();}ret.put("url", url);ret.put("jsapi_ticket", jsapi_ticket);ret.put("nonceStr", nonce_str);ret.put("timestamp", timestamp);ret.put("signature", signature);ret.put("appId", WX_APPID);return ret;}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;}
/*** 随机字符串*/public String create_nonce_str() {return UUID.randomUUID().toString();}/*** 时间戳* @return*/private static String create_timestamp() {return Long.toString(System.currentTimeMillis() / 1000);}

生成wx.config

public Map<String,String> wxConfig(String url) {String jsapi_ticket = ticketAll;// 注意 URL 一定要动态获取,不能 hardcodeMap<String, String> ret = sign(jsapi_ticket, url);for (Map.Entry entry : ret.entrySet()) {System.out.println(entry.getKey() + ", " + entry.getValue());}return ret;}

接口返回wx.config

/*** 获取wechatConfig信息* @param url   访问页面的地址* @return*/@GetMapping("/getWechatConfig")public Map<String,String> getWechatConfig(String url){return wechatUtil.wxConfig(url);}

前端代码(完整代码在附录)

分享接口文档
引入JS文件

<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js" type="text/javascript"></script>

JS代码

//将url 存进缓存下
var url = "https://" + window.location.host; //服务器的url
var urlCureent = encodeURIComponent(location.href); //当前页面的url
$.ajax({async:true,dataType:"json",type:"GET",url: url + "/verification/getWechatConfig?url=" + urlCureent,contentType: "application/json; charset=utf-8",success:function(data){wx.config({debug: 0,appId: data.appId,timestamp: data.timestamp,nonceStr: data.nonceStr,signature: data.signature,jsApiList: ["updateAppMessageShareData", "updateTimelineShareData","onMenuShareTimeline","onMenuShareAppMessage"]});wx.ready(function () {   //需在用户可能点击分享按钮前就先调用wx.updateAppMessageShareData({title: cardVo.cardName + "个人名片", // 分享标题desc: cardVo.cardNameEn + "'s card", // 分享描述link: location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享图标success: function () {console.log("success");// 设置成功}});wx.updateTimelineShareData({title: cardVo.cardName + "个人名片", // 分享标题link: location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享图标success: function () {console.log("success2");// 设置成功}});wx.onMenuShareTimeline({title: cardVo.cardName + "个人名片", // 分享标题desc: cardVo.cardNameEn + "'s card", // 分享描述link: location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享图标});wx.onMenuShareAppMessage({title: cardVo.cardName + "个人名片", // 分享标题link: location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享图标});});}});

测试


可以看到已经成功获取JSSDK 权限

附录

后端完整代码

微信工具类.java

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.Formatter;/*** @program:gzdm* @author:wihenne* @creatTime:2021/05/05**/
@Component
@PropertySource({"classpath:application.properties"})
public class WechatUtil {@Value("${WX_APPID}")String WX_APPID;@Value("${WX_APPSECRET}")String WX_APPSECRET;@Value("${WX_GRANTTYPE}")String WX_GRANTTYPE;@AutowiredHttpUtil httpUtil;public static String tokenAll;            //微信公众号的accessToken对象,由于请求次数有限制,这里使用全局静态变量保存起来public static String ticketAll;//使用全局静态变量存储ApiTicket对象,当然如果使用缓存框架保存当然更好,这边只是做一个简单示例//用于下面返回随机字符串的函数private final static String string = "0123456789";final private static char[] chars = string.toCharArray();/*** 获取公众号的ACCESS_TOKEN** @return string*///刷新access_token 100分钟刷新一次,服务器启动的时候刷新一次(access_token有效期是120分钟,我设置的是每100分钟刷新一次)@Scheduled(initialDelay = 1000, fixedDelay = 100*60*1000)public String getAccessToken() {Map<String,String> resultMap=new HashMap<>();//获取access_tokenString url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}";//设置参数Map<String, Object> map = new HashMap<>();map.put("appid", WX_APPID);map.put("secret", WX_APPSECRET);//发送get请求String accessTokenResult = ((Map<String, String>) JSON.parse(httpUtil.GETclient(url, map))).get("access_token");tokenAll = accessTokenResult;return accessTokenResult;}/*** 获取jsapiTicket** @return map*/@Scheduled(initialDelay = 1000, fixedDelay = 100*60*1000)public String getJsapiTicket() {Map<String,Object> map=new HashMap<>();String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={ACCESS_TOKEN}&type=jsapi";//设置参数map.put("ACCESS_TOKEN", getAccessToken());//发送get请求String ticket = ((Map<String, String>) JSON.parse(httpUtil.GETclient(url, map))).get("ticket");ticketAll = ticket;return ticket;}/*** 生成wx.config** @return map*/public Map<String,String> wxConfig(String url) {String jsapi_ticket = ticketAll;// 注意 URL 一定要动态获取,不能 hardcodeMap<String, String> ret = sign(jsapi_ticket, url);for (Map.Entry entry : ret.entrySet()) {System.out.println(entry.getKey() + ", " + entry.getValue());}return ret;}/*** 签名* @return*/public Map<String, String> sign(String jsapi_ticket, String url){Map<String,String> ret = new HashMap<String, String>();String nonce_str = create_nonce_str();String timestamp = create_timestamp();String stringAppend;String signature = "";//注意这里参数名必须全部小写,且必须有序stringAppend = "jsapi_ticket=" + jsapi_ticket +"&noncestr=" + nonce_str +"&timestamp=" + timestamp +"&url=" + url;System.out.println(stringAppend);try{MessageDigest crypt = MessageDigest.getInstance("SHA-1");crypt.reset();crypt.update(stringAppend.getBytes("UTF-8"));signature = byteToHex(crypt.digest());}catch (NoSuchAlgorithmException e){e.printStackTrace();}catch (UnsupportedEncodingException e){e.printStackTrace();}ret.put("url", url);ret.put("jsapi_ticket", jsapi_ticket);ret.put("nonceStr", nonce_str);ret.put("timestamp", timestamp);ret.put("signature", signature);ret.put("appId", WX_APPID);return ret;}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;}//Sha1加密public static String getSha1(String str){if(str==null||str.length()==0){return null;}char hexDigits[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};try {MessageDigest mdTemp = MessageDigest.getInstance("SHA1");mdTemp.update(str.getBytes("UTF-8"));byte[] md = mdTemp.digest();int j = md.length;char buf[] = new char[j*2];int k = 0;for (int i = 0; i < j; i++) {byte byte0 = md[i];buf[k++] = hexDigits[byte0 >>> 4 & 0xf];buf[k++] = hexDigits[byte0 & 0xf];}return new String(buf);} catch (Exception e) {return null;}}/*** 随机字符串*/public String create_nonce_str() {return UUID.randomUUID().toString();}/*** 时间戳* @return*/private static String create_timestamp() {return Long.toString(System.currentTimeMillis() / 1000);}
}

网络工具类.java

import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UrlPathHelper;import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.Map;/*** @program:gzdm* @author:wihenne* @creatTime:2021/05/06**/
@Component
public class HttpUtil {//发起GET请求public String GETclient(String url, Map<String, Object> map) {RestTemplate restTemplate = new RestTemplate();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);String result = restTemplate.getForObject(url, String.class, map);return result;}//发起POST请求,获取图片字节public byte[] getCodeImgBytes(String url,Map<String,Object> param){RestTemplate restTemplate = new RestTemplate();MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();HttpEntity requestEntity = new HttpEntity(param, headers);ResponseEntity<byte[]> entity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]);byte[] result = entity.getBody();return result;}/*** 获得当前访问的URL路径* @param request* @return*/public static String getLocation(HttpServletRequest request) {UrlPathHelper helper = new UrlPathHelper();StringBuffer buff = request.getRequestURL();String uri = request.getRequestURI();String origUri = helper.getOriginatingRequestUri(request);buff.replace(buff.length() - uri.length(), buff.length(), origUri);String queryString = helper.getOriginatingQueryString(request);if (queryString != null) {buff.append("?").append(queryString);}try {return new String(buff.toString().getBytes(), "iso-8859-1");} catch (UnsupportedEncodingException e) {return buff.toString();}}
}

controler交互

/*** 获取wechatConfig信息* @param url   访问页面的地址* @return*/@GetMapping("/getWechatConfig")public Map<String,String> getWechatConfig(String url){return wechatUtil.wxConfig(url);}

js

//将url 存进缓存下
var url = "https://" + window.location.host;
var urlCureent = encodeURIComponent(location.href);
$.ajax({async:true,dataType:"json",type:"GET",url: url + "/verification/getWechatConfig?url=" + urlCureent,contentType: "application/json; charset=utf-8",success:function(data){wx.config({debug: 0,appId: data.appId,timestamp: data.timestamp,nonceStr: data.nonceStr,signature: data.signature,jsApiList: ["updateAppMessageShareData", "updateTimelineShareData","onMenuShareTimeline","onMenuShareAppMessage"]});wx.ready(function () {   //需在用户可能点击分享按钮前就先调用wx.updateAppMessageShareData({title: cardVo.cardName + "个人名片", // 分享标题desc: cardVo.cardNameEn + "'s card", // 分享描述link: location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享图标success: function () {console.log("success");// 设置成功}});wx.updateTimelineShareData({title: cardVo.cardName + "个人名片", // 分享标题link: location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享图标success: function () {console.log("success2");// 设置成功}});wx.onMenuShareTimeline({title: cardVo.cardName + "个人名片", // 分享标题desc: cardVo.cardNameEn + "'s card", // 分享描述link: location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享图标});wx.onMenuShareAppMessage({title: cardVo.cardName + "个人名片", // 分享标题link: location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致imgUrl: url + cardVo.cardImg, // 分享图标});});}});

常见问题

获取JSSDK权限成功但分享无效果

官方建议使用wx.updateAppMessageShareData、wx.updateTimelineShareData,但是我当时无效果,再加上旧版本接口wx.onMenuShareTimeline、wx.onMenuShareAppMessage就可以了。

本地调试无效果

本地调试需要ngrok等外网穿透再做白名单

微信页面缓存

微信缓存机制,在js后面加个版本号即可。

<script type="text/javascript">var js = document.getElementById('cardJs');js.src = './gzdm/card.js?v='+ new Date().getTime();var css = document.getElementById('cardCss');css.href = './css/card.css?v='+ new Date().getTime();
</script>

微信自定义分享链接内容,wx.updateAppMessageShareData、wx.updateTimelineShareData、wx.onMenuShareTimeline相关推荐

  1. h5页面生成图片分享到微信js_H5微信自定义分享链接(设置标题+简介+图片)

    起源:最近公司在做招募广告的html5页面,然后做出来后,产品提出一个问题,需要分享出去的链接是卡片形式,内容也要自己定义,这下就难到我了,因为是第一次遇到这种需求,果断百度,然而,我就像大家一样,看 ...

  2. 微信自定义分享链接信息(标题,图片和内容)的实现教程 填写链接

    网页分享到微信要怎么才能自定义标题,内容和图片呢? 需求概述 微信中可以直接转发网页链接,用户在微信内置浏览器中打开后可以通过微信浏览器中右上角进行分享到朋友,朋友圈,QQ等.但直接这样分享的话,用户 ...

  3. 微信自定义分享链接信息(标题,图片和内容)实现过程详解

    网页分享到微信要怎么才能自定义标题,内容和图片呢? 需求概述 微信中可以直接转发网页链接,用户在微信内置浏览器中打开后可以通过微信浏览器中右上角进行分享到朋友,朋友圈,QQ等.但直接这样分享的话,用户 ...

  4. 分享!微信自定义分享链接的标题(描述和图片)都是怎么实现的?

    自定义微信链接是什么? 自定义微信分享链接是指将一条网页链接通过微信接口生成一张卡片,并且该卡片的标题,内容和图片都可以自己编辑.如下图效果 ● 自定义网页链接示例(带标题,内容简介,缩略图) ● 未 ...

  5. H5网页如何在微信中自定义分享链接(可设置标题+简介+图片)

    自定义分享链接是什么? 自定义微信分享链接是指将一条网页链接通过微信接口生成一张卡片,并且该卡片的标题,内容和图片都可以自己编辑.如下图效果 ● 自定义网页链接示例(带标题,内容简介,缩略图) ● 未 ...

  6. 微信自定义分享卡片链接的解决方案(可自定义标题 描述 图片)

    微信自定义分享卡片链接的解决方案(可自定义标题 描述 图片) 参考文章: (1)微信自定义分享卡片链接的解决方案(可自定义标题 描述 图片) (2)https://www.cnblogs.com/rg ...

  7. 微信自定义分享限制分享

    微信自定义分享&限制分享 一.微信自定义分享 [ (* ̄︶ ̄)微信官方文档 ] 业务需求: 开发过程中有些业务需要借助微信进行推广和宣传.难免需要使用微信提供的一些功能,比如微信的二次分享(也 ...

  8. vue项目全局配置微信分享_Vue 项目实现微信自定义分享

    最近公司项目中有在微信中自定义分享的需要,遇到的问题记录一下. ¶一.实现效果 ¶二.遇到的问题 1.需求:项目需要带参分享,进行好友拆礼盒的类似操作,需要带个参与活动的 id,其实这种分享微信是不太 ...

  9. 微信自定义分享(php方法)

    首先拥有一个微信公众号,企业认证的,获取接口权限.如下图所示: 其次拥有一台服务器,备案号的域名 因为前端页面需要的appId.signature是需要后台传递过来的. 第一步:绑定域名 先登录微信公 ...

  10. 微信自定义分享功能实现

    微信自定义分享功能实现 微信自定义分享功能实现 一.实现的关键 1.后端的任务 2.前台的任务 二.实现具体步骤 1.js安全域名配置(被分享的网址必须实现) 2.添加服务器配置(成为开发者) 3.生 ...

最新文章

  1. 独家 | 一文读懂PySpark数据框(附实例)
  2. 画瀑布图_常见的招财风水画之含义
  3. 20170505思考点--编写案例时是以功能为主还是业务为主要
  4. 启动项目无法打印日志处理及logback简单使用
  5. 2021江西高考成绩查询方式6,2021年江西高考成绩6月23日公布 多种查分方式
  6. SpringBoot加载自定义yml文件
  7. QT连接PostSql (小白教程)
  8. 支付宝实现JS调起支付你必须知道的坑(40004 ACQ.INVALID_PARAMETER)
  9. 《程序员》9月刊推荐:移动应用产业链大势图
  10. 马斯克被指性骚扰空姐,已支付170万封口费,马一龙:这是他们卑鄙的剧本!...
  11. Artemis资源限制(17)
  12. jmeter学习指南之察看结果树,你知道都有哪些功能吗
  13. CISA国际信息系统审计师
  14. MYSQL:查询年龄最大的5个学生(包括年龄并列第5名的所有学生)的姓名、年龄及所在系。
  15. 1、vivado新建工程
  16. 2023年工程师中级和高级有什么区别,他们评审的要求有哪些不同?
  17. 项目中添加音效--依旧的简单使用
  18. 【深度报道】阿里云、华为云、用友云,企业服务的三种新生态
  19. 王垠:自动编程是不可能的 我为什么不在乎人工智能
  20. java中编写敏感词过滤程序_Java敏感词过滤

热门文章

  1. chcp 437>nul graftabl 936>nul
  2. 标准结构篇:7)塑料齿轮轮系设计总章
  3. BottomNavigationView取消水波纹动画
  4. SwitchyOmega_Chromium插件的下载安装以及使用
  5. amd一键超频怎么用_(完整版)超频必看AMD_CPU超频教程
  6. dell服务器硬件参数提升速度,DELL R620服务器配置
  7. boost noncopyable实现与ADL
  8. android手机的根目录,安卓根目录详解
  9. java用线程做小球碰撞_多线程之碰撞小球
  10. 亲爱的程序猿们怎么找工作