1. 程式人生 > >微信公眾號開發 自定義分享 從前臺到Java後臺 呼叫微信JS介面分享朋友圈

微信公眾號開發 自定義分享 從前臺到Java後臺 呼叫微信JS介面分享朋友圈

20180811寫在前面的話

有很多人遇到問題之後問我,結果大多數是因為配置問題,所以請詳細閱讀前面的配置步驟。

20181016注意事項

文中原始碼下載地址

上面這個地址是我當時做的整個專案的原始碼,包含了一些業務在裡面,用的是SSM框架,現在寫了一個SpringBoot版本的,只有頁面分享的程式碼,不包含業務的程式碼,簡單清晰。原始碼都只需要1積分。另外,在官方文件中看到,以前寫的分享介面即將廢棄,並且給上了新的介面,程式碼中的介面用的是以前的介面,所以下載之後建議修改成新的介面

SpringBoot版本下載地址

正文

我這幾天做的是自定義分享到朋友圈和分享給好友,能夠自己設定分享出來的標題,描述和圖片。

自定義分享到朋友圈需要呼叫微信的JS介面,個人公眾號和企業公眾號的擁有的介面許可權都不一樣。企業認證過的公眾號擁有的介面許可權會多一些。

那需要怎麼做呢,官方文件的【微信JS-SDK說明文件】中寫了JSSDK的使用步驟。

第一步

繫結域名,在【公眾號設定】的【功能設定】中設定JS介面安全域名網頁授權域名,做這兩步都要下載一個txt檔案。

我用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

});

這個時候我蒙圈了,這什麼玩意,這些引數是自己輸嗎?在哪獲得這些引數呢。於是我在公眾號裡努力找也只找到了appID,其他的引數怎麼獲得?timestamp和nonceStr還好解決,signature怎麼獲得,註釋裡寫見附錄1。

於是我翻到附錄1

這裡寫到生成簽名先要了解jsapi_ticket,而jsapi_ticket是通過access_token來獲取的。

接下來看看access_token是什麼,官方文件中【開始開發】中【獲取access_token】中有說明,公眾號可以使用AppIDAppSecret呼叫本介面來獲取access_token

AppID在微信公眾號的【開發】的【基本配置】有,【基本配置】中也有AppSecret,獲取就行了,真是得來全不費功夫,但是要用文件記下AppID和AppSecret,獲取access_token會用到。

這裡還有個IP白名單只有將IP地址設定為公眾號的IP白名單,才能成功呼叫該介面。AppID和AppSecret可在“微信公眾平臺-開發-基本配置”頁中獲得(需要已經成為開發者,且帳號沒有異常狀態)。呼叫介面時,請登入“微信公眾平臺-開發-基本配置”提前將伺服器IP地址新增到IP白名單中,點選檢視設定方法,否則將無法呼叫成功。可以設定多個,輸入一個IP地址之後回車就行了,我遇到過幾次情況就是在公司那個地方的公網IP和我住的地方的公網IP不一樣,導致我在家的時候出現了後面的access_token獲取不到的情況。記得要配置線上環境的IP地址

現在AppIDAppSecret都有了,怎麼獲得access_token。前面看的附錄1中寫了全域性快取jsapi_ticketaccess_token,官方文件【獲取access_token】中也有獲得access_tokenURL

https請求方式: GET

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

定義一個類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,建立一個InitAccessTokenServlet,Tomcat啟動就初始化這個Servlet。

建立一個InitAccessTokenServlet

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的執行緒

建立一個AccessTokenThread

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的執行緒

建立一個JSApiTicketThread

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

建立一個公共類CommonUtil

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("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

建立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

建立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就寫好了,公共類中CommonUtilhttpsRequest()方法可能還有不同的寫法,大家可以到網上再瞭解。

關於獲取jsapi_ticket,在官方文件【微信JS-SDK說明文件】的【附錄1】中有詳細說明。

我用的是SSM框架,把這個類改成了Controller

建立InitAccessTokenController

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 +
                 "&timetamp=" + 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中

這個時候再看步驟三,這些引數都有了,jsApiList可以看附錄

第四步

然後把分享到朋友圈的這段程式碼寫到wx.ready中,自己設定引數就行了,imgUrl要填寫的是圖片的url

再囉嗦一句,如果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裡寫的標題,描述和圖片了。

總結

  • 微信公眾號裡面配置JS介面安全域名,網頁授權域名,IP白名單
  • 分享的頁面中引入微信JS檔案
  • AppID和AppSecret在微信公眾號獲取
  • 通過AppID和AppSecret獲取access_token
  • 通過access_token獲取jsapi_ticket
  • 在分享的頁面中獲取後臺的appid,jsapi_ticket等資料
  • 在分享的頁面中的JavaScript程式碼通過wx.config介面注入許可權驗證配置,需要後臺的appid,jsapi_ticket等資料
  • 在wx.config介面之後通過wx.ready處理成功驗證
  • 把呼叫的JS介面寫在wx.config中

20180606補充:

1、前臺的程式碼新增到需要自定義分享的頁面中。

2、appidjsapi_ticketsignature等資料需要在頁面中載入時請求後臺獲取,不能在頁面中自己定義賦值。

3、invalid url domain錯誤:

  • 檢查當前頁面所在域名和JS介面安全域名是否一致
  • 檢查AppID和公眾號後臺的AppID是否一致
  • JS介面安全域名配置一級域名