实现方案:

       1、主要通过使用微信JS-SDK实现;

       2、微信相关文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

相关配置:

       1、调用微信的分享需要提供微信公众号的AppID和AppSecret(注意AppSecret是保密的,如果重置可能会影响以前的使用场景)

       2、本次所使用的   AppID: ******      AppSecret: ******

       3、通过AppID和AppSecret获取微信的access_token,注意需要配置微信的ip白名单

       4、使用微信的sdk分享还需要配置JS接口安全域名(具体配置规则微信的文档里面有详细介绍)

       5、后端需要根据微信文档做权限算法解析,并返回前端所需要的字段

后端代码:

package com.fairy.fshop.util;

import java.io.IOException;
import java.net.URISyntaxException;

import org.apache.http.client.ClientProtocolException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.fairy.scsowing.common.HttpUtil;
import com.sowing.common.exception.ServiceException;

import net.sf.json.JSONObject;

/**
 * 微信分享工具类
 * @author dwj
 *
 */
@Component
public class WeiXinShareUtil {
	
	private static final Logger LOG = LoggerFactory.getLogger(WeiXinShareUtil.class);
	
	public static String wx_appid = "wx51a9d9d6e19f9bef";
	
	public static String wx_secret = "c10df335f8723f4198fe19d4c0137a04";
	
	public static String wx_ticketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=";
	
	public static String wx_accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";

    //以上值为测试用

	public WeiXinShareUtil(){}
	
	@Value("${share.weixin.appid}")
	public void setAppid(String appid) {
		wx_appid = appid;
	}
	@Value("${share.weixin.secret}")
	public void setSecret(String secret) {
		wx_secret = secret;
	}
	@Value("${share.weixin.ticketUrl}")
	public void setWeixinTicketUrl(String weixinTicketUrl) {
		wx_ticketUrl = weixinTicketUrl;
	}
	@Value("${share.weixin.accessTokenUrl}")
	public void setWeixinAccessTokenUrl(String weixinAccessTokenUrl) {
		wx_accessTokenUrl = weixinAccessTokenUrl;
	}


	/**
	 * 获取微信认证token(有效期7200s)
	 * @return
	 * @throws ClientProtocolException
	 * @throws URISyntaxException
	 * @throws IOException
	 */
	public static String getWXAccessToken() {
		try {
			HttpUtil httpUtil = new HttpUtil();
			String access_token = httpUtil.doGetForString(wx_accessTokenUrl + wx_appid + "&secret=" + wx_secret);
			LOG.info("获取微信accessToken接口返回参数:{}", access_token);
			Object json =JSONObject.fromObject(access_token).get("access_token");
			return json.toString();
		} catch (Exception e) {
			LOG.error("获取微信accessToken异常,原因:{}", e);
			throw new ServiceException("获取微信accessToken异常");
		}
		
	}
	
	/**
	 * 通过获取的微信token获取ticket
	 * @param accessToken
	 * @return
	 * @throws IOException 
	 * @throws URISyntaxException 
	 * @throws ClientProtocolException 
	 */
	public static String getWXTicket(String accessToken) {
		try {
			HttpUtil httpUtil = new HttpUtil();
			String ticket = httpUtil.doGetForString(wx_ticketUrl + accessToken);
			LOG.info("获取微信ticket接口返回参数:{}", ticket);
			Object json =JSONObject.fromObject(ticket).get("ticket");
			return json.toString();
		} catch (Exception e) {
			LOG.error("获取微信ticket异常,原因:", e);
			throw new ServiceException("获取微信ticket异常");
		}
		
	}
}
package com.sowing.hsh.api.controller;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.fairy.fshop.system.redis.RedisService;
import com.fairy.fshop.util.WeiXinShareUtil;
import com.fairy.scsowing.common.HttpUtil;

/**
 * 微信分享
 */
@RestController
public class WeixinShareController {
	
	private static final Logger LOG = LoggerFactory.getLogger(WeixinShareController.class);
	
	private static String weixinTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=";
	@Resource
	private RedisService redisService;
	/**
	 * 获取微信分享的ticket
	 *
	 * @param accessToken
	 * @return
	 * @throws IOException
	 * @throws URISyntaxException
	 */
	@RequestMapping("/weixin/shareTicket")
	public static void getWeixinTicket(@RequestParam("accessToken") String accessToken, HttpServletResponse response)
			throws IOException, URISyntaxException {
		HttpUtil httpUtil = new HttpUtil();
		String ticket = httpUtil.doGetForString(weixinTicketUrl + accessToken);
		response.getOutputStream().write(ticket.getBytes());
	}

