Only the Chinese version of this page is provided currently. The English version will be provided soon.
Help & Documentation>TencentDB for MySQL

设置账号 CAM 验证

Last updated: 2026-04-01 17:20:09

本文为您介绍通过控制台设置账号 CAM 验证相关说明和操作。
说明:
如果您需要开启账号 CAM 验证,请您填写 申请单 申请使用。

支持地域

此功能目前支持的地域为:上海、广州。

背景

在使用云数据库的场景中,通常需要为数据库创建独立的账号和密码,并授权给相应的用户进行访问和操作。这种方式的账户管理比较复杂,容易出现账号密码泄露等安全问题。基于以上背景,云数据库 MySQL 支持为账号启用 CAM 验证功能,通过打通腾讯云平台子账号与数据库账号之间的关系以及增加 CAM 凭证的身份验证,简化账号权限管理的复杂度,提升数据库的安全性和账号管理效率。

操作场景

如果用户对安全性有较高要求,可以通过本功能将 CAM 与数据库账号验证相绑定,用户在请求时可获取对应的密码来访问数据库,以提升数据库的安全性。我们建议如下两种场景,启用 CAM 验证。
使用 CAM 身份验证作为临时、个人访问数据库的机制时。
仅对可以轻松重试的工作负载使用 CAM 身份验证。

注意事项

请尽可能使用长连接访问数据库。
启用 CAM 验证前需要提前配置好相关 CAM 权限规则。
启用 CAM 验证后不支持修改密码。
root 账号也支持启用 CAM 验证。
关闭 CAM 验证后将无法通过 CAM 获取访问凭证,因此在关闭 CAM 验证时需要输入一个新密码。

功能限制

单个实例建议不超过10个账号开启 CAM 验证。
启用 CAM 验证后不支持对该账号进行重置密码操作。
仅支持单个主机地址的账户启用 CAM 验证功能。
相同账号名不支持重复启用 CAM 验证功能。
若实例先启用了 CAM 验证,则不支持开启 密码复杂度 功能。
若实例开启了 密码复杂度 功能,在启用 CAM 验证后,不支持调整密码复杂度规则。

前提条件

已填写 申请单 申请使用此功能。
实例状态为运行中。

步骤1:配置 CAM 权限规则

在使用账号 CAM 验证功能之前,用户需要先配置相关的 CAM 权限规则。

策略内容

{
"statement": [
{
"action": [
"cam:BuildDataFlowAuthToken"
],
"effect": "allow",
"resource": [
"qcs::cam::uin/<用户 uin>:resourceUser/<实例 ID>/<账号名>",
]
}
],
"version": "2.0"
}
<用户 uin>:替换为实际的账号 ID。
<实例 ID>:替换为实际需要授权的实例 ID。
<账号名>:替换为实际需要授权的账号名。

操作指引

1. 使用管理员账号登录访问管理控制台,在 策略 页面,按照策略生成器创建自定义策略(请参见 通过策略生成器创建自定义策略)。

效果:允许。
服务:访问管理(cam)。
操作:其他操作 - BuildDataFlowAuthToken。
资源:特定资源 - 添加资源六段式。
填写资源:<实例 ID>/<账号名>。
2. 单击下一步,自定义策略的名称,并将策略授予目标子账号。
3. 单击完成,完成授权。

步骤2:启用 CAM 验证

启用 CAM 验证分为两种情形,分别是创建账号时启用 CAM 验证和为已有账号启用 CAM 验证,您可按照如下步骤分别操作。
情形1:创建账号时启用 CAM 验证
情形2:为已有账号启用 CAM 验证
1. 登录 MySQL 控制台
2. 在实例列表,单击实例 ID操作列的管理,进入实例管理页面。
3. 在实例管理页面,选择数据库管理 > 账号管理 > 创建账号,在弹出的对话框中填写相关信息,确认无误后单击确定。
说明:
创建账号的操作介绍请参考 创建账号,以下仅介绍开启 CAM 验证相关的步骤。
启用 CAM 验证:打开“启用 CAM 验证”后的按钮,在弹窗下阅读重要提示后单击确定

4. 成功启用 CAM 验证的账号名下会显示“已启用 CAM 验证”。
1. 登录 MySQL 控制台
2. 在实例列表,单击实例 ID操作列的管理,进入实例管理页面。
3. 在实例管理页,选择数据库管理 > 账号管理
4. 在账号管理页面,找到目标账号,在其操作列单击启用 CAM 验证
5. 在弹窗中阅读重要提示后,单击确定

6. 成功启用 CAM 验证的账号名下会显示“已启用 CAM 验证”。

步骤3:在应用程序通过代码调用获取密码

