微信分享
文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
1、微信后台配置
设置与开发/ 公众号设置 / 功能设置 / JS接口安全域名 (配置网页所用的域名, 【需通过备案】) 基本配置 / IP白名单 (将服务器公网ip配置到此处)
2、后端代码
PHP版 参照官网给出的示例,稍作改造
PHP版本 7.1
目录结构:
./ JSSDK.php 微信分享必要参数获取基本接口 JSSDKCache.php 通过继承,实现对access_token、jsapi_ticket数据的缓存 RedisService.php Redis缓存服务 WeixinService.php 配置具体的微信参数,调用JSSDKCache.php WeixinController.php 对外提供数据接口
用到的第三方库
# 网络请求 composer require rmccue/requests # Redis客户端 composer require predis/predis
JSSDK.php
<?php namespace app\utils\weixin; use Requests; /** * 微信分享必要参数获取基本接口 * * Class JSSDK * @package app\utils\weixin */ class JSSDK { // 开发者ID protected $appId; // 开发者密码(AppSecret) protected $appSecret; public function __construct($appId, $appSecret) { $this->appId = $appId; $this->appSecret = $appSecret; } public function getSignPackage($jsapi_ticket, $url) { $timestamp = time(); $nonceStr = $this->createNonceStr(); // 这里参数的顺序要按照 key 值 ASCII 码升序排序 $string = "jsapi_ticket=$jsapi_ticket&noncestr=$nonceStr×tamp=$timestamp&url=$url"; $signature = sha1($string); $signPackage = array( "appId" => $this->appId, "nonceStr" => $nonceStr, "timestamp" => $timestamp, "url" => $url, "signature" => $signature, "rawString" => $string ); return $signPackage; } private function createNonceStr($length = 16) { $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $str = ""; for ($i = 0; $i < $length; $i++) { $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $str; } /** * 获取 Access token * https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html */ public function getAccessToken() { $params = [ 'grant_type' => 'client_credential', 'appid' => $this->appId, 'secret' => $this->appSecret ]; $url = "https://api.weixin.qq.com/cgi-bin/token?" . http_build_query($params); $data = self::httpGet($url); if ($data) { return empty($data['access_token']) ? null : $data['access_token']; } else { return null; } } /** * 获取 jsapi_ticket * https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62 */ public function getJsapiTicket($access_token) { $params = [ 'access_token' => $access_token, 'type' => 'jsapi', ]; $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?" . http_build_query($params); $data = self::httpGet($url); if ($data) { return empty($data['ticket']) ? null : $data['ticket']; } else { return null; } } private function httpGet($url) { $response = Requests::get($url); if ($response->success) { return json_decode($response->body, true); } else { return null; } } }
JSSDKCache.php
<?php namespace app\utils\weixin; use app\service\RedisService; /** * 自定义实现缓存: access_token、jsapi_ticket * * Class JSSDKCache * @package app\utils\weixin */ class JSSDKCache extends JSSDK { public function getSignData($url) { $jsapi_ticket = $this->getJsapiTicketFromCache(); $data = $this->getSignPackage($jsapi_ticket, $url); return $data; } public function getAccessTokenFromCache() { $cache_key = 'access_token.' . $this->appId; $access_token = RedisService::get($cache_key); if (!$access_token) { // 获取access_token $access_token = $this->getAccessToken(); // 7200 RedisService::set($cache_key, $access_token, 7100); } return $access_token; } public function getJsapiTicketFromCache() { $cache_key = 'jsapi_ticket.' . $this->appId; $jsapi_ticket = RedisService::get($cache_key); if (!$jsapi_ticket) { // 获取access_token $access_token = $this->getAccessTokenFromCache(); $jsapi_ticket = parent::getJsapiTicket($access_token); // 7200 RedisService::set($cache_key, $jsapi_ticket, 7100); } return $jsapi_ticket; } }
<?php namespace app\service; use Predis\Client; /** * 使用Redis 缓存 * * Class RedisService * @package app\service */ class RedisService { // 可能有多个网站,每个网站给一个前缀 private static $prefix = 'www'; public static function getClient() { $client = new Client(); return $client; } public static function getKey($key) { return self::$prefix . '.' . $key; } public static function get($key) { $client = self::getClient(); $key = self::getKey($key); return $client->get($key); } public static function set($key, $value, $expire = null) { //https://learnku.com/laravel/t/3563/using-predis-to-operate-the-redis-database-how-to-set-the-key-life-cycle-expires $client = self::getClient(); $key = self::getKey($key); if ($expire) { // SET key value [EX seconds] [PX milliseconds] [NX|XX] $client->set($key, $value, 'EX', $expire); } else { $client->set($key, $value); } } }
WeixinService.php
<?php namespace app\service; use app\utils\weixin\JSSDKCache; class WeixinService { // 公众号 开发者ID private static $APP_ID = ''; // 开发者密码(AppSecret) private static $APP_SECRET = ''; public static function getWeixinConfig($url) { $jssdk = new JSSDKCache(self::$APP_ID, self::$APP_SECRET); return $jssdk->getSignData($url); } }
WeixinController.php
<?php namespace app\api\controller; use app\BaseController; use app\service\WeixinService; class WeixinController extends BaseController { public function getWeixinConfig() { $url = input('url'); return WeixinService::getWeixinConfig($url); } }
3、前端代码weixin-share.html
方式一:后端渲染
<!--微信分享--> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <script> wx.config({ debug: false, // 开启调试模式 appId: '{$appId}', // 必填,公众号的唯一标识 timestamp: '{$timestamp}', // 必填,生成签名的时间戳 nonceStr: '{$nonceStr}', // 必填,生成签名的随机串 signature: '{$signature}',// 必填,签名 // 必填,需要使用的JS接口列表 jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'], // 可选,获取开放标签权限 openTagList: ['wx-open-launch-app'] }); // 自定义“分享给朋友”及“分享到QQ”按钮的分享内容 wx.ready(function () { //需在用户可能点击分享按钮前就先调用 wx.updateAppMessageShareData({ title: '{$title}', // 分享标题 desc: '{$summary}', // 分享描述 link: '{$url}', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: "{$cover_icon}", // 分享图标 success: function () { // 设置成功 } }); // 分享到朋友圈 wx.updateTimelineShareData({ title: '{$title}', // 分享标题 link: '{$url}', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: "{$cover_icon}", // 分享图标 success: function () { // 设置成功 } }) }); wx.error(function(res){ // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 console.log("config验证失败") }); </script>
方式二:前端通过接口获取
<!-- Axios --> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <!--微信分享--> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <script> url = window.location.href.split('#')[0]; axios.post('/api/getWeixinConfig', {url: url}).then(res=>{ let data = res.data.data; wx.config({ debug: false, // 开启调试模式 appId: data.appId, // 必填,公众号的唯一标识 timestamp: data.timestamp, // 必填,生成签名的时间戳 nonceStr: data.nonceStr, // 必填,生成签名的随机串 signature: data.signature,// 必填,签名 // 必填,需要使用的JS接口列表 jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'], // 可选,获取开放标签权限 // openTagList: ['wx-open-launch-app'] }); // 自定义“分享给朋友”及“分享到QQ”按钮的分享内容 wx.ready(function () { //需在用户可能点击分享按钮前就先调用 wx.updateAppMessageShareData({ title: '{{ detail.title }}', // 分享标题 desc: '{{ detail.summary }}', // 分享描述 link: data.url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: '{{ detail.head_image }}', // 分享图标 success: function () { // 设置成功 } }); // 分享到朋友圈 wx.updateTimelineShareData({ title: '{{ detail.title }}', // 分享标题 link: data.url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: '{{ detail.head_image }}', // 分享图标 success: function () { // 设置成功 } }) }); wx.error(function(res){ // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 console.log("config验证失败") }); }) </script>