	/**
	 * 获取微信分享js-sdk权限验证签名
	 * @param url 分享地址
	 * @return
	 */
	@RequestMapping("/weixin/share/sign")
	@ResponseBody
	public  Map<String, String> getWXSign(@RequestParam("shareUrl") String shareUrl) {
		
		try {
			shareUrl = URLDecoder.decode(shareUrl, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			LOG.error("分享url解码异常:{}", e.getMessage());
		}   
		 
		//通过redis做缓存,防止获取微信token和ticket超过请求限制
		String accessToken = redisService.getString("weixin_access_token_redis_key");
		String jsapi_ticket = redisService.getString("weixin_ticket_redis_key");
		if(StringUtils.isBlank(accessToken)) {
			accessToken = WeiXinShareUtil.getWXAccessToken();
			redisService.setString("weixin_access_token_redis_key", accessToken, 7000);
		}
	
		if(StringUtils.isBlank(jsapi_ticket)) {
			jsapi_ticket = WeiXinShareUtil.getWXTicket(accessToken);
			redisService.setString("weixin_ticket_redis_key", jsapi_ticket, 7000);
		}
		String nonce_str = createNonceStr();
		String timestamp = createTimestamp();
		String signature;

		// 注意这里参数名必须全部小写,且必须有序
		String signatureStr = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + shareUrl;
		LOG.info("微信分享加密字符串:{}", signatureStr);

		try {
			MessageDigest crypt = MessageDigest.getInstance("SHA-1");
			crypt.reset();
			crypt.update(signatureStr.getBytes("UTF-8"));
			signature = byteToHex(crypt.digest());
		} catch (Exception e) {
			LOG.error("获取微信签名signature异常,原因:{}", e);
			return null;
		} 
		Map<String, String> ret = new HashMap<String, String>();
		ret.put("url", shareUrl);
		ret.put("jsapi_ticket", jsapi_ticket);
		ret.put("nonceStr", nonce_str);
		ret.put("timestamp", timestamp);
		ret.put("signature", signature);

		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;
	}

	/**
	 * 随机字符串
	 * @return
	 */
	private static String createNonceStr() {
		return UUID.randomUUID().toString();
	}

	/**
	 * 获取系统时间戳
	 * @return
	 */
	private static String createTimestamp() {
		return Long.toString(System.currentTimeMillis() / 1000);
	}
}

前端代码:

export const wxApi = {
    wxRegister(option){    //获取签名算法解析之后的相关字段
        Axios.get(`${process.env.VUE_APP_URL}weixin/share/sign?`,{
            params: {
                shareUrl: decodeURIComponent(location.href)           
            }
        }).then(function (res) {
           
            let data = res.data;
            if(res){
                wx.config({    //根据微信的文档进行config接口注入权限验证配置
                    debug:  true, // 开启调试模式
                    appId: data.appId || 'wx53c4a6dcfd495e12', // 必填,公众号的唯一标识
                    timestamp: data.timestamp, // 必填,生成签名的时间戳
                    nonceStr: data.nonceStr, // 必填,生成签名的随机串
                    signature: data.signature, // 必填,签名,见附录1
                    jsApiList: ['updateAppMessageShareData','updateTimelineShareData','onMenuShareTimeline'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
                  })

                  wx.ready(() => {   //通过ready接口处理成功验证
                    if (option) {
                      wxApi.updateAppMessageShareData(option);
                      wxApi.updateTimelineShareData(option);
                      wxApi.onMenuShareTimeline(option)
                    }
                  })

            }
        })
        .catch(function (err) {
            console.log(err);
        })

        
    },


    updateAppMessageShareData(option){
        wx.updateAppMessageShareData({ 
            title: option.title, // 分享标题
            desc: '刚在惠生活发现了一个好东西,我猜你也感兴趣,快来看看吧!',
            link: option.link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
            imgUrl: option.imgUrl, // 分享图标
            success: function () {
              // 设置成功
            }
        })
    },

    updateTimelineShareData(option){
        wx.updateTimelineShareData({ 
            title: option.title, // 分享标题
            link: option.link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
            imgUrl: option.imgUrl, // 分享图标
            success: function () {
              // 设置成功
            }
        })
    },

    onMenuShareTimeline(option){
        wx.onMenuShareTimeline({
            title: option.title, // 分享标题
            link: option.link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
            imgUrl: option.imgUrl, // 分享图标
            success: function () {
            // 用户点击了分享后执行的回调函数
            }
        })
    }

}

使用方法:

解构并调用wxRegister方法,此方法需传递一个option对象前端使用:

option:{

   title: '', // 分享标题
   link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
   imgUrl: '', // 分享图标
}