账号具备了相关 CAM 权限规则,并且启用了 CAM 验证后,用户可在应用程序通过 Java 代码调用来获取密码,从而连接数据库实例。您也可以使用 Python 来连接数据库实例,具体方法请参见 附录2:通过 Python 连接数据库。您还可以使用 go 来连接数据库实例,具体方法请参见 附录3:通过 go 连接数据库
1. 在腾讯云控制台,账号信息 页面,查询账号的 APPID。

2. 访问管理控制台 > API 密钥管理 获取 SecretID 和 SecretKey。
3. 在应用程序使用如下代码。
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-dbauth-sdk-java</artifactId>
<version>1.0.4</version>
</dependency>
间接依赖项:tencentcloud-sdk-java 3.1.1039版本及以上。
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.1039</version>
</dependency>
通过代码调用获取密码的示例
package com.tencentcloud.dbauth;
import com.tencentcloudapi.common.Credential;
import com.tencentcloud.dbauth.model.GenerateAuthenticationTokenRequest;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;

public class GenerateDBAuthentication {

public static void main(String[] args) {
// 定义认证令牌的参数
String region = "<实例所在地域>";
String instanceId = "<实例 ID>";
String userName = "<账号名>";
// 从环境变量中获取凭证
Credential credential = new Credential(System.getenv("<TENCENTCLOUD_SECRET_ID>"), System.getenv("<TENCENTCLOUD_SECRET_KEY>"));

System.out.println(getAuthToken(region, instanceId, userName, credential));
}

public static String getAuthToken(String region, String instanceId, String userName, Credential credential) {
try {
// 实例化一个 http 选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("cam.tencentcloudapi.com");
// 实例化一个 client 选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);

// 构建 GenerateAuthenticationTokenRequest
GenerateAuthenticationTokenRequest tokenRequest = GenerateAuthenticationTokenRequest.builder()
.region(region)
.credential(credential)
.userName(userName)
.instanceId(instanceId)
.clientProfile(clientProfile) // clientProfile 是可选的
.build();

return DBAuthentication.generateAuthenticationToken(tokenRequest);

} catch (TencentCloudSDKException e) {
e.printStackTrace();
}
return "";
}
}
<实例所在地域>:替换为您需要访问的实例所在地域,示例:ap-guangzhou。
<实例 ID>:替换为需要访问的实例 ID。
<账号名>:替换为实际登录的账号名。
<TENCENTCLOUD_SECRET_ID>:替换为从访问管理控制台获取到的 SecretID。
<TENCENTCLOUD_SECRET_KEY>:替换为从访问管理控制台获取到的 SecretKey。

步骤4:使用身份令牌连接云数据库 MySQL

步骤3 中获取到身份令牌 AuthToken 后,即可使用身份令牌连接云数据库 MySQL,示例如下。
mysql --host=<IP 地址> --port=<端口号> --user=<账号名> --password=<密码>;
<IP 地址>:替换为您实例的 IP 地址。
<端口号>:替换为您实例的端口号,若未修改过端口号,则默认为3306。
<账号名>:替换为您实际登录的账号名。
<密码>:替换为 步骤3 中获取到的 AuthToken。

使用 Java 代码连接数据库示例

package com.tencentcloud.examples;

import com.tencentcloud.dbauth.DBAuthentication;
import com.tencentcloud.dbauth.model.GenerateAuthenticationTokenRequest;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class CAMDatabaseAuthenticationTester {
public static void main(String[] args) throws Exception {
// 定义连接所需的变量
String region = "ap-guangzhou";
String instanceId = "cdb-123456";
String userName = "test";
String host = "gz-cdb-123456.sql.tencentcdb.com";
int port = 3306;
String dbName = "mysql";
String secretId = System.getenv("TENCENTCLOUD_SECRET_ID");
String secretKey = System.getenv("TENCENTCLOUD_SECRET_KEY");

// 获取连接
Connection connection = getDBConnectionUsingCAM(secretId, secretKey, region,
instanceId, userName, host, port, dbName);

// 验证连接是否成功
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 'Success!';");
while (rs.next()) {
String id = rs.getString(1);
System.out.println(id); // 应打印 "Success!"
}

// 关闭连接
stmt.close();
connection.close();
}

/**
* 使用 CAM 数据库认证获取数据库连接
*
* @param secretId 密钥 ID
* @param secretKey 密钥
* @param region 地区
* @param instanceId 实例 ID
* @param userName 用户名
* @param host 主机
* @param port 端口
* @param dbName 数据库名称
* @return Connection 对象
* @throws Exception 异常
*/
private static Connection getDBConnectionUsingCAM(
String secretId, String secretKey, String region, String instanceId, String userName,
String host, int port, String dbName) throws Exception {

// 从 secretId 和 secretKey 获取凭证
Credential credential = new Credential(secretId, secretKey);

// 定义最大尝试次数
int maxAttempts = 3;
Exception lastException = null;
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
// 使用凭证获取认证令牌
String authToken = getAuthToken(region, instanceId, userName, credential);

String connectionUrl = String.format("jdbc:mysql://%s:%d/%s", host, port, dbName);
return DriverManager.getConnection(connectionUrl, userName, authToken);
} catch (Exception e) {
lastException = e;
System.out.println("Attempt " + attempt + " failed.");
Thread.sleep(5000);
}
}
System.out.println("All attempts failed. error: " + lastException.getMessage());
throw lastException;
}

