henryspace

记录精彩的程序人生 开始使用

PHP 获取微信公众号授权

<?php
/**
 * Created by PhpStorm.
 * User: henry
 * Date: 2021/4/27
 * Time: 11:09
 */

namespace App\Libs\WeiXin;

use Illuminate\Support\Facades\Cache;

class WebAuth
{

    /**
     * 公众号的唯一标识 密钥
     * @var string
     */
    protected $appId = '';
    protected $appSecret = '';

    /**
     * 用户同意授权,获取code
     * @var string
     */
    protected $authUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirec";

    /**
     * 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
     * @var string
     */
    public $redirectUri = '';

    /**
     * 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
     * @var string
     */
    public $scope = '';

    /**
     * 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
     * @var string
     */
    public $state = '';

    /**
     * 重定向后回调请求链接里会带上的code值
     * @var string
     */
    public $code = '';


    /**
     * 调用服务端API的应用凭证,暂不需要
     * @var string
     */
    protected $accessToken = '';

    /**
     * 通过code换取网页授权access_token
     * @var string
     */
    protected $accessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";

    /**
     * 用户公众号openid
     * @var string
     */
    public $openId = "";

    /**
     * 获取用户信息接口
     * @var string
     */
    protected $userInfoUrl = " https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN";

    /**
     * 保存类的实例的静态成员变量
     * @var object
     */
    public static $instance;


    /**
     * Notice constructor.
     * @throws \Exception
     */
    private function __construct()
    {
        $this->appId = env('TENCENT_FWH_APPID');
        $this->appSecret = env('TENCENT_FWH_APPSECRET');

        if (empty($this->appId) || empty($this->appSecret)) {
            throw new \Exception("配置参数缺失");
        }
    }

