1. 程式人生 > >php實現判斷使用者是否在微信內登陸,是否關注公眾號

php實現判斷使用者是否在微信內登陸,是否關注公眾號

記一次小需求:
一個投票活動,在原有的投票接口裡(很老的專案。。)增加判斷使用者是否在微信內登陸,且是否關注公眾號,如果使用者未關注,則引導使用者關注公眾號。

一、需求要點

  • 只對指定對部分活動有效
  • 區分微信瀏覽器訪問和外部瀏覽器訪問
  • 區分使用者是否關注我們的公眾號
  • 關注可投,不關注引導關注

二、大體實現

  1. 指定部分活動有效
    最簡單對方式,設定一個數組,對id在陣列內的邏輯活動才執行新增的邏輯,這樣主流程裡只插入幾行程式碼,整個邏輯在另一個函式內實現。
...前置邏輯
$wechatAuthList = [
]; if (in_array($article_id, $wechatAuthList)){ if($this->wechatAuth()){ return ['code' => 403, 'message' => '引導關注'] } } ...後面的邏輯
  1. 區分微信瀏覽器訪問和外部瀏覽器訪問
    通過user-agent判斷,沒什麼好說了。
	// 判斷是否是微信瀏覽器
    public function isWeixin()
    {
        if ( strpos($_SERVER['HTTP_USER_AGENT'
], 'MicroMessenger') !== false ) { return true; } return false; }
  1. 區分使用者是否關注我們的公眾號
    通過微信授權介面,獲取使用者資訊,通過返回結果的 subscribe 欄位判斷。

    ‘subscribe’ = 1 (已關注)
    ‘subscribe’ = 0 (未關注)

這一步我們要依次用到四個介面:

//獲取code
"https://open.weixin.qq.com/connect/oauth2/authorize?appid={
$this->appId}
&redirect_uri=$redirect&response_type=code&scope={$scope}&state=1#wechat_redirec"
; //獲取opendId "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appId}&secret={$this->secret}&code={$code}&grant_type=authorization_code"; //獲取access_token(基礎token) "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->secret}"; //獲取使用者資訊 "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$this->accessToken}&openid={$this->openId}&lang=zh_CN"