/**
* 获取认证令牌
*
* @param region 区域
* @param instanceId 实例 ID
* @param userName 用户名
* @param credential 凭证
* @return 认证令牌
*/
private static String getAuthToken(String region, String instanceId, String userName, Credential credential) throws TencentCloudSDKException {
// 实例化一个 http 选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("cam.tencentcloudapi.com");
// 实例化一个 client 选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);

// 构建 GenerateAuthenticationTokenRequest
GenerateAuthenticationTokenRequest tokenRequest = GenerateAuthenticationTokenRequest.builder()
.region(region)
.credential(credential)
.userName(userName)
.instanceId(instanceId)
.clientProfile(clientProfile) // clientProfile 是可选的
.build();

return DBAuthentication.generateAuthenticationToken(tokenRequest);
}
}

修改密码轮转周期

当账号启用 CAM 验证功能后,支持通过控制台修改密码的轮转周期。
注意:
为实例新建账号,并设置密码轮转周期后,该账号的密码轮转周期将立即生效。
当实例下存在已开启 CAM 验证的账号时,在执行修改密码轮转周期操作后,该账号的密码轮转周期不会立即生效,您需要操作 刷新密码 来使其立即生效,否则该账号将在下次密码轮转周期到期时更新。
1. 登录 MySQL 控制台
2. 在实例列表,单击实例 ID操作列的管理,进入实例管理页面。
3. 在实例管理页面,选择账号管理
4. 单击修改密码轮转周期,然后在弹窗中修改时间(默认为1天,取值范围:1 - 30天,仅支持设置为整数),单击确定

刷新密码

当账号启用 CAM 验证功能后,可以通过刷新密码操作来更新密码。例如该账号设置的轮转周期为24小时更换一次密码,则在未到达轮转周期之前,可以通过刷新密码操作来立即更新密码。
注意:
注意刷新密码后,当前登录凭证会失效,需要观察业务访问数据库状态是否符合预期。
1. 登录 MySQL 控制台
2. 在实例列表,单击实例 ID操作列的管理,进入实例管理页面。
3. 在实例管理页面,选择账号管理
4. 在账号管理页面,找到目标账号,在其操作列单击更多 > 刷新密码
5. 在弹窗下阅读风险提示后,单击确定


关闭 CAM 验证

注意:
关闭 CAM 验证后无法通过 CAM 获取访问凭证,请及时更新密码。
1. 登录 MySQL 控制台
2. 在实例列表,单击实例 ID操作列的管理,进入实例管理页面。
3. 在实例管理页面,选择数据库管理 > 账号管理
4. 在账号管理页面,找到目标账号,在其操作列单击更多 > 关闭 CAM 验证
5. 在弹窗下输入新密码并确认密码后单击确定


附录1:错误码

如果返回结果中存在 Error 字段,则表示调用 API 接口失败。有关错误码的说明,请参见 错误码
云数据库 MySQL 账号 CAM 验证功能相关的错误码如下:

公共错误码

错误码
说明
AuthFailure.InvalidAuthorization
请求头部的 Authorization 不符合腾讯云标准。
AuthFailure.InvalidSecretId
密钥非法(不是云 API 密钥类型)。
AuthFailure.MFAFailure
MFA 错误。
AuthFailure.SecretIdNotFound
密钥不存在。请在 控制台 检查密钥是否已被删除或者禁用,如状态正常,请检查密钥是否填写正确,注意前后不得有空格。
AuthFailure.SignatureExpire
签名过期。Timestamp 和服务器时间相差不得超过五分钟,请检查本地时间是否和标准时间同步。
AuthFailure.SignatureFailure
签名错误。签名计算错误,请对照调用方式中的签名方法文档检查签名计算过程。
AuthFailure.TokenFailure
token 错误。
AuthFailure.UnauthorizedOperation
请求未授权。请参考 CAM 文档对鉴权的说明。

