SpringBoot JS-SDK自定义微信分享的实现

 更新时间:2019年09月25日 10:31:50   作者:桌前明月  
这篇文章主要介绍了SpringBoot JS-SDK自定义微信分享的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

在介绍使用微信自定义分享前,我们来先了解一下什么是自定义分享?

访问自定义微信外链地址页面,点击红色框位置进行分享给朋友或者朋友圈,具体操作如下图所示:

在这里插入图片描述
分享后图文消息如下图所示:

我们要做的就是自定义下图所示中红框中的信息。

闲话少说,接下来就正式开始自定义分享实战环节!

自定义分享实战

第一步需要先申请接口测试号并进行JS接口安全域名设置

访问如下链接进行接口测试号申请。点击访问 选择接口测试号申请,如下图所示:

在这里插入图片描述

或者直接访问 :申请测试账号页面 如下图所示:

在这里插入图片描述

点击登录进行扫码登录,如下图所示:

登录后如下图所示:

在这里插入图片描述

在这里插入图片描述

windows系统在 hosts 文件中配置回环地址的域名

在这里插入图片描述

在这里插入图片描述

第二步是下载微信web开发者工具,可以在PC 进行测试。

点击访问下载页面 如下图所示:

在这里插入图片描述

傻瓜式一步一步安装即可。

第三步 看微信操作教程并完成代码实现

点击访问微信JS-SDK说明文档 如下图所示:

在这里插入图片描述

JSSDK使用步骤

步骤一:绑定域名(上面已经操作了)

步骤二:引入JS文件(下面实战代码中会介绍到如何使用)

在这里插入图片描述
步骤三:通过config接口注入权限验证配置(下面实战代码中会介绍到如何使用)
在这里插入图片描述

步骤四:通过ready接口处理成功验证(这里就不详细介绍了具体内容请参看微信文档查看)

步骤五:通过error接口处理失败验证(这里就不详细介绍了具体内容请参看微信文档查看)

上面步骤步骤三中的 signature是一个重要的参数,生成它需要获取 jsapi_ticket,而生成 jsapi_ticket 需要通过 access_token