下文會描述具體步驟。

  1. 關注可投,不關注引導關注
    基本是前端工作了,對後端來說就是根據不同情況返回不同結果,前端根據返回值確定是否彈出浮層顯示二維碼。
    虛擬碼:
 $.ajax({
        type: '...',
        url: '...',
        data: '...',
        success: function(data) {
        	if (data.code === 403){
        		$("#tab").alert();
        	}
        }

三、詳細步驟

下面來說下第3部分,如何區分使用者是否關注我們的公眾號

首先明確需要用到哪些:

  • 上邊列出的四個介面
  • 公眾號 appId 和 secret
  • 微信公眾平臺設定授權回撥地址和redirect_uri 一致

需要注意的:

  • 別忘了到微信公眾平臺設定授權回撥地址和redirect_uri 一致,不然會報 redirect_uri引數錯誤
  • 重定向地址redirect_uri,如果有引數的話,別忘了urlencode(),不然微信重定向回來原本的引數會沒有
  • 獲取code時需要在瀏覽器重定向,不能用ajax這種非同步請求!
  • scope 有兩種值,不需要使用者感知的用靜默授權(snsapi_base):

snsapi_base (不彈出授權頁面,直接跳轉,只能獲取使用者openid)
snsapi_userinfo (彈出授權頁面,可通過openid拿到暱稱、性別、所在地。並且, 即使在未關注的情況下,只要使用者授權,也能獲取其資訊 )

  • 請求到的 code 只能使用一次
  • access_token 在微信有兩種,一種是授權用的,與openid同時返回,只用於授權,沒有請求次數限制;另一個種是基礎token,每天最多請求2000次,請求subscribe欄位要用這個access_token,得到他時只獲得兩個資料,access_token 和 expired,注意區分

具體程式碼:

整體流程:

 	//程式碼精簡處理了,只寫寫大體,細節不介紹了,比如異常處理、日誌記錄之類的
	public function wechatAuth()
    {
        $wechat = new Wechat();
        // 是否微信內建瀏覽器登陸判斷
        if ($wechat->isWeixin()){
        	  // 獲取openId(這一步會先獲取code,再用得到的code獲取openid,同時會得到)
            $openId = $wechat->getOpenId();
            // 獲取 client 的 access_token
            $accessToken = $wechat->getAccessToken();
			  // 獲取使用者資訊
            $info = $wechat->getUserInfo();
            // 未關注返回true,即引到認證
            if (isset($info->subscribe) && $info->subscribe == 0){
                return true;
            }
        }
        return false;
    }

獲取openid:

public function getOpenId($redirect = null, $scope = 'snsapi_base')
    {
        if (isset($this->openId)){
            return $this->openId;
        }

        // 如果之前session中存入了openId,則直接返回
        if (isset($_SESSION['openId'])){
            $this->openId = $_SESSION['openId'];
            return $_SESSION['openId'];
        }

        // 如果沒有code,則先請求code,再從微信自動跳轉回來
        // 這部分註釋掉了,因為這個介面是ajax請求,被微信限制,所以code獲取部分後來翻到了前段js裡,如果在微信內,先通過window.location.href = ''去獲取code,然後請求介面時再攜帶code來請求
//        if (!isset($_GET['code'])){
//            $redirect = empty($redirect)?'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] : $redirect;
//            $redirect = urlencode($redirect);
//            $url="https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appId}&redirect_uri=$redirect&response_type=code&scope=$scope&state=1#wechat_redirec";
//            header("Location:".$url);exit();
//        }

        $code = $_GET['code'];
        // 獲取 openid 及 授權用的access_token
        $get_openid_url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid='.$this->appId.'&secret='.$this->secret.'&code='.$code.'&grant_type=authorization_code';
        $response = file_get_contents($get_openid_url);
        $userInfo = json_decode($response, true);

        // openid如有則存入session
        if (array_key_exists('openid', $userInfo)){
            $_SESSION['openId'] = $userInfo;
            $this->openId = $userInfo['openid'];
        }
        return $this->openId;
    }

獲取access_token:

//這裡只看原生的幾句就行。因為微信授權域名只能設定兩個,而有多個系統需要使用,所以做了一層代理,通過代理平臺的介面獲取access_token。
//而這個專案比較老,零幾年的了,所以redis、memcache之類的都沒有可用的,就直接寫檔案記錄了,具體的失效處理就不寫了。
public function getAccessToken($refresh = false)
    {
        // 微信原生方法
        $get_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->secret";
        $response = file_get_contents($get_token_url);
        $tokenInfo = json_decode($response, true);
        $accessToken = isset($tokenInfo['access_token'])? $tokenInfo['access_token'] : '';

        // 平臺介面獲取
        //$accessTokenFile = '/tmp/access_token';
        // 如果需要重新整理token 或 token檔案不存在,則重新請求token並寫入檔案
        //if ($refresh || !file_exists($accessTokenFile)){
        //    $accessTokenApi = '......';
        //    $res = file_get_contents($accessTokenApi);
        //    $accessToken = json_decode($res)->token;
        //    if (!is_dir('/tmp')){
        //        mkdir('/tmp', 777);
        //    }
        //    file_put_contents($accessTokenFile, $accessToken);
        //}else{// 否則直接取檔案
        //    $accessToken = file_get_contents($accessTokenFile);
        //}
        //$this->accessToken = $accessToken;
        return $accessToken;
    }

獲取使用者資訊:

 public function getUserInfo()
    {
        $get_user_info_url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=$this->accessToken&openid=$this->openId&lang=zh_CN";
        $response = file_get_contents($get_user_info_url);
        $info = json_decode($response);
        return $info;
    }

正常情況下,微信會返回下述JSON資料包:

{
    "subscribe": 1, 
    "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", 
    "nickname": "Band", 
    "sex": 1, 
    "language": "zh_CN", 
    "city": "廣州", 
    "province": "廣東", 
    "country": "中國", 
    "headimgurl":"http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
    "subscribe_time": 1382694957,
    "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
    "remark": "",
    "groupid": 0,
    "tagid_list":[128,2],
    "subscribe_scene": "ADD_SCENE_QR_CODE",
    "qr_scene": 98765,
    "qr_scene_str": ""
}

其他

通過 js 獲取 code 的程式碼:

<script>
    var oCode = '';
    function isWeixin() { //判斷是否是微信
        var ua = navigator.userAgent.toLowerCase();
        return ua.match(/MicroMessenger/i) == "micromessenger";
    }

    function getUrlParam(name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
        var r = window.location.search.substr(1).match(reg);
        if (r != null) return unescape(r[2]);
        return null;
    }

    function getWxCode() {
        var appId = 'wx133b*****3db0a';
        var url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appId + "&redirect_uri=" + location.href.split('#')[0] + "&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
        var code = getUrlParam("code");
        if (!code) {
            window.location = url;
        } else {
            return code;
        }
    }
    // 如果是微信內登陸則重定向獲取code
    if (isWeixin()){
        console.log('is weixin!');
        oCode = getWxCode();
        console.log(oCode);
    }
</script>