业务错误码

错误码
说明
FailedOperation.BuildAuthToken
生成 AuthToken 异常。
FailedOperation.FlowAuthIllegal
凭据操作失败。

附录2:通过 Python 连接数据库

1. 在腾讯云控制台,账号信息 页面,查询账号的 APPID。

2. 访问管理控制台 > API 密钥管理 获取 SecretID 和 SecretKey。
3. 通过 pip 安装方式将腾讯云数据库 CAM Python SDK 安装到您的项目中。请在命令行中执行以下命令:
pip install git+https://github.com/TencentCloud/dbauth-sdk-python.git
请注意,如果同时有 python2 和 python3 环境,python3 环境需要使用 pip3 命令安装。
间接依赖项:tencentcloud-sdk-python 3.0.1224版本及以上。

使用 Python 连接数据库示例

import logging
import os
import time

import pymysql
from dbauth.db_authentication import DBAuthentication
from dbauth.model.generate_authentication_token_request import GenerateAuthenticationTokenRequest
from tencentcloud.common import credential
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile

# 配置 root logger
logging.basicConfig(
level=logging.INFO,
format='[%(asctime)s] - [%(threadName)s] - {%(module)s:%(funcName)s:%(lineno)d} %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
log = logging.getLogger(__name__)


def main():
region = "ap-guangzhou"
instance_id = "cdb-123456"
user_name = "camtest"
host = "gz-cdb-123456.sql.tencentcdb.com"
port = 25925
db_name = "test"
secret_id = os.environ['AK']
secret_key = os.environ['SK']

connection = None
try:
# 获取连接
connection = get_db_connection_using_cam(secret_id, secret_key, region,
instance_id, user_name, host, port, db_name)

# 验证连接是否成功
with connection.cursor() as cursor:
cursor.execute("SELECT 'Success!';")
result = cursor.fetchone()
log.info(result[0]) # 应该打印 "Success!"
except Exception as e:
log.error(f"An error occurred: {e}")
finally:
if connection and connection.open:
connection.close()


def get_db_connection_using_cam(secret_id, secret_key, region, instance_id, user_name, host, port, db_name):
cred = credential.Credential(secret_id, secret_key)

max_attempts = 3
last_exception = None
for attempt in range(1, max_attempts + 1):
try:
auth_token = get_auth_token(region, instance_id, user_name, cred)

connection = pymysql.connect(
host=host,
port=port,
user=user_name,
password=auth_token,
database=db_name
)
return connection
except Exception as e:
last_exception = e
log.info(f"Attempt {attempt} failed.")
time.sleep(5)

log.error(f"All attempts failed. error: {last_exception}")
raise last_exception


def get_auth_token(region, instance_id, user_name, cred):
try:
# 实例化一个 http 选项,可选的,没有特殊需求可以跳过
http_profile = HttpProfile()
http_profile.endpoint = "cam.tencentcloudapi.com"

# 实例化一个 client 选项,可选的,没有特殊需求可以跳过
client_profile = ClientProfile()
client_profile.httpProfile = http_profile

request = GenerateAuthenticationTokenRequest(
region=region,
instance_id=instance_id,
user_name=user_name,
credential=cred,
client_profile=client_profile, # 可选
)
return DBAuthentication.generate_authentication_token(request)
except TencentCloudSDKException as err:
log.error(err)
raise


if __name__ == "__main__":
main()


附录3:通过 go 连接数据库

依赖环境:go 1.17版本及以上。
间接依赖项:tencentcloud-sdk-go v1.0.1015版本及以上。
1. 在腾讯云控制台,账号信息 页面,查询账号的 APPID。

2. 访问管理控制台 > API 密钥管理 获取 SecretID 和 SecretKey。
3. 请在命令行中执行以下命令:
go get -v -u github.com/tencentcloud/dbauth-sdk-go

使用 go 连接数据库示例

package main

import (
"database/sql"
"fmt"
"os"
"time"

_ "github.com/go-sql-driver/mysql"
"github.com/sirupsen/logrus"
"github.com/tencentcloud/dbauth-sdk-go/dbauth"
"github.com/tencentcloud/dbauth-sdk-go/dbauth/model"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
)

func init() {
logrus.SetOutput(os.Stdout)
logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
logrus.SetLevel(logrus.InfoLevel)
}

func main() {
// 定义数据库连接参数
region := "ap-guangzhou"
instanceId := "cdb-123456"
userName := "camtest"
host := "gz-cdb-123456.sql.tencentcdb.com"
port := 3306
dbName := "test"
ak := os.Getenv("TENCENTCLOUD_SECRET_ID")
sk := os.Getenv("TENCENTCLOUD_SECRET_KEY")

// 获取连接
connection, err := getDBConnectionUsingCam(ak, sk, region, instanceId, userName, host, port, dbName)
if err != nil {
logrus.Error("Failed to get connection:", err)
return
}

// 验证连接是否成功
stmt, err := connection.Query("SELECT 'Success!';")
if err != nil {
logrus.Error("Failed to execute query:", err)
return
}
for stmt.Next() {
var result string
stmt.Scan(&result)
logrus.Info(result) // Success!
}

// 关闭连接
if err := stmt.Close(); err != nil {
logrus.Error("Failed to close statement:", err)
}
if err := connection.Close(); err != nil {
logrus.Error("Failed to close connection:", err)
}
}

// 使用 CAM 获取数据库连接
func getDBConnectionUsingCam(secretId, secretKey, region, instanceId, userName, host string, port int, dbName string) (*sql.DB, error) {
credential := common.NewCredential(secretId, secretKey)
maxAttempts := 3
var lastErr error

for attempt := 1; attempt <= maxAttempts; attempt++ {
// 获取认证 Token
authToken, err := getAuthToken(region, instanceId, userName, credential)
if err != nil {
return nil, err
}

connectionUrl := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", userName, authToken, host, port, dbName)
db, err := sql.Open("mysql", connectionUrl)
if err != nil {
lastErr = err
logrus.Warnf("Open connection failed. Attempt %d failed.", attempt)
time.Sleep(5 * time.Second)
continue
}
if err = db.Ping(); err != nil {
lastErr = err
logrus.Warnf("Ping failed. Attempt %d failed.", attempt)
time.Sleep(5 * time.Second)
continue
}
return db, nil
}

logrus.Error("All attempts failed. error:", lastErr)
return nil, lastErr
}

// 获取认证 Token
func getAuthToken(region, instanceId, userName string, credential *common.Credential) (string, error) {
// 实例化一个 client 选项,可选的,没有特殊需求可以跳过
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "cam.tencentcloudapi.com"
// 创建一个 GenerateAuthenticationTokenRequest 对象,ClientProfile 是可选的
tokenRequest, err := model.NewGenerateAuthenticationTokenRequest(region, instanceId, userName, credential, cpf)
if err != nil {
logrus.Errorf("Failed to create GenerateAuthenticationTokenRequest: %v", err)
return "", err
}

return dbauth.GenerateAuthenticationToken(tokenRequest)
}

附录4:通过 PHP 连接数据库

ZTS PHP
NTS PHP

依赖环境

依赖环境:PHP 7.2版本及以上。
PHP 扩展(必需):
ext-openssl(用于加密解密)。
ext-json(用于 JSON 处理)。
ext-parallel(用于高性能异步定时器,需要 ZTS PHP)。
ext-apcu(用于跨进程共享内存 Token 缓存)。
1. 使用前需要在控制台 启用 CAM 验证
2. 在腾讯云控制台,账号信息 页面,查询账号的 APPID。
3. 访问管理控制台 > API 密钥管理 获取 SecretID 和 SecretKey。
注意:
定时器实现:
使用 parallel 扩展实现高性能异步定时器。
现代 PHP 8.x 兼容的线程解决方案。
需要 ZTS PHP + parallel 扩展。
Token 缓存:
使用 APCu 实现跨进程共享内存缓存(必需)。
自动清理过期 Token。

使用

安装 SDK
composer require tencentcloud/dbauth-sdk-php
安装 PHP 扩展(必需)
parallel 扩展(必需 - 高性能异步定时器):必须安装 parallel 扩展以支持自动刷新功能。
# 1. 安装 ZTS PHP(线程安全版本)
# Ubuntu/Debian:
sudo apt-get install php-zts php-dev

# CentOS/RHEL:
sudo yum install php-zts php-devel

# macOS (使用 phpbrew):
phpbrew install <version> +default +zts
phpbrew switch <version>

# 2. 安装 parallel 扩展
pecl install parallel

# 3. 启用扩展(编辑 php.ini)
echo "extension=parallel.so" >> $(php -i | grep 'Loaded Configuration' | awk '{print $NF}')

# 4. 验证安装
php -m | grep parallel
php -i | grep "Thread Safety" # 应显示 "enabled"
APCu 扩展(必需 - 共享内存 Token 缓存):APCu 扩展用于跨进程的共享内存 Token 缓存,必须安装。
# 安装 APCu 扩展
pecl install apcu

# 在 php.ini 中启用
echo "extension=apcu.so" >> /etc/php.ini
echo "apc.enable_cli=1" >> /etc/php.ini # CLI 使用必需

# 验证安装
php -m | grep apcu

重要注意事项

定时器线程管理
由于使用了 parallel 扩展的异步定时器功能,在进程结束时必须手动清理定时器线程:
// 在应用程序退出前调用,确保所有定时器线程正确退出
\\TencentCloud\\DBAuth\\Internal\\TimerManager::cancelAllTimers();
为什么需要手动调用?
parallel 扩展创建的线程不会随主进程自动退出。
如果不手动取消,定时器线程会继续运行,可能导致资源泄漏。

使用 PHP 连接数据库示例

<?php
require_once 'vendor/autoload.php';

use TencentCloud\\Common\\Credential;
use TencentCloud\\Common\\Profile\\ClientProfile;
use TencentCloud\\Common\\Profile\\HttpProfile;
use TencentCloud\\DBAuth\\DBAuthentication;
use TencentCloud\\DBAuth\\Model\\GenerateAuthenticationTokenRequest;

// 定义数据库连接参数
$region = "ap-guangzhou";
$instanceId = "cdb-123456";
$userName = "camtest";
$host = "gz-cdb-123456.sql.tencentcdb.com";
$port = 3306;
$dbName = "test";
$ak = getenv("TENCENTCLOUD_SECRET_ID");
$sk = getenv("TENCENTCLOUD_SECRET_KEY");

// 获取连接
$connection = getDBConnectionUsingCam($ak, $sk, $region, $instanceId, $userName, $host, $port, $dbName);

// 验证连接是否成功
$stmt = $connection->query("SELECT 'Success!';");
foreach ($stmt as $row) {
echo $row[0] . "\\n"; // Success!
}

// 关闭连接
$stmt = null;
$connection = null;

// 重要:在进程结束时手动取消所有定时器,确保线程正确退出
\\TencentCloud\\DBAuth\\Internal\\TimerManager::cancelAllTimers();

// 使用 CAM 获取数据库连接
function getDBConnectionUsingCam($secretId, $secretKey, $region, $instanceId, $userName, $host, $port, $dbName) {
$credential = new Credential($secretId, $secretKey);
$maxAttempts = 3;
$lastException = null;

for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
try {
// 获取认证 Token
$authToken = getAuthToken($region, $instanceId, $userName, $credential);
$connectionUrl = "mysql:host=$host;port=$port;dbname=$dbName;charset=utf8mb4";
$pdo = new PDO($connectionUrl, $userName, $authToken, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
// 测试连接
$pdo->query("SELECT 1");
return $pdo;
} catch (Exception $e) {
$lastException = $e;
echo "连接失败。尝试 $attempt 失败。\\n";
sleep(5);
}
}

echo "所有尝试均失败。错误: " . $lastException->getMessage() . "\\n";
throw $lastException;
}

// 获取认证Token
function getAuthToken($region, $instanceId, $userName, $credential) {
// 实例化一个client选项,可选的,没有特殊需求可以跳过
$httpProfile = new HttpProfile();
$httpProfile->setEndpoint("cam.tencentcloudapi.com");
$clientProfile = new ClientProfile();
$clientProfile->setHttpProfile($httpProfile);
// 创建一个 GenerateAuthenticationTokenRequest 对象,ClientProfile 是可选的
$request = GenerateAuthenticationTokenRequest::builder()
->region($region)
->instanceId($instanceId)
->userName($userName)
->credential($credential)
->clientProfile($clientProfile)
->build();

return DBAuthentication::generateAuthenticationToken($request);
}

依赖环境

依赖环境:PHP 7.4版本及以上。
PHP 扩展(必需):
ext-openssl(用于加密解密)。
ext-json(用于 JSON 处理)。
ext-shmop(必需,用于 Token 缓存)。
1. 使用前需要在控制台 启用 CAM 验证
2. 在腾讯云控制台,账号信息 页面,查询账号的 APPID。
3. 访问管理控制台 > API 密钥管理 获取 SecretID 和 SecretKey。
注意:
进程管理:
所有平台(Windows/Linux/macOS):使用proc_open()实现后台定时器进程。
清空共享内存:
在启动进程时:需要调用DBAuthentication::clearCache()清空共享内存,避免访问到过期的 token。
Token 缓存(必需):
必须安装 shmop 扩展用于 Token 缓存。
用于实现跨进程 Token 共享(大幅提升性能,避免重复请求 CAM 服务)。
Windows、Linux 和 macOS 均支持。

使用

安装 SDK
composer require tencentcloud/dbauth-sdk-php
安装 PHP 扩展(必需)
必须安装 shmop 扩展用于 Token 缓存:
注意:
shmop 扩展是必需的,如果未安装,SDK 将无法正常工作。
Linux(Ubuntu/Debian)
# shmop 通常已内置在 PHP 中,验证是否已启用
php -m | grep shmop

# 如果未启用,需要重新编译 PHP 并添加 --enable-shmop 选项
# 或安装包含 shmop 的 PHP 包
sudo apt-get update
sudo apt-get install php-common

# 重启 PHP-FPM
sudo systemctl restart php-fpm
macOS
# shmop 通常已内置在 PHP 中,验证是否已启用
php -m | grep shmop

# 如果未启用,可能需要在 php.ini 中启用
# 或通过 Homebrew 重新安装 PHP
brew reinstall php

# 如果使用 PHP-FPM
brew services restart php
Windows
# shmop 通常已内置在 PHP 中,验证是否已启用
php -m | grep shmop

# 如果未启用,在 php.ini 中取消注释或添加:
extension=shmop

# 重启 Web 服务器
配置(php.ini)
extension=shmop ; 启用 shmop 扩展

重要注意事项

在使用 SDK 之前,请注意以下重要步骤:
1. 进程启动时:调用 DBAuthentication::clearCache() 清空共享内存,避免访问到过期的 token。
2. 进程结束时:调用 TencentCloud\\DBAuth\\Internal\\TimerManager::cancelAllTimers() 正确清理定时器资源。

使用 PHP 连接数据库示例

<?php
require_once 'vendor/autoload.php';

use TencentCloud\\Common\\Credential;
use TencentCloud\\Common\\Profile\\ClientProfile;
use TencentCloud\\Common\\Profile\\HttpProfile;
use TencentCloud\\DBAuth\\DBAuthentication;
use TencentCloud\\DBAuth\\Model\\GenerateAuthenticationTokenRequest;
use TencentCloud\\DBAuth\\Internal\\Constants;
use TencentCloud\\DBAuth\\Internal\\TimerManager;

// 定义数据库连接参数
$region = "ap-guangzhou";
$instanceId = "cdb-123456";
$userName = "camtest";
$host = "gz-cdb-123456.sql.tencentcdb.com";
$port = 3306;
$dbName = "test";
$ak = getenv("TENCENTCLOUD_SECRET_ID");
$sk = getenv("TENCENTCLOUD_SECRET_KEY");

// 清空共享内存(重要:在启动进程时调用,避免访问到过期的token)
DBAuthentication::clearCache();

// 获取连接
$connection = getDBConnectionUsingCam($ak, $sk, $region, $instanceId, $userName, $host, $port, $dbName);

// 验证连接是否成功
$stmt = $connection->query("SELECT 'Success!';");
foreach ($stmt as $row) {
echo $row[0] . "\\n"; // Success!
}

// 关闭连接
$stmt = null;
$connection = null;

// 取消所有定时器(重要:在进程结束时调用,正确清理定时器资源)
TimerManager::cancelAllTimers();

// 使用 CAM 获取数据库连接
function getDBConnectionUsingCam($secretId, $secretKey, $region, $instanceId, $userName, $host, $port, $dbName) {
$credential = new Credential($secretId, $secretKey);
$maxAttempts = 3;
$lastException = null;

for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
try {
// 获取认证 Token
$authToken = getAuthToken($region, $instanceId, $userName, $credential);
$connectionUrl = "mysql:host=$host;port=$port;dbname=$dbName;charset=utf8mb4";
$pdo = new PDO($connectionUrl, $userName, $authToken, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
// 测试连接
$pdo->query("SELECT 'Success'");
return $pdo;
} catch (PDOException $e) {
$lastException = $e;
echo "连接失败。尝试 $attempt 失败。\\n";
sleep(5);
}
}

echo "所有尝试均失败。错误: " . $lastException->getMessage() . "\\n";
throw $lastException;
}

// 获取认证 Token
function getAuthToken($region, $instanceId, $userName, $credential) {
// 实例化一个 client 选项,可选的,没有特殊需求可以跳过
$httpProfile = new HttpProfile();
$httpProfile->setEndpoint(Constants::CAM_ENDPOINT);
$clientProfile = new ClientProfile();
$clientProfile->setHttpProfile($httpProfile);
// 创建一个 GenerateAuthenticationTokenRequest 对象,ClientProfile 是可选的
$request = GenerateAuthenticationTokenRequest::builder()
->region($region)
->instanceId($instanceId)
->userName($userName)
->credential($credential)
->clientProfile($clientProfile)
->build();

return DBAuthentication::generateAuthenticationToken($request);
}

附录5:通过 .NET 连接数据库

依赖环境

依赖环境:.NET 6.0及以上版本。
1. 使用前需要在控制台 启用 CAM 验证
2. 在腾讯云控制台,账号信息 页面,查询账号的 APPID。
3. 访问管理控制台 > API 密钥管理 获取 SecretID 和 SecretKey。

间接依赖

需要 TencentCloudSDK v3.0.1374及以上版本。
说明:
版本号统一在 Directory.Build.props 文件中管理。更新依赖时,请修改该文件中的版本属性,以确保所有项目的版本一致性。

使用

dotnet add package TencentCloudSDK --version 3.0.1374

日志配置

如需打印 SDK 的相关日志,请在初始化时调用DBAuthentication.SetLoggerFactory(loggerFactory),传入 ILoggerFactory 实例:
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole().SetMinimumLevel(LogLevel.Information);
});