获取signature流程如下:

  1. 获取 access_token 然后根据 access_token 获取 jsapi_ticket 。
  2. 排序 noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳),url(当前网页的URL,不包含#及其后面部分)4个参数拼接例如:noncestr=XX&jsapi_ticket=XX&jtimestamp=XX&jurl=XX
  3. 然后通过sha1加密拼接的4个参数获取到signature

可能你对操作流程还是看不懂,没有关系可以直接参考代码实现,接下来就是代码实现:

初始化微信JSSDK配置信息 Controller 内容如下:

@RestController
@RequestMapping("/weixin")
public class WeiXinDemoController {
	
	@Autowired
	private WeiXinService weiXinService;
	/**
	 * 初始化微信JSSDK配置信息
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("/initWXJSSDKConfigInfo")
	public String initWXJSConfig (HttpServletRequest request,HttpServletResponse response) throws Exception{
		String shareUrl = request.getParameter("shareUrl");//分享的URL
		Map map = weiXinService.initJSSDKConfigInfo(shareUrl);
		String json = weiXinService.mapToJson(map);
		return json;
	}
	
}

初始化JSSDK配置信息,配置信息有:noncestr(随机字符串)、有效的jsapi_ticket、timestamp(时间戳)、shareUrl(当前网页的URL,不包含#及其后面部分)appid(公众号 appid)。

	/**
	 * 初始化JSSDK配置信息
	 * @param shareUrl
	 * @return
	 * @throws Exception
	 */
	public Map initJSSDKConfigInfo(String shareUrl) throws Exception {
		
		String accessToken = this.getJSSDKAccessToken();
		String jsapiTicket = this.getJSSDKJsapiTicket(accessToken);
		String timestamp = Long.toString(System.currentTimeMillis() / 1000);
		String nonceStr = UUID.randomUUID().toString();
		String signature = this.buildJSSDKSignature(jsapiTicket,timestamp,nonceStr,shareUrl);
		
		Map<String,String> map = new HashMap<String,String>();
		map.put("shareUrl", shareUrl);
		map.put("jsapi_ticket", jsapiTicket);
		map.put("nonceStr", nonceStr);
		map.put("timestamp", timestamp);
		map.put("signature", signature);
		map.put("appid", weiXinConfig.getAppID());
		return map;
	}

获取 JSSDK access_token 方法。

public String getJSSDKAccessToken() {
		String token = null;
		String url = JSSDK_ACCESSTOKEN.replaceAll("APPID",
				weiXinConfig.getAppID()).replaceAll("APPSECRET",
				weiXinConfig.getAppsecret());

		String json = postRequestForWeiXinService(url);
		Map map = jsonToMap(json);
		if (map != null) {
			token = (String) map.get("access_token");
		}
		return token;
	}

获取 JSSDK jsapi_ticket 方法。

public String getJSSDKJsapiTicket(String token) {
	 String url = JSSDK_GETTICKET.replaceAll("ACCESS_TOKEN", token);
	 String json = postRequestForWeiXinService(url);
	 Map map = jsonToMap(json);
	 String jsapi_ticket = null;
	 if (map != null) {
     jsapi_ticket = (String) map.get("ticket");
   }
	 return jsapi_ticket;
	}

拼接 noncestr(随机字符串), 有效的jsapi_ticket、 timestamp(时间戳)、url(当前网页的URL,不包含#及其后面部分)并通过sha1进行加密。

 /**
   * 构建分享链接的签名。
   * @param ticket
   * @param nonceStr
   * @param timeStamp
   * @param url
   * @return
   * @throws Exception
   */
  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微信配置参数获取签名。
   * 
   * @param signature
   * @param timestamp
   * @param nonce
   * @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; 
  }

基础工具方法如下:

public String mapToJson(Map map){
		Gson gson = new Gson();
		String json = gson.toJson(map);
		return json;
	}
	private Map jsonToMap(String json) {
		Gson gons = new Gson();
		Map map = gons.fromJson(json, new TypeToken<Map>(){}.getType());
		return map;
	}

	private String postRequestForWeiXinService(String getAccessTokenUrl) {
		ResponseEntity<String> postForEntity = restTemplate.postForEntity(getAccessTokenUrl, null, String.class);
		String json = postForEntity.getBody();
		return json;
	}
	
	private String getRequestForWeiXinService(String getUserInfoUrl) {
		ResponseEntity<String> postForEntity = restTemplate.getForEntity(getUserInfoUrl.toString(), String.class);
		String json = postForEntity.getBody();
		return json;
	}

wxShare.js 主要是获取JSSDK配置信息并定义分享功能处理。具体代码如下:

//alert(window.location.href.split('#')[0]);/***用于获得当前连接url用**/
/***用户点击分享到微信圈后加载接口接口*******/
$.post("/weixin/initWXJSSDKConfigInfo",{"shareUrl":window.location.href.split('#')[0]},function(data,status){
  data=eval("("+data+")");
  wx.config({
    debug: false,
    appId: data.appid,
    timestamp:data.timestamp,
    nonceStr:data.nonceStr,
    signature:data.signature,
    jsApiList: [
      'checkJsApi',
      'onMenuShareTimeline',
      'onMenuShareAppMessage',
      'onMenuShareQQ',
      'onMenuShareWeibo',
      'onMenuShareQZone',
      'hideOptionMenu',
    ]
  });
  var shareTitle = $("#wx_share_span").data("shareTitle");
  if(!shareTitle){
  	shareTitle = $("title").html();
  }
  var shareImg = $("#wx_share_span").data("shareImg");
  if(!shareImg){
  	//shareImg = common.bp()+'/m_images/shareImg.jpg';
  }
  var shareLink = $("#wx_share_span").data("shareLink");
  if(!shareLink){
  	shareLink = window.location.href.split('#')[0];
  }
  var shareDesc = $("#wx_share_span").data("shareDesc");
  if(!shareDesc){
  	shareDesc = $("meta[name=description]").attr("content");
  }
  wx.ready(function(){
    // alert("准备分享");
    wx.onMenuShareTimeline({
      title : shareTitle, // 分享标题
      link : shareLink, // 分享链接
      imgUrl : shareImg, // 分享图标
      success : function() {
        // 用户确认分享后执行的回调函数
        //alert("分享成功");
      },
      cancel : function() {
        // 用户取消分享后执行的回调函数
        //alert("分享取消");
      }
    });
    //wx.hideOptionMenu();/***隐藏分享菜单****/
    wx.onMenuShareAppMessage({
    	title: shareTitle, // 分享标题
    	desc: shareDesc, // 分享描述
    	link: shareLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
    	imgUrl: shareImg, // 分享图标
    	success: function () {
    	// 用户确认分享后执行的回调函数
    	},
    	cancel: function () {
    	// 用户取消分享后执行的回调函数
    	}
  	});
    wx.onMenuShareQQ({
    	title: shareTitle, // 分享标题
    	desc: shareDesc, // 分享描述
    	link: shareLink, // 分享链接
    	imgUrl: shareImg, // 分享图标
    	success: function () {
    	// 用户确认分享后执行的回调函数
    	},
    	cancel: function () {
    	// 用户取消分享后执行的回调函数
    	}
  	});
    wx.onMenuShareWeibo({
    	title: shareTitle, // 分享标题
    	desc: shareDesc, // 分享描述
    	link: shareLink, // 分享链接
    	imgUrl: shareImg, // 分享图标
    	success: function () {
    	// 用户确认分享后执行的回调函数
    	},
    	cancel: function () {
    	// 用户取消分享后执行的回调函数
    	}
  	});
    wx.onMenuShareQZone({
    	title: shareTitle, // 分享标题
    	desc: shareDesc, // 分享描述
    	link: shareLink, // 分享链接
    	imgUrl: shareImg, // 分享图标
    	success: function () {
    	// 用户确认分享后执行的回调函数
    	},
    	cancel: function () {
    	// 用户取消分享后执行的回调函数
    	}
  	});
  });
});

分享页面该页面需要引入wxShare.js和jweixin-1.2.0.js,并且通过在隐藏的span标签上定义自定义分享的内容,具体代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="/jquery-1.8.3.min.js" type="text/javascript" ></script>
</head>
<body>
<span id="wx_share_span" style="display: none"></span>
<script type="text/javascript">
	$(document).ready(function(){
		
		$("#wx_share_span").data("shareTitle", "桌前明月教你玩微信公众号自定义分享");
		$("#wx_share_span").data("shareDesc", "保证学会哈!");
		//$("#wx_share_span").data("shareLink", "/weixinshare.html");
		//$("#wx_share_span").data("shareImg", "/banner.jpg");
	});
</script>

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

测试

访问:http://www.zhuoqianmingyue.com:8090/weixinshare.html 未引入 wxShare.js 的测试结果如下:

在这里插入图片描述

引入 wxShare.js 的测试结果如下:

在这里插入图片描述

一般情况下我们通过接口测试号配置成测试环境域名,上述测试通过后就可以在正式服务号上配置JS接口安全域名,顺便在把我们项目配置的 appid 换成服务号的 appid 即可。具体操作如下:

在这里插入图片描述

在这里插入图片描述

小结

微信自定义分享具体操作步骤如下:

  • 定义获取JS-SDK配置信息接口
  • 定义页面初始化调用JS-SDK配置信息接口 js 代码和定义分享接口js代码
  • 分享页面引入 jweixin-1.2.0.js和 调用JS-SDK配置信息的js文件

三步中最为总要的就是第一步:定义获取JS-SDK配置信息接口,这个接口需要获取jsapi_ticket 并通过和配置信息参数一起生成签名 signature。

在这里再三强调一下,微信提供操作文档一定要多看几篇,因为很多细节都在文档中进行了说明。当你把微信提供操作文档看透,你就会觉得其实就是个API 调用而已。

代码示例

具体代码示例请查看我的GitHub 仓库 springbootexamples 中的 spring-boot-2.x-weixin 查看。

GitHub:https://github.com/zhuoqianmingyue/springbootexamples

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • idea编译报错-代码没问题IDEA编译不通过的处理方案

    idea编译报错-代码没问题IDEA编译不通过的处理方案

    这篇文章主要介绍了idea编译报错-代码没问题IDEA编译不通过的问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • springboot实现在工具类(util)中调用注入service层方法

    springboot实现在工具类(util)中调用注入service层方法

    这篇文章主要介绍了springboot实现在工具类(util)中调用注入service层方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Elasticsearch写入瓶颈导致skywalking大盘空白

    Elasticsearch写入瓶颈导致skywalking大盘空白

    这篇文章主要为大家介绍了Elasticsearch写入瓶颈导致skywalking大盘空白的解决方案,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • Java实例化类详解

    Java实例化类详解

    学习JAVA这门面向对象的语言,实质就是不断地创建类,并把类实例化为对象并调用方法。对于初学JAVA的人总搞清楚对象是如何实例化的,假如类之间存在继承关系,那就更糊涂了。下面我们通过两个例题来说明对象的实例化过程。
    2016-03-03
  • 如何使用Spring Boot实现自定义Spring Boot插件

    如何使用Spring Boot实现自定义Spring Boot插件

    在本文中,我们介绍了如何使用 Spring Boot 实现自定义插件,使用自定义插件可以帮助我们快速地添加一些额外的功能,提高系统的可扩展性和可维护性,感兴趣的朋友跟随小编一起看看吧
    2023-06-06
  • 详解Spring Boot Web项目之参数绑定

    详解Spring Boot Web项目之参数绑定

    本篇文章主要介绍了详解Spring Boot Web项目之参数绑定,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • Mybatis中ResultMap解决属性名和数据库字段名不一致问题

    Mybatis中ResultMap解决属性名和数据库字段名不一致问题

    我们Pojo类的属性名和数据库中的字段名不一致的现象时有发生,本文就详细的介绍一下Mybatis中ResultMap解决属性名和数据库字段名不一致问题,感兴趣的可以了解一下
    2021-10-10
  • 详解Java如何创建Annotation

    详解Java如何创建Annotation

    在本文中,我们将介绍注解的基础知识,包括注解是什么,它们如何在示例中使用,以及如何处理它们。下面和小编一起来学习吧
    2019-05-05
  • Spring中AOP注解@Aspect的使用详解

    Spring中AOP注解@Aspect的使用详解

    这篇文章主要介绍了Spring中AOP注解@Aspect的使用详解,AOP是种面向切面的编程思想,面向切面编程是将程序抽象成各个切面,将那些影响了多个类的公共行为抽取到一个可重用模块里,减少系统的重复代码,降低模块间的耦合度,增强代码的可操作性和可维护性,需要的朋友可以参考下
    2024-01-01
  • SpringBoot配置 Druid 三种方式(包括纯配置文件配置)

    SpringBoot配置 Druid 三种方式(包括纯配置文件配置)

    本文给大家分享在项目中用纯 YML(application.yml 或者 application.properties)文件、Java 代码配置 Bean 和注解三种方式配置 Alibaba Druid 用于监控或者查看 SQL 状况的相关知识,感兴趣的朋友一起看看吧
    2021-10-10

最新评论