扣子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是绑定到每个用户的