本文最后更新于:2025年2月18日 下午
网站集成微信分享功能
说明:文章具有时效性,若您此刻的时间距离文章发布时间过长,无法保证此文章依旧有效,请自行斟酌。本文写于2024/10/26
序言
写在前面的话:
要想实现在微信分享出去的链接携带有网站图标,网站标题和网站描述。只能按照微信官方文档中的要求,使用微信js
文件的SDK
。
这种必须是在微信中用微信自带的浏览器打开的网页下才能生效。**而且不能是直接复制链接发送给好友然后点开网站的方式进去,这种状态下即便进去,分享网站后,出来的内容也只有一条网址而已。对于已经生成带有图标标题和描述的分享卡片,这种类型的链接点进去打开网站是可以正常分享出这种效果的。**普通网站想分享还是应该采用二维码展示的方式。
如果你的网站要适配移动端,希望在微信打开后方便分享给其他微信用户或者朋友圈,那我这篇文章将会有所帮助。
准备
一、微信服务号
首先,要实现这种类似的功能需要一个企业认证的微信服务号,而且认证必须在有效期内。若只是个人测试学习,可以申请使用公众平台测试号。测试号拥有全部功能,会生成一个虚拟的微信公众号,需要关注这个号,才能正常使用。
二、服务器
一台拥有公网IP
的服务器。**因为微信公众平台需要测试你要引入此功能的项目地址是可用的。**若是个人测试,为了尽量节省开销,可以考虑开通个按量付费的服务器,选择停机不收费,用的时候打开,不用的时候关掉就好了,只不过每次启用公网IP
地址都会变。按自己需求来吧,这里不再赘述。
配置
测试号与微信服务号信息位置对照表
测试号 |
服务号 |
测试号信息 |
设置与开发 > 开发接口管理 > 基本配置 > 公众号开发信息 |
接口配置信息 |
设置与开发 > 开发接口管理 > 基本配置 > 服务器配置 |
JS 接口安全域名 |
设置与开发 > 公众号设置 > 功能设置 > JS 接口安全域名 |
微信服务器接口配置
这里需要你的项目中开放一个供微信请求的接口,微信通过成功调用这个接口来判断你的项目是否可用。
注:由于测试号无法指定令牌加密方式,所以接下来就以明文模式来展示后端接口的示例。
其他加密模式请参阅官方文档自行实现。当然你不验证token
直接返回echostr
,也能直接验证成功!成功后,即便再次启动项目,接口配置信息也无需再次配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
@GetMapping("/wxCheck") public String wxCheck( @RequestParam(value = "signature") String signature, @RequestParam(value = "timestamp") String timestamp, @RequestParam(value = "nonce") String nonce, @RequestParam(value = "echostr") String echostr ){ String token = "myWXToken"; String[] arr = {token, timestamp, nonce};
Arrays.sort(arr);
StringBuilder result = new StringBuilder(); for (String s : arr) { result.append(s); } String s = SHA1(String.valueOf(result)); if (!s.equals(signature)) { System.out.println("Token值匹配失败!"); return null; } System.out.println("Token值匹配成功!"); Map<String,Object> map = new HashMap<>(); map.put("signature",signature); map.put("timestamp",timestamp); map.put("nonce",nonce); map.put("echostr",echostr); System.out.println("来自微信的消息:"+map); return echostr; }
|
注意:需要先启动项目,再配置接口,因为确定配置接口时,微信会请求我们填写的请求接口。当成功返回微信本身请求携带的随机字符串给他后,验证成功!
JS
接口安全域名
这里配置服务器地址或绑定服务器的已备案的域名。
这里测试号与服务号有些区别:
服务号的官方提示信息:
设置JS
接口安全域名后,公众号开发者可在该域名下调用微信开放的JS
接口。
注意事项:
1、可填写五个域名或路径(例:wx.qq.com
或wx.qq.com/mp
),需使用由字母、数字、“-”或中文组成的合法域名,不支持IP
地址、端口号及短链域名。
2、填写的域名须通过ICP
备案的验证。
3、 将文件MP_verify_ffPvlU5jlXYhLLC2.txt
上传至填写域名或路径指向的web服务器(或虚拟主机)的目录(若填写域名,将文件放置在域名根目录下,例如wx.qq.com/MP_verify_ffPvlU5jlXYhLLC2.txt
;若填写路径,将文件放置在路径目录下,例如wx.qq.com/mp/MP_verify_ffPvlU5jlXYhLLC2.txt
),并确保可以访问。
4、 一个自然月内最多可修改并保存五次,本月剩余保存次数:4
服务号,需要项目开放静态资源路径以供微信访问,若开启了拦截器或过滤器,则需要将此放行。
测试号的提示信息:
设置JS
接口安全域后,通过关注该测试号,开发者即可在该域名下调用微信开放的JS
接口
IP
白名单
服务号需要将服务器的ip
地址配置在白名单里,否则会报异常。
流程
- 引入微信
SDK
的js
文件
js
携带当前页地址参数请求后端接口
- 后端通过
appid
和appsecret
参数,使用OAuth2.0
的客户端认证模式,请求获取微信access_token
- 后端通过
access_token
获取api_ticket
- 生成当前时间戳
timestamp
和随机字符串nonceStr
- 通过
SHA1
签名算法生成签名signature
- 将签名signature,时间戳timestamp,随机字符串nonceStr和appID返回给前端。
- 前端接收到信息后,完成微信配置
- 前端定义分享的格式。
- 测试微信分享效果。
开始
前端页面完整代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| <!DOCTYPE html> <html> <head> <title>微信分享示例</title> <meta charset="UTF-8"> </head> <body> <h1>欢迎使用微信分享功能!</h1>
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <script src="/static/dist/js/jquery-3.6.0.min.js"></script> <script> $.ajax({ async : false, type: "POST", url: "/demo", data : { URL : encodeURIComponent(window.location.href.split("#")[0]) }, success: function (data) { console.log(data); wx.config({ debug: true, appId: data.appId, timestamp: data.timestamp, nonceStr: data.nonceStr, signature: data.signature, jsApiList: [ 'updateAppMessageShareData', 'updateTimelineShareData' ] }); console.log("wx配置结束!"); wx.ready(function () { shareData = { title: localStorage.getItem("title"), desc: localStorage.getItem("Description"), link: window.location.href, imgUrl: "http://你的域名/你的图片.jpg", success: function () { console.log('分享成功'); }, cancel: function () { console.log('取消分享'); } }; wx.updateTimelineShareData(shareData); wx.updateAppMessageShareData(shareData); });
wx.error(function () { console.log('微信JS SDK加载失败'); });
}, error: function (xhr, status, error) { console.error('提交失败: ', error); } }) </script> </body> </html>
|
后端接口:
demo
页面控制,引入thymeleaf
视图解析器依赖测试即可。
1 2 3 4 5 6 7 8
| @Controller public class IndexController {
@GetMapping("/demo") public String demo() { return "demo"; } }
|
请求接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
|
@Controller @RequiredArgsConstructor public class WxController {
@ResponseBody @GetMapping("/wxCheck") public String wxCheck( @ApiParam(name = "signature",value = "微信加密签名") @RequestParam(value = "signature") String signature, @ApiParam(name = "timestamp",value = "时间戳") @RequestParam(value = "timestamp") String timestamp, @ApiParam(name = "nonce",value = "随机数") @RequestParam(value = "nonce") String nonce, @ApiParam(name = "echostr",value = "随机字符串") @RequestParam(value = "echostr") String echostr ){
String token = "weChatTest"; String[] arr = {token, timestamp, nonce};
Arrays.sort(arr);
StringBuilder result = new StringBuilder(); for (String s : arr) { result.append(s); } String s = SHA1(String.valueOf(result));
if (!s.equals(signature)) { System.out.println("Token值匹配失败!"); return null; } System.out.println("Token值匹配成功!");
Map<String,Object> map = new HashMap<>(); map.put("signature",signature); map.put("timestamp",timestamp); map.put("nonce",nonce); map.put("echostr",echostr); System.out.println("来自微信的消息:"+map); return echostr; }
@ResponseBody @PostMapping("demo") public Map<String,Object> demo( HttpSession session, String URL) {
String url = ""; String appId = "你的appid"; String appSecret = "你的appSecret"; try { url = java.net.URLDecoder.decode(URL,"UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); System.out.println("UTF-8格式地址解析异常!"); retuen null; } String access_token=(String) session.getAttribute("access_token"); if(access_token==null){ access_token = getAccessToken(appId, appSecret); if (access_token ==null){ System.out.println("获取access_token失败!"); return null; } session.setAttribute("access_token", access_token); } String jsapi_ticket = (String) session.getAttribute("access_ticket"); if(jsapi_ticket==null){ jsapi_ticket = getTicket(access_token); if (jsapi_ticket ==null){ System.out.println("获取jsapi_ticket失败!"); return null; } session.setAttribute("jsapi_ticket", jsapi_ticket); }
String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16); String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
System.out.println("accessToken:"+access_token+"\njsapi_ticket:"+jsapi_ticket+"\n时间戳:"+timestamp+"\n随机字符串:"+noncestr);
String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
String signature =SHA1(str); if (signature == null){ System.out.println("SHA1加密失败!"); return null; } System.out.println("参数:"+str+"\n签名:"+signature); Map<String,Object> map = new HashMap<>(); map.put("signature",signature); map.put("appId",appId); map.put("timestamp",timestamp); map.put("nonceStr",noncestr); return map; }
public static String getAccessToken(String appid,String secret) {
try(CloseableHttpClient httpClient = HttpClients.createDefault()) { String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appid+"&secret="+secret; HttpGet httpGet = new HttpGet(url); CloseableHttpResponse response = httpClient.execute(httpGet); System.out.println(response.getStatusLine()); if (response.getStatusLine().getStatusCode() == 200) { String result = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println(result); Map<String, Object> map = JSON.parseObject(result); return (String) map.get("access_token"); }else { System.out.println("==========="); } } catch (Exception e) { throw new RuntimeException(e); } return null; }
public static String getTicket(String access_token) {
try(CloseableHttpClient httpClient = HttpClients.createDefault()) { String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+access_token+"&type=jsapi"; HttpGet httpGet = new HttpGet(url); CloseableHttpResponse response = httpClient.execute(httpGet); System.out.println(response.getStatusLine()); if (response.getStatusLine().getStatusCode() == 200) { String result = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println(result); Map<String, Object> map = JSON.parseObject(result); return (String) map.get("ticket"); }else { System.out.println("==========="); } } catch (Exception e) { throw new RuntimeException(e); } return null; }
public static String SHA1(String decript) { try { MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1"); digest.update(decript.getBytes()); byte messageDigest[] = digest.digest(); 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 null; } }
|
使用
这里需要将地址构造成二维码的形式,再用微信扫描打开。
草料二维码
因为微信对直接访问直链地址有限制,直接在聊天记录里点链接打开,分享出的信息也只有直链地址,没有标题、描述和图标的。
打开后,进入我们的那个demo
页面,前端如果在微信配置中,开启了debug,那么这时就会弹出提示框:errMsg:config:ok
这样就成功了,点击右上角然后分享给自己,就可以看到带有标题、描述和图标的分享卡片效果了。
最后
弄好后,可以先在电脑上打开页面,这样可以在控制台看到后端请求的结果,前端数据载入情况等。
控制台都正常,没有问题信息的话,再用手机扫码访问测试。
附录5-常见错误及解决方法
参考资料
- 微信内分享网页自定义标题,图片,描述
- 微信公众号官方文档 - 开始开发 - 接入指南
- 微信公众号官方文档 - 开始开发 - 接口测试号申请
- 微信公众号官方文档 - 微信网页开发 -
JS-SDK
说明文档