釘釘提供的內網穿透之HTTP穿透:https://www.cnblogs.com/pxblog/p/13862376.html
網頁分享到微信中如何顯示標題圖,如果自定義標題圖,描述,顯示效果如下
官網接口地址;https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
加入maven依賴
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>[4.1.12,)</version> </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20171018</version> </dependency>
HttpClientUtil.java
package com.test.cms.share; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.HttpResponseException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; public class HttpClientUtil { private static class SingletonHolder{ private final static HttpClientUtil INSTANCE=new HttpClientUtil(); } private HttpClientUtil(){} public static HttpClientUtil getInstance(){ return SingletonHolder.INSTANCE; } public String get(String url){ CharsetHandler handler = new CharsetHandler("UTF-8"); CloseableHttpClient client = null; try { HttpGet httpget = new HttpGet(new URI(url)); HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); client= httpClientBuilder.build(); client = (CloseableHttpClient) wrapClient(client); return client.execute(httpget, handler); } catch (Exception e) { //e.printStackTrace(); return ""; }finally { try { if(client!=null){ client.close(); } } catch (IOException e) { e.printStackTrace(); } } } public static String post(String url, String params,String contentType) { //創建HttpClientBuilder HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); //HttpClient CloseableHttpClient client = httpClientBuilder.build(); client = (CloseableHttpClient) wrapClient(client); HttpPost post = new HttpPost(url); CloseableHttpResponse res = null; try { StringEntity s = new StringEntity(params,"UTF-8"); if(StringUtils.isBlank(contentType)){ s.setContentType("application/json"); } s.setContentType(contentType); s.setContentEncoding("utf-8"); post.setEntity(s); res = client.execute(post); HttpEntity entity = res.getEntity(); return EntityUtils.toString(entity, "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { res.close(); client.close(); } catch (IOException e) { e.printStackTrace(); } } return ""; } public static String post(String urlStr,String xmlInfo) { String line1 = ""; try { URL url = new URL(urlStr); URLConnection con = url.openConnection(); con.setDoOutput(true); //con.setRequestProperty("Pragma:", "no-cache"); con.setRequestProperty("Cache-Control", "no-cache"); con.setRequestProperty("Content-Type", "text/xml"); OutputStreamWriter out = new OutputStreamWriter(con .getOutputStream()); out.write(new String(xmlInfo.getBytes("utf-8"))); out.flush(); out.close(); BufferedReader br = new BufferedReader(new InputStreamReader(con .getInputStream())); String line = ""; for (line = br.readLine(); line != null; line = br.readLine()) { line1+=line; } return new String(line1.getBytes(),"utf-8"); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } private class CharsetHandler implements ResponseHandler<String> { private String charset; public CharsetHandler(String charset) { this.charset = charset; } public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException { StatusLine statusLine = response.getStatusLine(); if (statusLine.getStatusCode() >= 300) { throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); } HttpEntity entity = response.getEntity(); if (entity != null) { if (!StringUtils.isBlank(charset)) { return EntityUtils.toString(entity, charset); } else { return EntityUtils.toString(entity); } } else { return null; } } } private static HttpClient wrapClient(HttpClient base) { try { SSLContext ctx = SSLContext.getInstance("TLSv1"); X509TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; ctx.init(null, new TrustManager[] { tm }, null); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(ctx, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); return httpclient; } catch (Exception ex) { return null; } } }
RandomStr.java
package com.test.cms.share; import java.util.Random; public class RandomStr { private static char ch[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1' };//最后又重復兩個0和1,因為需要湊足數組長度為64 private static Random random = new Random(); //生成指定長度的隨機字符串 public static String createRandomString(int length) { if (length > 0) { int index = 0; char[] temp = new char[length]; int num = random.nextInt(); for (int i = 0; i < length % 5; i++) { temp[index++] = ch[num & 63];//取后面六位,記得對應的二進制是以補碼形式存在的。 num >>= 6;//63的二進制為:111111 // 為什么要右移6位?因為數組里面一共有64個有效字符。為什么要除5取余?因為一個int型要用4個字節表示,也就是32位。 } for (int i = 0; i < length / 5; i++) { num = random.nextInt(); for (int j = 0; j < 5; j++) { temp[index++] = ch[num & 63]; num >>= 6; } } return new String(temp, 0, length); } else if (length == 0) { return ""; } else { throw new IllegalArgumentException(); } } public static void main(String[] args) { System.out.println(createRandomString(16)); } }
Sha1.java
package com.test.cms.share; import java.security.MessageDigest; public class Sha1 { private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static String getFormattedText(byte[] bytes) { int len = bytes.length; StringBuilder buf = new StringBuilder(len * 2); // 把密文轉換成十六進制的字符串形式 for (int j = 0; j < len; j++) { buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); buf.append(HEX_DIGITS[bytes[j] & 0x0f]); } return buf.toString(); } public static String encode(String str) { if (str == null) { return null; } try { MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); messageDigest.update(str.getBytes()); return getFormattedText(messageDigest.digest()); } catch (Exception e) { throw new RuntimeException(e); } } }
控制器類
ShareController.java
package com.test.cms.share; import cn.hutool.json.JSONUtil; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.HttpResponseException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.json.JSONException; import org.json.JSONObject; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.net.URI; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; @Controller public class ShareController { /** * 微信公眾號的appid */ private String appid="wxd"; /** * 微信公眾號的appSecret */ private String secret="ebded33"; /** * 這是跳轉到分享的頁面 * @return */ @RequestMapping(value = "/to_detail") public String share(){ return "/index/share"; } /** * 獲取微信分享配置的請求 方法只寫了主要方法,需要根據自己的要求 完善代碼 * @param url 前台傳過來的當前頁面的請求地址 * @return */ @RequestMapping(value = "/get_wx_config") @ResponseBody public String share(String url){ long timestamp = System.currentTimeMillis() / 1000; String noncestr = RandomStr.createRandomString(16); String ticket =getJsapiTicket(); String str = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url; System.out.println("簽名str:" + str); String signature = Sha1.encode(str); //這只是簡單寫法,沒有做錯誤判斷 Map map=new HashMap(); map.put("appId",appid); map.put("timestamp",timestamp); map.put("nonceStr",noncestr); map.put("signature",signature); //這里使用了hutool工具將map轉為String String json = JSONUtil.toJsonStr(map); return json; } /** * 官方文檔:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62 * * 獲取jsapi_ticket * * * 生成簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號用於調用微信JS接口的臨時票據。 * 正常情況下,jsapi_ticket的有效期為7200秒,通過access_token來獲取。 * 由於獲取jsapi_ticket的api調用次數非常有限,頻繁刷新jsapi_ticket會導致api調用受限,影響自身業務,開發者必須在自己的服務全局緩存jsapi_ticket 。 * * 參考以下文檔獲取access_token(有效期7200秒,開發者必須在自己的服務全局緩存access_token): * https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html * * 用第一步拿到的access_token 采用http GET方式請求獲得jsapi_ticket(有效期7200秒,開發者必須在自己的服務全局緩存jsapi_ticket): * https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi * * @return */ public String getJsapiTicket() { String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"; tokenUrl = tokenUrl + "&appid=" + appid + "&secret=" + secret; JSONObject tokenJson=new JSONObject(); tokenJson=getUrlResponse(tokenUrl); System.out.println("tokenJson:"+tokenJson.toString()); String token=""; try { token=tokenJson.getString("access_token"); } catch (JSONException e) { e.printStackTrace(); System.out.println("報錯了"); return null; } String jsapiTicketUrl="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi"; JSONObject jsapiTickeJson=new JSONObject(); System.out.println("getJsapiTicket:獲取token:"+token); if(StringUtils.isNotBlank(token)){ jsapiTicketUrl = jsapiTicketUrl.replace("ACCESS_TOKEN",token); jsapiTickeJson=getUrlResponse(jsapiTicketUrl); System.out.println("tokenJson:"+jsapiTickeJson.toString()); try { return (String) jsapiTickeJson.get("ticket"); } catch (JSONException e) { e.printStackTrace(); return null; } }else{ return null; } } private JSONObject getUrlResponse(String url){ CharsetHandler handler = new CharsetHandler("UTF-8"); try { HttpGet httpget = new HttpGet(new URI(url)); HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); //HttpClient CloseableHttpClient client = httpClientBuilder.build(); client = (CloseableHttpClient) wrapClient(client); return new JSONObject(client.execute(httpget, handler)); } catch (Exception e) { e.printStackTrace(); return null; } } private static HttpClient wrapClient(HttpClient base) { try { SSLContext ctx = SSLContext.getInstance("TLSv1"); X509TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; ctx.init(null, new TrustManager[] { tm }, null); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(ctx, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); return httpclient; } catch (Exception ex) { return null; } } private class CharsetHandler implements ResponseHandler<String> { private String charset; public CharsetHandler(String charset) { this.charset = charset; } public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException { StatusLine statusLine = response.getStatusLine(); if (statusLine.getStatusCode() >= 300) { throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); } HttpEntity entity = response.getEntity(); if (entity != null) { if (!StringUtils.isBlank(charset)) { return EntityUtils.toString(entity, charset); } else { return EntityUtils.toString(entity); } } else { return null; } } } }
頁面主要代碼、這里展示的是分享給朋友、分享到朋友圈代碼,其他分享可以具體看官方接口代碼
share.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>分享頁面給朋友,朋友圈</title> </head> <body> </body> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script> <script type="text/javascript"> $(function () { //當前頁面的url地址 var currUrl = decodeURIComponent(location.href.split('#')[0]); $.ajax({ url: "/get_wx_config", dataType : "json", data: { 'url': currUrl }, error: function (res) { console.log(res); alert("發生錯誤"); }, success: function (res) { console.log(res); var appId = res.appId; var nonceStr = res.nonceStr; var timestamp = res.timestamp; var signature = res.signature; wx.config({ debug: false, //開啟調試模式,開發階段可以改成true,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。 appId: appId, //必填,公眾號的唯一標識 timestamp: timestamp, // 必填,生成簽名的時間戳 nonceStr: nonceStr, //必填,生成簽名的隨機串 signature: signature, // 必填,簽名,見附錄1 jsApiList: [ //必填,需要使用的JS接口列表,所有JS接口列表 見附錄2 'updateAppMessageShareData', 'updateTimelineShareData' ] }); wx.ready(function () { //需在用戶可能點擊分享按鈕前就先調用 //分享給朋友”及“分享到QQ” wx.updateAppMessageShareData({ title: '朋友我是標題', // 分享標題 desc: '朋友 我是描述', // 分享描述 link: '', // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致 imgUrl: '', // 分享圖標 success: function (res) { // 設置成功 } }) //分享到朋友圈”及“分享到QQ空間 wx.updateTimelineShareData({ title: '朋友圈我是標題', // 分享標題 link: '', // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公眾號JS安全域名一致 imgUrl: '', // 分享圖標 success: function (res) { // 設置成功 } }) }); wx.error(function (res) { }); } }); }); </script> </html>
設置公眾號的js安全域名
把域名放進去,不用加http的前綴,點擊保存即可
保存之前要把文件下載下來放到項目根目錄下,如果是開發環境的springboot項目可以參照這個教程做
https://www.cnblogs.com/pxblog/p/13445128.html