基于PHP-TP6.1扣子Agent Access Token获取

扣子Access Token sdk并没有给PHP版本的示例,于是自己写一个,经试用并无不妥,分享之;
直接上代码吧

<?php
/*
 * @Author: braver.wang
 * @Date: 2025-05-06 10:12:59
 * @Description: 扣子Oauth Access Token SDK ,文档地址:https://www.coze.cn/open/docs/developer_guides/oauth_jwt
 */

namespace app\helper;

use think\facade\App;

class CozeHelper
{
    private $apiurl; // API 地址
    private $appid; // 应用 ID
    private $public_key_id; // 应用公钥 ID
    private $private_key; // 应用私钥
    /**
     * 初始化配置参数
     */
    public function __construct()
    {
        $this->apiurl = config('cozeconfig.apiurl');
        $this->appid = config('cozeconfig.appid');
        $this->public_key_id = config('cozeconfig.public_key_id');
        $this->private_key = file_get_contents(App::getRootPath() . 'public/private_key.pem');
    }

    public function get_access_token(int $docid)
    {
        $postData = [
            'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', // 固定值
            'duration_seconds' => 86399, // 申请的 AccessToken 有效期,单位为秒,默认 900 秒,即 15 分钟。最大可设置为 86399 秒,即 24 小时。
        ];
        // print_r($postData);
        $headers = [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $this->get_jwt($docid)
        ];
        // print_r($headers);
        $response = $this->httpRequest($this->apiurl, 'POST', json_encode($postData, JSON_UNESCAPED_UNICODE), $headers, 300);
        $result = json_decode($response, true);
        if (!empty($result['access_token'])) {
            return [
                'code' => 1,
                'access_token' => $result['access_token'],
                'message' => '获取成功'
            ];
        }
        return [
            'code' => 0,
            'message' => empty($result['error_message']) ? '获取访问令牌失败' : "获取访问令牌失败,错误码:{$result['error_code']},错误信息:{$result['error_message']}"
        ];
    }
    /**
     * 获取JWT
     * @param int $docid 用户ID
     * @return string JWT令牌
     */
    public function get_jwt(int $docid)
    {
        $header = [
            'alg' => 'RS256', // 签名算法 固定为RS256
            'typ' => 'JWT', // 固定为JWT
            'kid' => strval($this->public_key_id) // 应用公钥ID指纹
        ];
        /**
         * 访问令牌的会话标识。目前仅限在会话隔离场景下使用,即将 session_name 指定为用户在业务侧的 UID,以此区分不同业务侧用户的对话历史。
         * 若未指定 session_name,不同用户的对话历史可能会掺杂在一起。
         */
        $payload = [
            'iss' => strval($this->appid), // 应用ID
            'aud' => 'api.coze.cn', // 固定值
            'iat' => time(), // 生成时间
            'exp' => time() + 86400, // 过期时间
            'jti' => (string)$this->get_rand_str(48, false, 1), // 随机字符串
            'session_name' => strval($docid) // 会话标识
        ];
        $encodeHeader = $this->base64url_encode(json_encode($header));
        $encodePayload = $this->base64url_encode(json_encode($payload));
        $signature = '';
        $data = $encodeHeader . '.' . $encodePayload;
        $private_key = $this->private_key;
        if (!openssl_pkey_get_private($private_key)) {
            echo '私钥格式错误' . openssl_error_string();
            die();
        }
        openssl_sign($data, $signature, $private_key, 'sha256WithRSAEncryption');
        $encodeSignature = $this->base64url_encode($signature);
        return $encodeHeader . '.' . $encodePayload . '.' . $encodeSignature;
    }