DBAuthentication.SetLoggerFactory(loggerFactory);

使用 .NET 连接数据库示例

using System;
using System.Data;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using TencentCloud.Common;
using TencentCloud.Common.Profile;
using TencentCloud.DBAuth.SDK;
using TencentCloud.DBAuth.SDK.Internal;
using TencentCloud.DBAuth.SDK.Model;
using MySql.Data.MySqlClient;

// 注意:此示例需要 MySqlConnector 包来建立 MySQL 连接
// 要使用此示例,请添加以下 NuGet 包:
// dotnet add package MySqlConnector --version 2.3.7

namespace TencentCloud.DBAuth.SDK.Examples
{
public class ReadmeExample
{
public static async Task Main(string[] args)
{
// 配置日志记录
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole().SetMinimumLevel(LogLevel.Information);
});
DBAuthentication.SetLoggerFactory(loggerFactory);
var logger = loggerFactory.CreateLogger(typeof(ReadmeExample));

// 定义认证令牌的参数
var region = "ap-guangzhou";
var instanceId = "cdb-123456";
var userName = "camtest";
var host = "gz-cdb-123456.sql.tencentcdb.com";
var port = 3306;
var dbName = "test";
var ak = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID");
var sk = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY");

if (string.IsNullOrEmpty(ak) || string.IsNullOrEmpty(sk))
{
logger.LogError("必须设置 TENCENTCLOUD_SECRET_ID和TENCENTCLOUD_SECRET_KEY 环境变量");
return;
}

// 获取数据库连接并在使用完毕后自动释放
using var connection = await GetDBConnectionUsingCam(ak, sk, region, instanceId, userName, host, port, dbName, logger);
if (connection == null)
{
logger.LogError("获取连接失败");
return;
}

// 验证连接是否成功
// 在实际应用中,您将在此处使用连接
logger.LogInformation("成功!数据库连接已建立。");
}

