微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

时间:2021-05-29 17:01:34
先说明一下,文章中有的代码是我在网上找的,如果有相同的地方请原谅,我只是想告诉大家完整的步骤。

我这几天做的是自定义分享到朋友圈和分享给好友,能够自己设置分享出来的标题,描述和图片。

自定义分享到朋友圈需要调用微信的JS接口,个人公众号和企业公众号的拥有的接口权限都不一样。企业认证过的公众号拥有的接口权限会多一些。


那需要怎么做呢,官方文档的【微信JS-SDK说明文档】中写了JSSDK的使用步骤。

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115


第一步:绑定域名,在【公众号设置】的【功能设置】中设置JS接口安全域名网页授权域名,做这两步都要下载一个txt文件。

微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

我用Java写后台,所以Http服务器用的是Tomcat,这里要注意的是,如果你的域名是直接指向到你的服务器,需要把Tomcat的默认端口改成80,然后把这个文件放在webapps/ROOT目录下,如果输入域名/文件名在浏览器能够看到文件的内容就说明文件的位置放正确了,点保存就好了。


第二步:引入JS文件(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js

 

第三步:通过config接口注入权限验证配置

wx.config({

    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。

    appId: '', // 必填,公众号的唯一标识

    timestamp: , // 必填,生成签名的时间戳

    nonceStr: '', // 必填,生成签名的随机串

    signature: '',// 必填,签名,见附录1

    jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2

});

微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

这个时候我蒙圈了,这什么玩意,这些参数是自己输吗?在哪获得这些参数呢。于是我在公众号里努力找也只找到了appID,其他的参数怎么获得?timestamp和nonceStr还好解决,signature怎么获得,注释里写见附录1。


于是我翻到附录1。

微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

这里写到生成签名先要了解jsapi_ticket,而jsapi_ticket是通过access_token来获取的。


接下来看看access_token是什么,官方文档中【开始开发】中【获取access_token】中有说明,公众号可以使用AppID和AppSecret调用本接口来获取access_token。

微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

AppID在【开发】的【基本配置】有,【基本配置】中也有AppSecret,获取就行了,真是得来全不费功夫,但是要用文档记下AppID和AppSecret,获取access_token会用到。

微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

这里还有个IP白名单,只有将IP地址设置为公众号的IP白名单,才能成功调用该接口。AppID和AppSecret可在“微信公众平台-开发-基本配置”页中获得(需要已经成为开发者,且帐号没有异常状态)。调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,点击查看设置方法,否则将无法调用成功。可以设置多个,输入一个IP地址之后回车就行了,我遇到过几次情况就是在公司那个地方的公网IP和我住的地方的公网IP不一样,导致我在家的时候出现了后面的access_token获取不到的情况。记得要配置线上环境的IP地址

 

这一页还有个【服务器配置】,启用并设置服务器配置后,用户发给公众号的消息以及开发者需要的事件推送,将被微信转发到该URL中这次做的是自定义分享朋友圈,用户和公众号没有交互,但是这里也可以配置一下。

 微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

官方文档的【接入指南】中也写得比较详细,我只说一下这里的Token是自己输入的,和access_token是两回事,这里填写的Token需要和后台的定义的Token值一样,这个URL是能够直接访问后台进行验证的URL。官方只有PHP的代码,下面是Java后台代码。如果遇到Token验证失败,检查一下这里填写的Token和后台写的Token是不是一样。如果遇到指向服务器失败,看看这个URL是否能直接到验证的后台。配置好了之后启用。

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class StringServletBase extends HttpServlet {

private final String token = "syncoToken";

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("开始签名校验");
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");

System.out.println("signature=" + signature);
System.out.println("timestamp=" + timestamp);
System.out.println("nonce=" + nonce);
System.out.println("echostr=" + echostr);

ArrayList<String> array = new ArrayList<String>();
array.add(signature);
array.add(timestamp);
array.add(nonce);

// 排序
String sortString = sort(token, timestamp, nonce);
// 加密
String mytoken = Decript.SHA1(sortString);
// 校验签名
if (mytoken != null && mytoken != "" && mytoken.equals(signature)) {
System.out.println("签名校验通过。");
response.getWriter().println(echostr); // 如果检验成功输出echostr,微信服务器接收到此输出,才会确认检验完成。
} else {
System.out.println("签名校验失败。");
}
}

/**
* 排序方法
*
* @param token
* @param timestamp
* @param nonce
* @return
*/
public static String sort(String token, String timestamp, String nonce) {
String[] strArray = { token, timestamp, nonce };
Arrays.sort(strArray);

StringBuilder sbuilder = new StringBuilder();
for (String str : strArray) {
sbuilder.append(str);
}

return sbuilder.toString();
}

}
import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;public class Decript {	public static String SHA1(String decript) {		try {			MessageDigest digest = MessageDigest.getInstance("SHA-1");			digest.update(decript.getBytes());			byte messageDigest[] = digest.digest();			// Create Hex String			StringBuffer hexString = new StringBuffer();			// 字节数组转换为 十六进制 数			for (int i = 0; i < messageDigest.length; i++) {				String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);				if (shaHex.length() < 2) {					hexString.append(0);				}				hexString.append(shaHex);			}			return hexString.toString();		} catch (NoSuchAlgorithmException e) {			e.printStackTrace();		}		return "";	}}

现在AppID和AppSecret都有了,怎么获得access_token。前面看的附录1中写了全局缓存jsapi_ticket和access_token,官方文档【获取access_token】中也有获得access_token的URL。

https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

定义一个类AccessToken

public class AccessToken implements java.io.Serializable {
// 接口访问凭证
private String accessToken;
// 凭证有效期,单位:秒
private int expiresIn;

public AccessToken() {

}

public String getAccessToken() {
return accessToken;
}

public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}

public int getExpiresIn() {
return expiresIn;
}

public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
}

再写一个类JsApiTicket

public class JsApiTicket implements java.io.Serializable {

private String ticket;
// 凭证有效期,单位:秒
private int expiresIn;

public JsApiTicket() {

}

public String getTicket() {
return ticket;
}

public void setTicket(String ticket) {
this.ticket = ticket;
}

public int getExpiresIn() {
return expiresIn;
}

public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}

}

线上环境需要项目发布到服务器上就能开始获取access_token,创建一个InitAccessTokenServletTomcat启动就初始化这个Servlet。

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import com.kavo.utils.AccessTokenThread;
import com.kavo.utils.JsApiTicketThread;

public class InitAccessTokenServlet extends HttpServlet {

public void init() throws ServletException {
// 获取web.xml中配置的参数
String WX_APPID = getInitParameter("appid");
String WX_APPSECRET = getInitParameter("appsecret");
AccessTokenThread.appid = WX_APPID;
AccessTokenThread.appsecret = WX_APPSECRET;

if ("".equals(AccessTokenThread.appid) || "".equals(AccessTokenThread.appsecret)) {
System.out.println("appid和appsecret未给出");
} else {
new Thread(new AccessTokenThread()).start();
new Thread(new JsApiTicketThread()).start();
}
}

}

代码中有getInitParameter方法,这是获取初始值的方法,在web.xml中需要这样配置。

<servlet>
<servlet-name>InitAccessTokenServlet</servlet-name>
<servlet-class>com.kavo.controller.InitAccessTokenServlet</servlet-class>

<init-param>
<param-name>appid</param-name>
<param-value>公众号appid</param-value>
</init-param>
<init-param>
<param-name>appsecret</param-name>
<param-value>公众号addsecret</param-value>
</init-param>

<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>InitAccessTokenServlet</servlet-name>
<url-pattern>/InitAccessTokenServlet</url-pattern>
</servlet-mapping>


在InitAccessTokenServlet中有一段代码是开启线程的。因为access_token的有效期是2小时(看文档),所以获取一次之后2小时之内需要再获取。

下面是获取access_token的线程

import javax.servlet.ServletContext;

import com.kavo.pojo.AccessToken;

public class AccessTokenThread implements Runnable {
public static String appid = "";
public static String appsecret = "";
public static AccessToken accessToken = null;


@Override
public void run() {
while (true) {
try {
accessToken = CommonUtil.getAccessToken(appid, appsecret);
if (null != accessToken) {
System.out.println("accessToken初始化成功:" + accessToken.getAccessToken());
// 全局缓存access_token
ServletContext servletContext = ServletContextUtil.getServletContext();
servletContext.setAttribute("access_token", accessToken.getAccessToken());

// 有效期(秒)减去200秒,乘以1000(毫秒)——也就是在有效期的200秒前去请求新的accessToken
Thread.sleep((accessToken.getExpiresIn() - 200) * 1000);
} else {
// 等待一分钟,再次请求
Thread.sleep(60 * 1000);
}
} catch (Exception e) {
try {
// 等待一分钟,再次请求
Thread.sleep(60 * 1000);
} catch (Exception ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
}
}
然后是获取jsapi_ticket的线程

import javax.servlet.ServletContext;

import com.kavo.pojo.JsApiTicket;

public class JsApiTicketThread implements Runnable {

@Override
public void run() {
while (true) {
try {
ServletContext servletContext = ServletContextUtil.getServletContext();
String access_token = (String) servletContext.getAttribute("access_token");

JsApiTicket jsApiTicket = null;

if(null != access_token && !"".equals(access_token)){
// 获取jsapi_ticket
jsApiTicket = CommonUtil.getJsApiTicket(access_token);

if (null != jsApiTicket) {
System.out.println("jsapi_ticket获取成功:" + jsApiTicket.getTicket());
// 全局缓存jsapi_ticket
servletContext.setAttribute("jsapi_ticket", jsApiTicket.getTicket());

Thread.sleep((jsApiTicket.getExpiresIn() - 200) * 1000);
}
}
Thread.sleep(60 * 1000);
} catch (Exception e) {
try {
Thread.sleep(60 * 1000);
} catch (Exception ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
}

}

这两个线程都用了一个公共类CommonUtil用来获取access_tokenjsapi_ticket

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kavo.pojo.AccessToken;
import com.kavo.pojo.JsApiTicket;

public class CommonUtil {

// 凭证获取(GET)——access_token
public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
// 微信JSSDK的ticket请求URL地址——jsapi_ticket
public final static String JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";

/**
* 发送https请求
*
* @param requestUrl
* 请求地址
* @param requestMethod
* 请求方式(GET、POST)
* @param outputStr
* 提交的数据
* @return rootNode(通过rootNode.get(key)的方式获取json对象的属性值)
*/
public static JsonNode httpsRequest(String requestUrl, String requestMethod, String outputStr) {
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = null;
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");

sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();

URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);

conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
//conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if ("GET".equalsIgnoreCase(requestMethod))
conn.connect();

// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}

// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}

// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
rootNode = mapper.readTree(buffer.toString());
} catch (Exception e) {
e.printStackTrace();
}
return rootNode;
}

/**
* 获取接口访问凭证
*
* @param appid
* 凭证
* @param appsecret
* 密钥
* @return
*/
public static AccessToken getAccessToken(String appid, String appsecret) {
AccessToken accessToken = null;
String requestUrl = ACCESS_TOKEN_URL.replace("APPID", appid).replace("APPSECRET", appsecret);
// 发起GET请求获取凭证
JsonNode rootNode = httpsRequest(requestUrl, "GET", null);

if (null != rootNode.get("access_token")) {
accessToken = new AccessToken();
accessToken.setAccessToken(rootNode.get("access_token").textValue());
accessToken.setExpiresIn(toInt(rootNode.get("expires_in").toString()));
}
return accessToken;
}

/**
* 调用微信JS接口的临时票据
*
* @param access_token
* 接口访问凭证
* @return
*/
public static JsApiTicket getJsApiTicket(String access_token) {
String requestUrl = JSAPI_TICKET_URL.replace("ACCESS_TOKEN", access_token);
// 发起GET请求获取凭证
JsonNode rootNode = httpsRequest(requestUrl, "GET", null);
JsApiTicket jsApiTicket = null;
if (null != rootNode.get("jsapi_ticket")) {
jsApiTicket = new JsApiTicket();
jsApiTicket.setTicket(rootNode.get("ticket").textValue());
jsApiTicket.setExpiresIn(toInt(rootNode.get("expires_in").toString()));
}
return jsApiTicket;
}

public static Integer toInt(String str) {
if (str == null || str.equals("")) {
return null;
}
return Integer.valueOf(str);
}

}

这个公共类中用到了证书信任管理器类MyX509TrustManager

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

public class MyX509TrustManager implements X509TrustManager {

// 检查客户端证书
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

}

// 检查服务器端证书
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

}

// 返回受信任的X509证书数组
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}

}


线程中还用到了一个获取全局缓存的工具类ServletContextUtil

import javax.servlet.ServletContext;

import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;

@Component
public final class ServletContextUtil {
private static ServletContext serveltContext = null;

private ServletContextUtil(){};

public synchronized static ServletContext getServletContext() {

if(null == serveltContext) {
WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
serveltContext = webApplicationContext.getServletContext();
}
return serveltContext;
}
}

到这里为止,获取access_token和获取jsapi_ticket就写好了,公共类中CommonUtil的httpsRequest()方法可能还有不同的写法,大家可以到网上再了解。


关于获取jsapi_ticket,在官方文档【微信JS-SDK说明文档】的【附录1】中有详细说明。


请一定详细阅读【附录1】,这里讲了如何获得签名,码在【附录6-DEMO页面和示例代码】,URL是http://demo.open.weixin.qq.com/jssdk/sample.zip里面有Java代码写的如何获取签名。


我用的是SSM框架,把这个类改成了Controller。

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;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.kavo.utils.ServletContextUtil;

@Controller
@Scope("prototype")
public class InitAccessTokenController extends HttpServlet{

@Value("${WX_APPID}")
private String WX_APPID;

@RequestMapping("/initWXJSInterface")
public @ResponseBody Map<String, String> init(String url){
// 从全局缓存中取出jsapi_ticket
ServletContext servletContext = ServletContextUtil.getServletContext();
String jsapi_ticket = (String) servletContext.getAttribute("jsapi_ticket");

Map<String, String> ret = sign(jsapi_ticket, url);

System.out.println("currurl = "+ url);

// 注意 URL 一定要动态获取,不能 hardcode
// for (Map.Entry entry : ret.entrySet()) {
// System.out.println(entry.getKey() + ", " + entry.getValue());
// }
System.out.println("signature =" + ret.get("signature"));
return ret;
}

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 string1;
String signature = "";

// 注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsapi_ticket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
System.out.println(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();
}

ret.put("url", url);
ret.put("appId",WX_APPID);
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;
}

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

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

}

字段WX_APPID是写在配置文件中的,新建一个config.properties,在springmvc的配置文件中加载这个文件。因为步骤三中需要appid这个参数。sign方法中要记得把WX_APPID添加到ret中

微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈


这个时候再看步骤三,这些参数都有了,jsApiList可以看附录

微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

第四步:是wx.ready(){}


微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

然后把分享到朋友圈的这段代码写到wx.ready中,自己设置参数就行了,imgUrl要填写的是图片的url


微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈

再啰嗦一句,如果wx.config中的debug参数为true,每次调用都会弹出调用的信息,详细请看【微信JS-SDK说明文档】中的接口调用说明。下面是前台页面的代码:

<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var currurl = decodeURIComponent(location.href.split('#')[0]);

//ajax注入权限验证
$.ajax({
url : "initWXJSInterface",
dataType : 'json',
data : {
'url' : currurl
},
complete : function(XMLHttpRequest, textStatus) {},
error : function(XMLHttpRequest, textStatus, errorThrown) {
alert("发生错误:" + errorThrown);
},
success : function(res) {
var appId = res.appId;
var nonceStr = res.nonceStr;
var jsapi_ticket = res.jsapi_ticket;
var timestamp = res.timestamp;
var signature = res.signature;
// alert(appId +" " + nonceStr +" "+jsapi_ticket+" "+timestamp+" "+signature);
wx.config({
debug : false, //开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId : appId, //必填,公众号的唯一标识
timestamp : timestamp, // 必填,生成签名的时间戳
nonceStr : nonceStr, //必填,生成签名的随机串
signature : signature, // 必填,签名,见附录1
jsApiList : [ 'onMenuShareAppMessage', 'onMenuShareTimeline' ] //必填,需要使用的JS接口列表,所有JS接口列表 见附录2
}); // end wx.config


wx.ready(function() {
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
/*
wx.checkJsApi({
jsApiList : [ 'onMenuShareAppMessage' ], // 需要检测的JS接口列表,所有JS接口列表见附录2,
success : function(res) {
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
alert(res.checkResult);
alert(res.errMsg);
}
}); // end checkJsApi
*/
wx.onMenuShareAppMessage({
title : '分享好友标题', // 分享标题
desc : '分享好友描述', // 分享描述
link : currurl, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl : 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1505419265109&di=cc30743d364e5ae89172c62a662e1321&imgtype=0&src=http%3A%2F%2Fpic.qqtn.com%2Fup%2F2017-6%2F14973136731543515.jpg', // 分享图标
type : '', // 分享类型,music、video或link,不填默认为link
dataUrl : '', // 如果type是music或video,则要提供数据链接,默认为空
success : function() {
// 用户确认分享后执行的回调函数
// alert('share successful');
},
cancel : function() {
// 用户取消分享后执行的回调函数
// alert('share cancel');
}
}); // end onMenuShareAppMessage


wx.onMenuShareTimeline({
title : '分享朋友圈标题', // 分享标题
link : currurl, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl : 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1505419265109&di=cc30743d364e5ae89172c62a662e1321&imgtype=0&src=http%3A%2F%2Fpic.qqtn.com%2Fup%2F2017-6%2F14973136731543515.jpg', // 分享图标
success : function() {
// 用户确认分享后执行的回调函数
},
cancel : function() {
// 用户取消分享后执行的回调函数
}
}); // end onMenuShareTimeline
}); // end ready

wx.error(function(res) {
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
alert('error');
});
} // end success
}); // end ajax

}); // end document

</script>

把项目发布到服务器上,然后手机访问,分享出来的就是上面这段js里写的标题,描述和图片了。