    /**
     * 单例类
     * @return static
     * @throws \Exception
     */
    public static function getInstance()
    {
        if (!(self::$instance instanceof self)) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * 获取网页授权code
     * @return mixed
     * @throws \Exception|\GuzzleHttp\Exception\GuzzleException
     */
    public function getCode()
    {

        $this->redirectUri = urlencode($this->redirectUri);
        $this->authUrl = sprintf($this->authUrl, $this->appId, $this->redirectUri, $this->scope, $this->state);

        header("Location:{$this->authUrl}");
    }

    /**
     * 获取接口授权token, 用于网页授权后的应用,获取用户信息等
     * @return mixed|string
     * @throws \Exception
     */
    public function getAccessToken()
    {
        $this->accessToken = Cache::get('web_auth_access_token');
    
        if (!$this->accessToken) {
        
            $this->accessTokenUrl = sprintf($this->accessTokenUrl, $this->appId, $this->appSecret, $this->code);
            $res = $this->http($this->accessTokenUrl,[]);
            $res = json_decode($res, true);
            if (isset($res['errcode']) && $res['errcode'] > 0) {
                throw new \Exception($res['errmsg']);
            }
        
            $this->accessToken = $res['access_token'];
            Cache::put('web_auth_access_token', $this->accessToken, 7200);
        }

        return $this->accessToken;
    }

    /**
     * 获取用户Openid
     * @return string
     * @throws \Exception
     */
    public function getOpenId()
    {
        $this->accessTokenUrl = sprintf($this->accessTokenUrl, $this->appId, $this->appSecret, $this->code);
        $res = $this->http($this->accessTokenUrl,[]);
        $res = json_decode($res, true);
        if (isset($res['errcode']) && $res['errcode'] > 0) {
            throw new \Exception($res['errmsg']);
        }
        $this->openId = $res['openid'];

        return $this->openId;
    }

    /**
     * @return mixed
     * 正确时返回的JSON数据包如下:
     *
     *    {
     *         "openid": "OPENID",
     *         "nickname": NICKNAME,
     *         "sex": 1,
     *         "province":"PROVINCE",
     *         "city":"CITY",
     *         "country":"COUNTRY",
     *         "headimgurl":"https://thirdwx.qlogo.cn/vLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
     *         "privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
     *         "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
     *   }
     * @throws \Exception
     */
    public function getUserInfo()
    {

        // openid 可以通过上面接口获取,也可以是数据库里已经存在的数据,对$this->openId赋值即可
        if(!$this->openId) {
            throw new \Exception('openid参数缺失');
        }

        $this->getAccessToken();
        $this->userInfoUrl = sprintf($this->userInfoUrl, $this->accessToken, $this->openId);
        $res = $this->http($this->userInfoUrl);
        $res = json_decode($res, true);
        if (isset($res['errcode']) && $res['errcode'] > 0) {
            throw new \Exception($res['errmsg']);
        }

        return $res;
    }

    /**
     * 发curl请求
     * @param $url
     * @param $params
     * @param string $method
     * @param array $header
     * @param bool $multi
     * @return mixed
     * @throws \Exception
     */
    function http($url, $params = array(), $method = 'GET', $header = array(), $multi = false)
    {
        $headers = array(
            'Accept' => 'application/json',
            'Accept-Charset' => 'utf-8'
        );
        $headers = $header ? array_merge($headers, $header) : $headers;
        $opts = array(
            CURLOPT_TIMEOUT        => 30,
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_HTTPHEADER     => $headers
        );
        // 根据请求类型设置特定参数
        switch(strtoupper($method)){
            case 'GET':
                $opts[CURLOPT_URL] = $url . '?' . http_build_query($params);
                break;
            case 'POST':
                //判断是否传输文件
                $params = $multi ? $params : http_build_query($params);
                $opts[CURLOPT_URL] = $url;
                $opts[CURLOPT_POST] = 1;
                $opts[CURLOPT_POSTFIELDS] = $params;
                break;
            default:
                throw new \Exception('不支持的请求方式!');
        }
        // 初始化并执行curl请求
        $ch = curl_init();
        curl_setopt_array($ch, $opts);
        $data  = curl_exec($ch);
        $error = curl_error($ch);
        curl_close($ch);
        if($error) throw new \Exception('请求发生错误:' . $error);
        return  $data;
    }

    /**
     * 魔术方法
     * @param $propertyName
     * @return mixed
     */
    public function __get($propertyName)
    {
        return isset($this->$propertyName) ? $this->$propertyName : null;
    }

    /**
     * 魔术方法
     * @param $propertyName
     * @param $value
     */
    public function __set($propertyName, $value)
    {
        $this->$propertyName = $value;
    }

    /**
     * 魔术方法
     * @param $name
     * @param $arguments
     * @throws \Exception
     */
    public function __call($name, $arguments)
    {
        if (empty($name) ||  !method_exists($this, $name) ) {
            throw new \Exception('方法未定义');
        }
    }

}

以上是一个微信公众号授权单例,未测试过,但基本逻辑是这样

调用方式如下:

$params = $request->all();
$code = isset($params['code']) && !empty($params['code']) ? $params['code'] : '';
try{
    $ins = \App\Libs\WeiXin\WebAuth::getInstance();
    // code存在,进行第二步
    if ($code) {
        $ins->code = $code;
        $ins->getOpenId();
        // 处理绑定 或者继续获取用户信息 或者其他业务逻辑
        //TODO echo $ins->openId; 

    } else {
        // code不存在,进行第一步
        $transferRedirectUrl = $request->fullUrl();

        $ins->redirectUri = $transferRedirectUrl;
        $ins->scope = 'snsapi_base';//静默授权
        $ins->state = $params['state'];
        $ins->getCode();
    }
}catch (\Exception $e) {
    return ['code' => 410, 'msg' => '非法操作'];
}

return ['code' => 200, 'msg' => 'success'];
请成为永远疯狂永远浪漫永远清澈的存在。

评论
留下你的脚步
推荐阅读