微信頁面授權–(JS-SDK使用權限簽名算法)
使用方法:實例化此類,然後調用其中的getSignPackage()方法即可。
注意:其中涉及獲取access_token和jsapi_ticket需要自己緩存,不能頻繁獲取!
<?php
/**
* Author: helen
* CreateTime: 2016/4/11 10:39
* description: 微信頁面授權--(JS-SDK使用權限簽名算法)
*/
class JSSDK{
private $appId;
private $appSecret;
public function __construct($appId, $appSecret)
{
$this->appId = $appId;
$this->appSecret = $appSecret;
}
/*
* 獲取access_token
* (需要緩存,可利用數據庫存儲,不要頻繁刷新獲取)
* http請求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
* 接口請求參數
* 參數 是否必須 說明
grant_type 是 獲取access_token填寫client_credential
appid 是 第三方用戶唯一憑證
secret 是 第三方用戶唯一憑證密鑰,即appsecret
* 接口返回說明
* {"access_token":"ACCESS_TOKEN","expires_in":7200} access_token 獲取到的憑證 expires_in 憑證有效時間,單位:秒
* 接口錯誤說明
* {"errcode":40013,"errmsg":"invalid appid"}
* */
private function getAccessToken(){
$appId = $this->appId;
$appSecret = $this->appSecret;
$url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$appId.'&secret='.$appSecret;
$res = $this->api_request($url);
if(isset($res->access_token)){
return array(
'errcode' =>0,
'errmsg' =>'success',
'access_token' =>$res->access_token,
'expires_in' =>$res->expires_in
);
}else{
return array(
'errcode' =>$res->errcode,
'errmsg' =>$res->errmsg,
'access_token' =>null,
'expires_in' =>null
);
}
}
/*
* 獲取jsapi_ticket
* (有效期7200秒,開發者必須在自己的服務全局緩存jsapi_ticket)
* 請求方式:https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
* 接口返回值:JSON
* {
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}
* */
private function getJsApiTicket(){
$access_token_data = $this->getAccessToken();
if($access_token_data['errcode']==0){
$access_token = $access_token_data['access_token'];
$url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='.$access_token.'&type=jsapi';
$res = $this->api_request($url);
if($res->errcode==0){
return array(
'errcode' =>$res->errcode,
'errmsg' =>$res->errmsg,
'ticket' =>$res->ticket,
'expires_in' =>$res->expires_in
);
}else{
return array(
'errcode' =>$res->errcode,
'errmsg' =>$res->errmsg,
'ticket' =>null,
'expires_in' =>null
);
}
}else{
return array(
'errcode' =>$access_token_data['errcode'],
'errmsg' =>$access_token_data['errmsg'],
'ticket' =>null,
'expires_in' =>null
);
}
}
/*
* 簽名算法
* 簽名生成規則如下:參與簽名的字段包括noncestr(隨機字符串), 有效的jsapi_ticket, timestamp(時間戳), url(當前網頁的URL,不包含#及其後面部分) 。
* 1、對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)後,
* 2、使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1。
* 這裏需要注意的是所有參數名均爲小寫字符。對string1作sha1加密,字段名和字段值都採用原始值,不進行URL 轉義。
* */
/*
* 獲取隨機字符串
* mt_rand() 使用 Mersenne Twister 算法返回隨機整數。
* mt_rand(min,max)如果沒有提供可選參數 min 和 max,mt_rand() 返回 0 到 RAND_MAX 之間的僞隨機數。
* 想要 5 到 15(包括 5 和 15)之間的隨機數,用 mt_rand(5, 15)。
* 此函數rand()快四倍
* */
/*
* 1.簽名用的noncestr和timestamp必須與wx.config中的nonceStr和timestamp相同。
* 2.簽名用的url必須是調用JS接口頁面的完整URL。
* 3.出於安全考慮,開發者必須在服務器端實現簽名的邏輯。
* 注意:
* 確保你獲取用來簽名的url是動態獲取的,動態頁面可參見實例代碼中php的實現方式。
* 如果是html的靜態頁面在前端通過ajax將url傳到後臺簽名,前端需要用js獲取當前頁面除去'#'hash部分的鏈接(可用location.href.split('#')[0]獲取,而且需要encodeURIComponent),
* 因爲頁面一旦分享,微信客戶端會在你的鏈接末尾加入其它參數,如果不是動態獲取當前鏈接,將導致分享後的頁面簽名失敗。
* */
public function getSignPackage()
{
$jsapiTicket_data = $this->getJsApiTicket();
$nonceStr = $this->getNonceStr();
$timestamp = time();
$url = $this->getUrl();
if($jsapiTicket_data['errcode']==0){
$jsapiTicket = $jsapiTicket_data['ticket'];
// 這裏參數的順序要按照 key 值 ASCII 碼升序排序
$string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url";
$signature = sha1($string);
return array(
"appId" => $this->appId,
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => $signature,
"rawString" => $string,
"errcode" => $jsapiTicket_data['errcode'],
"errmsg" => $jsapiTicket_data['errmsg']
);
}else{
return array(
"appId" => $this->appId,
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => null,
"rawString" => null,
"errcode" => $jsapiTicket_data['errcode'],
"errmsg" => $jsapiTicket_data['errmsg']
);
}
}
/*
* 獲取nonceStr
* */
private function getNonceStr($length = 16)
{
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$nonceStr = "";
for ($i = 0; $i < $length; $i++) {
$nonceStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $nonceStr;
}
/*
* 獲取url
* url(當前網頁的URL,不包含#及其後面部分)
* */
private function getUrl(){
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
$url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
return $url;
}
/*
* 微信API調用方法
* */
private function api_request($url,$data=null){
//初始化cURL方法
$ch = curl_init();
//設置cURL參數(基本參數)
$opts = array(
//在局域網內訪問https站點時需要設置以下兩項,關閉ssl驗證!
//此兩項正式上線時需要更改(不檢查和驗證認證)
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_TIMEOUT => 500,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_URL => $url,
);
curl_setopt_array($ch, $opts);
//post請求參數
if (!empty($data)) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
//執行cURL操作
$output = curl_exec($ch);
if (curl_errno($ch)) { //cURL操作發生錯誤處理。
var_dump(curl_error($ch));
die;
}
//關閉cURL
curl_close($ch);
$res = json_decode($output);
return ($res); //返回json數據
}
}