    /**
     * 过滤base64url编码
     * @param $data 要过滤的数据
     * @return string 过滤后的base64url编码
     */
    private function base64url_encode($data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }
    /**
     * 生成随机字符串
     * @param int $length 字符串长度
     * @param bool $addtime 是否加入当前时间戳
     * @param int $inclduenumber 是否包含数字 0 不包含 1 大小写字母+数字 2 纯数字 3 大写字符+数字
     * @return string 随机字符串
     */
    private function get_rand_str($length = 6, $addtime = true, $inclduenumber = 0): string
    {
        if (1 == $inclduenumber) { // 大小写字母+数字
            $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        } else if (2 == $inclduenumber) { // 纯数字
            $chars = '123456789';
        } else if (3 == $inclduenumber) { // 大写字母+数字
            $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
        } else if (4 == $inclduenumber) { // 小写字母+数字
            $chars = 'abcdefghijklmnopqrstuvwxyz123456789';
        } else { // 不包含数字
            $chars = 'abcdefghijklmnopqrstuvwxyz';
        }
        $len = strlen($chars);
        $randStr = '';
        for ($i = 0; $i < $length; $i++) {
            $randStr .= $chars[rand(0, $len - 1)];
        }
        $tokenvalue = $randStr;
        if ($addtime) {
            $tokenvalue = $randStr . time();
        }
        return $tokenvalue;
    }
    /**
     * CURL请求
     * @param string $url 请求地址
     * @param string $method 请求方法,默认为GET
     * @param array $postfields POST请求的数据,默认为null
     * @param array $headers 请求头信息,默认为空数组
     * @param int $timeout 请求超时时间,默认为30秒
     * @param bool $debug 是否开启调试模式,默认为false
     * @return mixed 返回请求结果,如果请求失败则返回false
     */
    private function httpRequest($url, $method = 'GET', $postfields = null, $headers = [], $timeout = 30, $debug = false)
    {
        $method = strtoupper($method);
        $ci = curl_init();
        curl_setopt($ci, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0");
        curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 60); /* 在发起连接前等待的时间,如果设置为0,则无限等待 */
        curl_setopt($ci, CURLOPT_TIMEOUT, $timeout); /* 设置cURL允许执行的最长秒数 */
        curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
        switch ($method) {
            case "POST":
                curl_setopt($ci, CURLOPT_POST, true);
                if (!empty($postfields)) {
                    if (is_string($postfields) && preg_match('/^([\w\-]+=[\w\-]+(&[\w\-]+=[\w\-]+)*)$/', $postfields)) {
                        parse_str($postfields, $output);
                        $postfields = $output;
                    }
                    if (is_array($postfields)) {
                        $tmpdatastr = http_build_query($postfields);
                    } else {
                        $tmpdatastr = $postfields;
                    }
                    curl_setopt($ci, CURLOPT_POSTFIELDS, $tmpdatastr);
                }
                break;
            default:
                curl_setopt($ci, CURLOPT_CUSTOMREQUEST, $method); /* //设置请求方式  */
                break;
        }
        $ssl = preg_match('/^https:\/\//i', $url) ? TRUE : FALSE;
        curl_setopt($ci, CURLOPT_URL, $url);
        if ($ssl) {
            curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
            curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE); // 从证书中检查SSL加密算法是否存在
        }
        //curl_setopt($ci, CURLOPT_HEADER, true); /*启用时会将头文件的信息作为数据流输出*/
        if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) {
            curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1);
        }
        curl_setopt($ci, CURLOPT_MAXREDIRS, 2); // 指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的
        curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ci, CURLINFO_HEADER_OUT, TRUE);
        $response = curl_exec($ci);
        $requestinfo = curl_getinfo($ci);
        $http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
        if ($debug) {
            echo "=====post data======\r\n";
            var_dump($postfields);
            echo "=====info===== \r\n";
            print_r($requestinfo);
            echo "=====response=====\r\n";
            print_r($response);
        }
        curl_close($ci);
        return $response;
        // return array($http_code, $response);
    }
}

配置文件config/cozeconfig.php,具体里面的参数怎么来的还是自己看扣子帮助文档吧

<?php
/*
 * @Author: braver.wang
 * @Date: 2025-05-06 10:15:41
 * @Description: 扣子相关配置文件
 */
return [
    'apiurl' => 'https://api.coze.cn/api/permission/oauth2/token', // 获取AccessToken接口地址
    'appid' => 'XXXXXXX', // 应用ID
    'public_key_id' => 'XXXXXXXX', // 应用公钥ID
    'private_key' => `` // 应用私钥
];

使用

<?php
/*
 * @Author: braver.wang
 * @Date: 2025-05-06 14:29:33
 * @Description: 扣子相关控制器
 */

namespace app\controller\v1;

use app\helper\CozeHelper;
use app\APIBaseController;

class Coze extends APIBaseController
{
    public function index()
    {
        $docid = $this->request->param("docid", -1);
        if ($docid == -1 || $docid == '') $this->error("用户ID错误,请检查后再试");
        $coze = new CozeHelper();
        $rst = $coze->get_access_token($docid);
        if ($rst['code'] == 1) {
            $this->success(['accesstoken' => $rst['access_token']], "获取成功");
        }
        $this->error($rst['message']);
    }
}

我这里的接口是把用户id直接传给接口 然后token是绑定到每个用户的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容