// 使用CAM数据库认证获取数据库连接
static async Task<IDbConnection> GetDBConnectionUsingCam(
string secretId, string secretKey, string region, string instanceId,
string userName, string host, int port, string dbName, ILogger logger)
{
var credential = new Credential
{
SecretId = secretId,
SecretKey = secretKey
};
const int maxAttempts = 3;
Exception? lastErr = null;

for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
try
{
// 使用凭据获取认证令牌
var authToken = GetAuthToken(region, instanceId, userName, credential);
if (string.IsNullOrEmpty(authToken))
{
throw new InvalidOperationException("获取认证令牌失败");
}

// 注意:要使用 MySQL 连接,请安装 MySqlConnector 包
var connectionString = $"Server={host};Port={port};Database={dbName};User Id={userName};Password={authToken};";
var connection = new MySqlConnection(connectionString);
await connection.OpenAsync();
return connection;
}
catch (Exception ex)
{
lastErr = ex;
logger.LogWarning($"打开连接失败。尝试 {attempt}/{maxAttempts} 失败。");
if (attempt < maxAttempts)
{
logger.LogInformation("等待5秒后重试...");
await Task.Delay(5000);
}
}
}

logger.LogError($"所有尝试都失败。错误: {lastErr?.Message}");
return null;
}

// 获取认证令牌
static string GetAuthToken(string region, string instanceId, string userName, Credential credential)
{
// 实例化客户端配置,可选,如果没有特殊要求可以跳过
var clientProfile = new ClientProfile();
clientProfile.HttpProfile.Endpoint = Constants.CAM_EXTERNAL_ENDPOINT;
// 使用建造者模式创建 GenerateAuthenticationTokenRequest 对象
var tokenRequest = GenerateAuthenticationTokenRequest.NewBuilder()
.Region(region)
.InstanceId(instanceId)
.UserName(userName)
.Credential(credential)
.ClientProfile(clientProfile)
.Build();

return DBAuthentication.GenerateAuthenticationToken(tokenRequest);
}
}
}