首先确认微信的API只能在微信APP里面使用不能在其他浏览器中使用,所以你的网页都是针对微信网页的,如果不是微信浏览器打开的网页你就应该区别对待。
先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。 微信的开发不能在本地进行,必须在当前配置的域名下,并且必须使用80端口。
备注:登录后可在“开发者中心”查看对应的接口权限。
在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.0.0.js
请注意,如果你的页面启用了https,务必引入 https://res.wx.qq.com/open/js/jweixin-1.0.0.js ,否则将无法在iOS9.0以上系统中成功使用JSSDK
如需使用摇一摇周边功能,请引入 jweixin-1.1.0.js
备注:支持使用 AMD/CMD 标准模块加载方法加载
多数微信JDK开发者会卡在这一步。
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见附录1
jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
appId 你容易在你的公众号设置中拿到
timestamp 是当前时间的时间戳,这个你很容易拿到
nonceStr 是一个任意字符串,作为你签名的混淆字符
关键是signature不好拿到。
根据微信JDK的官方文档,你这个签名必须在服务器端完成,而且必须请求微信服务器两次才能拿到。
下面主要介绍怎么取到这个signature。
生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
成功返回如下JSON:
{ "errcode":0, "errmsg":"ok", "ticket":"sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg", "expires_in":7200 }
获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。
签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
即signature=sha1(string1)。
示例:
步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value
步骤2. 对string1进行sha1签名,得到signature:
0f9de62fce790f9a083d5c99e95740ceb90c27ed
注意事项
当你完成了上面的操作你就可以使用微信JDK提供的API了,如果这个通不过其他的你就不要想了。
下面提供PHP作为后台开发语言下的方法。
一共包括两个文件(wx.php和cache.php),其中一个是缓存类,用来缓存access_token和jsapi_ticket。
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>微信公众平台JDK API接口开发</title> </head> <body> <?php //引入缓存类 include('cache.php'); //实例化缓存类 $cache = new Cache(); //获取access_token的方法 function wx_get_token() { global $cache; $token = $cache->retrieve('access_token'); if (!$token) { //这个地方appid和secret改为公众号的appid和secret $res = file_get_contents('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=你的AppId&secret=你的secret'); $res = json_decode($res, true); $token = $res['access_token']; $cache->store('access_token', $token, 7200); } return $token; } //获取jsapi_ticket的方法 function wx_get_jsapi_ticket(){ global $cache; $ticket = ""; do{ $ticket = $cache->retrieve('wx_ticket'); if (!empty($ticket)) { break; } $token = $cache->retrieve('access_token'); if (empty($token)){ wx_get_token(); } $token = $cache->retrieve('access_token'); if (empty($token)) { break; } $url2 = sprintf("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi", $token); $res = file_get_contents($url2); $res = json_decode($res, true); $ticket = $res['ticket']; $cache->store('wx_ticket', $ticket, 7200); }while(0); return $ticket; } ?> <!--这里是PHP签名--> <?php $timestamp = time(); $wxnonceStr = '2016125162414'; $wxticket = wx_get_jsapi_ticket(); //把http://www.zhidao91.com/wx.php地址改为你们分享的地址,一定要全,如果是有参数,也要带上url上需要带上参数。 //注意顺序一定要按照参数名ASCII 码从小到大排序 $wxOri = 'jsapi_ticket='.$wxticket.'&noncestr='.$wxnonceStr.'×tamp='.$timestamp.'&url=http://www.zhidao91.com/wx.php'; //进行sha1签名,获取到微信JDK接口的signature $signature = sha1($wxOri); ?> <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> <script> wx.config({ debug: false, appId: '你的公众号appId', //这里是公众号appId timestamp:'<?php echo $timestamp; ?>', nonceStr: '<?php echo $wxnonceStr; ?>', signature: '<?php echo $signature; ?>', jsApiList: [ 'onMenuShareTimeline', 'onMenuShareAppMessage' ] }); </script> <script> // 微信JSSDK开发 wx.ready(function () { // 获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: '分享标题', // 分享标题 desc: "分享描述", // 分享描述 link:"http://www.zhidao91.com/wx.php",分享地址 imgUrl: "http://www.zhidao91.com/wp-content/uploads/2015/08/how-to-write-a-blog.jpg", // 分享图标 type: 'link', // 分享类型,music、video或link,不填默认为link }); // 分享到朋友圈 wx.onMenuShareTimeline({ title: '分享标题', // 分享标题 link: 'http://www.zhidao91.com/wx.php', // 分享地址 imgUrl: 'http://www.zhidao91.com/wp-content/uploads/2015/08/how-to-write-a-blog.jpg', // 分享的图标 fail: function (res) { //alert(JSON.stringify(res)); } }); }); </script> </body> </html>
这是使用PHP代码具体实现签名,并且使用微信分享到朋友圈和分享给朋友的两个接口的使用方法,其中涉及到的cache.php如下。
<?php /** * Simple Cache class * API Documentation: https://github.com/cosenary/Simple-PHP-Cache * * @author Christian Metz * @since 22.12.2011 * @copyright Christian Metz - MetzWeb Networks * @version 1.6 * @license BSD http://www.opensource.org/licenses/bsd-license.php */ class Cache { /** * The path to the cache file folder * * @var string */ private $_cachepath = 'cache/'; /** * The name of the default cache file * * @var string */ private $_cachename = 'default'; /** * The cache file extension * * @var string */ private $_extension = '.cache'; /** * Default constructor * * @param string|array [optional] $config * @return void */ public function __construct($config = null) { if (true === isset($config)) { if (is_string($config)) { $this->setCache($config); } else if (is_array($config)) { $this->setCache($config['name']); $this->setCachePath($config['path']); $this->setExtension($config['extension']); } } } /** * Check whether data accociated with a key * * @param string $key * @return boolean */ public function isCached($key) { if (false != $this->_loadCache()) { $cachedData = $this->_loadCache(); return isset($cachedData[$key]['data']); } } /** * Store data in the cache * * @param string $key * @param mixed $data * @param integer [optional] $expiration * @return object */ public function store($key, $data, $expiration = 0) { $storeData = array( 'time' => time(), 'expire' => $expiration, 'data' => serialize($data) ); $dataArray = $this->_loadCache(); if (true === is_array($dataArray)) { $dataArray[$key] = $storeData; } else { $dataArray = array($key => $storeData); } $cacheData = json_encode($dataArray); file_put_contents($this->getCacheDir(), $cacheData); return $this; } /** * Retrieve cached data by its key * * @param string $key * @param boolean [optional] $timestamp * @return string */ public function retrieve($key, $timestamp = false) { $cachedData = $this->_loadCache(); (false === $timestamp) ? $type = 'data' : $type = 'time'; if (!isset($cachedData[$key][$type])) return null; return unserialize($cachedData[$key][$type]); } /** * Retrieve all cached data * * @param boolean [optional] $meta * @return array */ public function retrieveAll($meta = false) { if ($meta === false) { $results = array(); $cachedData = $this->_loadCache(); if ($cachedData) { foreach ($cachedData as $k => $v) { $results[$k] = unserialize($v['data']); } } return $results; } else { return $this->_loadCache(); } } /** * Erase cached entry by its key * * @param string $key * @return object */ public function erase($key) { $cacheData = $this->_loadCache(); if (true === is_array($cacheData)) { if (true === isset($cacheData[$key])) { unset($cacheData[$key]); $cacheData = json_encode($cacheData); file_put_contents($this->getCacheDir(), $cacheData); } else { throw new Exception("Error: erase() - Key '{$key}' not found."); } } return $this; } /** * Erase all expired entries * * @return integer */ public function eraseExpired() { $cacheData = $this->_loadCache(); if (true === is_array($cacheData)) { $counter = 0; foreach ($cacheData as $key => $entry) { if (true === $this->_checkExpired($entry['time'], $entry['expire'])) { unset($cacheData[$key]); $counter++; } } if ($counter > 0) { $cacheData = json_encode($cacheData); file_put_contents($this->getCacheDir(), $cacheData); } return $counter; } } /** * Erase all cached entries * * @return object */ public function eraseAll() { $cacheDir = $this->getCacheDir(); if (true === file_exists($cacheDir)) { $cacheFile = fopen($cacheDir, 'w'); fclose($cacheFile); } return $this; } /** * Load appointed cache * * @return mixed */ private function _loadCache() { if (true === file_exists($this->getCacheDir())) { $file = file_get_contents($this->getCacheDir()); return json_decode($file, true); } else { return false; } } /** * Get the cache directory path * * @return string */ public function getCacheDir() { if (true === $this->_checkCacheDir()) { $filename = $this->getCache(); $filename = preg_replace('/[^0-9a-z\.\_\-]/i', '', strtolower($filename)); return $this->getCachePath() . $this->_getHash($filename) . $this->getExtension(); } } /** * Get the filename hash * * @return string */ private function _getHash($filename) { return sha1($filename); } /** * Check whether a timestamp is still in the duration * * @param integer $timestamp * @param integer $expiration * @return boolean */ private function _checkExpired($timestamp, $expiration) { $result = false; if ($expiration !== 0) { $timeDiff = time() - $timestamp; ($timeDiff > $expiration) ? $result = true : $result = false; } return $result; } /** * Check if a writable cache directory exists and if not create a new one * * @return boolean */ private function _checkCacheDir() { if (!is_dir($this->getCachePath()) && !mkdir($this->getCachePath(), 0775, true)) { throw new Exception('Unable to create cache directory ' . $this->getCachePath()); } elseif (!is_readable($this->getCachePath()) || !is_writable($this->getCachePath())) { if (!chmod($this->getCachePath(), 0775)) { throw new Exception($this->getCachePath() . ' must be readable and writeable'); } } return true; } /** * Cache path Setter * * @param string $path * @return object */ public function setCachePath($path) { $this->_cachepath = $path; return $this; } /** * Cache path Getter * * @return string */ public function getCachePath() { return $this->_cachepath; } /** * Cache name Setter * * @param string $name * @return object */ public function setCache($name) { $this->_cachename = $name; return $this; } /** * Cache name Getter * * @return void */ public function getCache() { return $this->_cachename; } /** * Cache file extension Setter * * @param string $ext * @return object */ public function setExtension($ext) { $this->_extension = $ext; return $this; } /** * Cache file extension Getter * * @return string */ public function getExtension() { return $this->_extension; } }
这是一个PHP的缓存工具类,作为缓存的工具。
上面的示例是PHP的,如果是其他后台语言只要转换成相应的逻辑就可以实现,注意两点,要使用微信的接口必须在绑定的域名下测试;签名必须先向微信请求到access_token,然后用access_token再去请求jsapi_ticket,最后用jsapi_ticket和相关的参数按照ASCII码从小到大进行sha1排序,最后拿到signature,然后用signature和相关参数去通过config接口(wx.config)注入权限验证配置。