通過API Key鑒權方式產生免登入的可分享的大盤連結。
背景資訊
-
原生的Grafana若需要直接存取大盤,要麼使用Snapshot功能,要麼開啟匿名模式。前者對訪問的資料做了鏡像,隨著時間變化無法查看後續更新的資料,後者若不配合IP白名單功能,則安全性較差。可觀測可視化 Grafana 版支援通過API Key鑒權方式產生免登入的可分享的大盤連結。
-
您可以將其分享給其他使用者。
-
使用這個連結的使用者將能夠訪問指定的大盤而無需登入,因為鑒權是通過API Key完成的。
-
-
Grafana 10.0.x版本中,API Key功能已被Service Account Token取代,但API Key仍可使用。Grafana 11.x版本中,API Key已被完全移除,API Keys管理頁面返回404,只能使用Service Account Token進行鑒權。更多資訊,請參見Service accounts。
步驟一:配置Grafana參數
登入ARMS控制台,在左側導覽列選擇。
在工作区管理頁面,單擊目標工作區ID。
-
在左側導覽列單擊参数设置。
-
在左側參數列表選擇aliyun,然後單擊修改参数。
-
修改api_key_share參數的運行參數為true,然後單擊保存并生效。
在左側導覽列選擇 aliyun 配置分類,在右側參數列表中找到 api_key_share 參數行。
-
可選:若期望連結用於iFrame內嵌,則需要額外調整以下參數。
-
跨域情況下
網域名稱需為HTTPS,並修改以下3個security參數。
allow_embedding=true cookie_samesite=none cookie_secure=true -
未跨域情況下
修改security下的allow_embedding參數為true,開啟iFrame內嵌即可。
-
如果iFrame嵌入到另外一個Grafana中,請在對應Grafana中調整panels參數。
enable_alpha = true disable_sanitize_html = true
-
步驟二:建立API Key
Grafana 9.0.x 和 10.0.x 建立 API Key 的步驟有所不同,請確認您的 Grafana 版本並選擇對應操作。
Grafana 11.x版本已完全移除API Key功能,無需執行Grafana 9.0.x的API Key建立步驟,直接按照Grafana 10.0.x版本的Service Account Token流程操作即可。
如果您從 9.0.x 升級到 10.0.x,在Administration > api keys頁面仍可看到Grafana 9.0.x中建立的API Key。您可以單擊migrate to service account將其遷移到Service Account,遷移後api keys頁面將被隱藏。原有API Key的使用不受影響。
Grafana 9.0.x版本
登入ARMS控制台,在左側導覽列選擇。
在工作区管理頁面,單擊目標工作區右側的访问地址URL連結進入Grafana。
說明如果需要登入Grafana,可以使用Grafana的Admin帳號和建立工作區時設定的密碼登入Grafana,或單擊Sign in with Alibaba Cloud直接使用當前購買工作區的阿里雲帳號登入Grafana。
-
單擊Grafana首頁左上方的
表徵圖。 -
在Grafana左側導覽列選擇Configuration > API keys。
說明進入該菜單需要Admin許可權。
-
單擊New API key或Add API key,然後設定以下參數。
參數
說明
Key name
API Key的名稱,不可以和已有的名稱重複。
Role
設定為Viewer。
Time to live
設定有效時間,例如60s(60秒)、10m(10分鐘)、1d(1天)。
-
單擊Add,在彈出的對話方塊中擷取並儲存API Key的值。
重要對話方塊關閉後將無法再次查看API Key的值。
在 API Key Created 彈窗中,Key 欄位右側提供 Copy 按鈕,請立即複製 API Key。彈窗下方展示了使用
AuthorizationHTTP header 認證請求的 curl 命令樣本:curl -H "Authorization: Bearer eyJrIjoi..." https://grafana-xxx.grafana.aliyuncs.com/api/dashboards/home。
Grafana 10.0.x版本
登入ARMS控制台,在左側導覽列選擇。
在工作区管理頁面,單擊目標工作區右側的访问地址URL連結進入Grafana。
說明如果需要登入Grafana,可以使用Grafana的Admin帳號和建立工作區時設定的密碼登入Grafana,或單擊Sign in with Alibaba Cloud直接使用當前購買工作區的阿里雲帳號登入Grafana。
-
單擊Grafana首頁左上方的
表徵圖。 -
在Grafana左側導覽列選擇。
重要-
進入該菜單需要Admin許可權。
-
服務帳號會佔用一個使用者帳號。
-
-
單擊Add service account,然後設定以下參數,然後單擊Create。
參數
說明
Display name
Service Account的名稱,不可以和已有的名稱重複。
Role
設定為Viewer。
-
單擊頁面右側的Add service account token,然後設定以下參數。
參數
說明
Display name
API Key的名稱,不可以和已有的名稱重複。
Expiration
設定有效時間,
-
No Expiration:無終止日期
-
Set Expiration date:設定有效期間
Expiration date
如果在Expiration中選擇Set Expiration date,則您需要設定有效期間的截止日。
-
-
單擊Generate token,然後在彈出的對話方塊單擊Copy to clipboard and close。
對話方塊關閉後將無法再次查看API Key的值。
步驟三:產生分享連結
Grafana 9.0.x版本
-
在Grafana頁面,進入需要分享的大盤頁面。
-
單擊
表徵圖,在Link頁簽擷取大盤分享連結。 -
在連結最後添加
&aliyun_api_key=<API Key值>,API Key值為上文步驟二中擷取的API Key。https://grafana-example.grafana.aliyuncs.com/d/TZWea****/test?orgId=1&from=167081684****&to=167083844****&aliyun_api_key=eyJr****WkIwNnN2c0RTSD****** -
使用帶有API Key的分享連結即可免登入訪問Grafana大盤。
Grafana 10.0.x版本
-
在Grafana頁面,進入需要分享的大盤頁面。
-
單擊
表徵圖,在链接頁簽擷取大盤分享連結。對話方塊中可配置鎖定時間範圍(將相對時間轉換為絕對時間)、主題(當前、深色、淺色)和縮短網址等選項,在底部連結網址地區單擊複製按鈕擷取連結。
-
在連結最後添加
&aliyun_api_key=<API Key值>,URL參數名aliyun_api_key保持不變,將值替換為上文步驟二中擷取的Service Account Token即可,無需額外改動。https://grafana-example.grafana.aliyuncs.com/d/TZWea****/test?orgId=1&from=167081684****&to=167083844****&aliyun_api_key=eyJrIjoiWkIwNnN2c0RTSD****** -
使用帶有Service Account Token的分享連結即可免登入訪問Grafana大盤。
步驟四:產生高安全性分享連結(可選)
步驟三:產生分享連結中產生的分享連結需要定期更換API Key,以避免API Key泄露造成的資料安全風險。本步驟分別介紹在9.0.x版本API Key和10.0.x版本Service Account Token情境下產生高安全性分享連結的方法。
在進行本步驟前,您需要先修改api_key_share_version的運行參數為v2。
登入ARMS控制台,在左側導覽列選擇。
-
在工作區管理頁面,單擊目標工作區ID,然後在左側導覽列單擊参数设置。
-
修改api_key_share_version參數的運行參數為v2,然後單擊保存并生效。
9.0.x版本API Key
-
將步驟二:建立API Key中擷取的API Key進行Base64解密。
Base64是網路上常見的用於傳輸8Bit位元組碼的編碼方式之一,Base64是一種基於64個可列印字元來表示二進位資料的方法。
-
搜尋常用工具網站進行解密,例如base64。
-
Java語言解密。
package main import java.util.Base64; public class Base64Example{ public static void main(String[] args) { String apiKey = "eyJr****REpzZGYzd2JIa0N3ekgyWjlWWmhrSTM5bWdGT2hGSmwiLCJuIjoidGVzdDEiLCJpZCI6MX0="; String decodeKey = new String(Base64.getDecoder().decode(apiKey)); System.out.println(decodeKey); } }運行結果:
{"k":"DJsd****HkCwzH2Z9VZhkI39mgFOhFJl","n":"test1","id":1} -
Go語言解密。
package main import "fmt" import "encoding/base64" func main() { apiKey := "eyJr****REpzZGYzd2JIa0N3ekgyWjlWWmhrSTM5bWdGT2hGSmwiLCJuIjoidGVzdDEiLCJpZCI6MX0=" decodeKey, err := base64.StdEncoding.DecodeString(apiKey) if err != nil { fmt.Println(err.Error()) return } fmt.Println(string(decodeKey)) }運行結果:
{"k":"DJsd****HkCwzH2Z9VZhkI39mgFOhFJl","n":"test1","id":1}
-
-
將上述解密的API Key值中的k進行PBKDF2加密。
PBKDF2(Password-Based Key Derivation Function 2)是一種基於密碼的密鑰匯出函數,用於從使用者提供的密碼和一些其他參數(如鹽值和迭代次數)安全地派生出加密金鑰。它主要應用於需要儲存使用者密碼的情境,旨在確保即使資料庫被泄露,攻擊者也難以直接擷取到使用者的純文字密碼或輕易破解派生出的密鑰。
加密有多種方式,以下是加密參數說明。
參數
說明
鹽salt
設定為API Key的名稱Name(即上面解密出來的n值),本樣本中為test1。
迭代次數iteration
設定為:10000
輸出位元組長度output len
設定為:50
加密金鑰長度key size
設定為:256
輸出類型
設定為:Hex
-
搜尋常用網站工具加密,例如df2。
-
Java語言加密。
package main import javax.crypto.SecretKeyFactory; import java.security.GeneralSecurityException; import javax.crypto.spec.PBEKeySpec; import java.security.spec.KeySpec; public class PBKDFExapmle { public static void main(String[] args) { String password = "DJsd****HkCwzH2Z9VZhkI39mgFOhFJl"; String salt = "test1"; int iterationCount = 10000; int outputLength = 50 * 8; try { KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), iterationCount, outputLength); SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); byte[] keyBytes = skf.generateSecret(spec).getEncoded(); System.out.println(bytesToHex(keyBytes)); } catch (GeneralSecurityException e) { e.printStackTrace(); } } private static String bytesToHex(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } }輸出:PBKDF2 Password
1e5b**80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882** -
Go語言加密。
package main import "fmt" import "encoding/hex" import "crypto/sha256" import "golang.org/x/crypto/pbkdf2" func main() { password:="DJsd****HkCwzH2Z9VZhkI39mgFOhFJl" salt="test1" newPasswd := pbkdf2.Key([]byte(password), []byte(salt), 10000, 50, sha256.New) encodePassword:= hex.EncodeToString(newPasswd) fmt.Println(encodePassword) }輸出:PBKDF2 Password
1e5b**80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882**
-
-
基於PBKDF2加密後的資訊構造MD5摘要簽名參數。
MD5資訊摘要演算法(MD5 Message-Digest Algorithm),是一種被廣泛使用的密碼散列函數,可以產出一個128位(16位元組)的散列值(hash value),用於確保資訊傳輸完整一致。
加密內容為:<PBKDF2 Password>+"_"+<當前系統時間整數,單位:秒>
PBKDF2 Password為:1e5b**80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882**
當前系統時間為:2024-09-20 17:12:13 ,則精確到秒的時間整數為:1726823533
所以需要MD5簽名的整體資訊為:1e5b**80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882**_1726823533
MD5簽名有多種方式:
-
搜尋常用網站工具加密,例如MD5。
-
Java代碼進行MD5簽名。
package main; import java.security.MessageDigest; public class MD5 { public static void main(String[] args) { String pbkdfPassword = "1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****"; long timeSeconds=System.currentTimeMillis()/1000; String key=pbkdfPassword+"_"+timeSeconds; System.out.println(MD5.getMD5String(key,"UTF-8")); } public static String getMD5String(String str, String charset) { try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); messageDigest.reset(); messageDigest.update(str.getBytes(charset)); byte[] byteArray = messageDigest.digest(); StringBuffer md5StrBuff = new StringBuffer(); for (int i = 0; i < byteArray.length; i++) { if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) { md5StrBuff.append("0").append( Integer.toHexString(0xFF & byteArray[i])); } else { md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); } } return md5StrBuff.toString().toLowerCase(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("MD5 error:"+e.getMessage()); } } } -
Go代碼進行MD5簽名。
package main import ( "crypto/md5" "encoding/hex" "fmt" "io" "time" "strconv" ) func main() { // 需要計算MD5的字串 pbkdfPassword := "1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****" timeSeconds:= time.Now().Unix() key:=pbkdfPassword+"_"+strconv.FormatInt(timeSeconds, 10) // 使用MD5包計算字串的MD5值 hash := md5.New() io.WriteString(hash, key) md5Str := hash.Sum(nil) // 將二進位的MD5值轉換成十六進位字串 md5StrHex := hex.EncodeToString(md5Str) fmt.Println("MD5 of", key, "is", md5StrHex) }
-
-
基於產生的MD5簽名資訊拼接連結參數。
參數說明:
參數名
說明
樣本
aliyun_api_key_sign
MD5簽名,隨時間變化。
例如:c3bf89b867cc88df72d507edc4d1****
aliyun_api_key_timestamp
簽章時間,當簽章時間和系統時間超過1分鐘後,簽名到期失效。
例如:1726823533
aliyun_api_key_name
API Key的名稱。
例如:test1
aliyun_api_key_org_id
API Key所在的組織Org ID。
例如:1
aliyun_api_key_expire_seconds
MD5簽名登入後失效時間,單位:秒。
預設:3600
樣本如下:
https://grafana-example.grafana.aliyuncs.com/d/TZWea****/test?orgId=1&from=167081684****&to=167083844****&aliyun_api_key_sign=c3bf89b867cc88df72d507edc4d1****&aliyun_api_key_timestamp=1726823533&aliyun_api_key_name=test1&aliyun_api_key_org_id=1至此您可以基於程式碼每次動態產生更安全的免密登入Grafana的連結,同時可以避免API Key泄露問題。
10.0.x版本Service Account Token
-
將步驟二:建立API Key中的API Key(10.0.x及之後版本稱之為Service Account Token)切分後進行PBKDF2加密。
PBKDF2(Password-Based Key Derivation Function 2)是一種基於密碼的密鑰匯出函數,用於從使用者提供的密碼和一些其他參數(如鹽值和迭代次數)安全地派生出加密金鑰。它主要應用於需要儲存使用者密碼的情境,旨在確保即使資料庫被泄露,攻擊者也難以直接擷取到使用者的純文字密碼或輕易破解派生出的密鑰。
將Service Account Token按“_”進行切分。
#以下列Service Account Token為例: Token:glsa_yV9HAOVCjNKkvKoLMiypOc5T0Oov****_4f5ff3ce #切分後為: 首碼:glsa Secret:yV9HAOVCjNKkvKoLMiypOc5T0Oov**** Salt:4f5ff3ce加密有多種方式,以下是加密參數說明。
參數
說明
鹽salt
設定為Token尾碼,本樣本中為4f5ff3ce。
迭代次數iteration
設定為:10000
輸出位元組長度output len
設定為:50
加密金鑰長度key size
設定為:256
輸出類型
設定為:Hex
-
搜尋常用工具網站進行解密,例如charsetpbkdf2。
-
Java語言解密。
package main import javax.crypto.SecretKeyFactory; import java.security.GeneralSecurityException; import javax.crypto.spec.PBEKeySpec; import java.security.spec.KeySpec; public class PBKDFExapmle { public static void main(String[] args) { String password = "yV9H****jNKkvKoLMiypOc5T0OovHXPV"; String salt = "4f5ff3ce"; int iterationCount = 10000; int outputLength = 50 * 8; try { KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), iterationCount, outputLength); SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); byte[] keyBytes = skf.generateSecret(spec).getEncoded(); System.out.println(bytesToHex(keyBytes)); } catch (GeneralSecurityException e) { e.printStackTrace(); } } private static String bytesToHex(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } }輸出:PBKDF2 Password
c3cd****971bab928e4ecd6e7a00c74657696ea07d38c43f3bb5dc3190f2285cb80695cf7bf2f25c9b1f34fe1e0f9549**** -
Go語言加密。
package main import "fmt" import "encoding/hex" import "crypto/sha256" import "golang.org/x/crypto/pbkdf2" func main() { password:="yV9H****jNKkvKoLMiypOc5T0OovHXPV" salt="4f5ff3ce" newPasswd := pbkdf2.Key([]byte(password), []byte(salt), 10000, 50, sha256.New) encodePassword:= hex.EncodeToString(newPasswd) fmt.Println(encodePassword) }輸出:PBKDF2 Password
c3cd****971bab928e4ecd6e7a00c74657696ea07d38c43f3bb5dc3190f2285cb80695cf7bf2f25c9b1f34fe1e0f9549****
-
-
基於PBKDF2加密後的資訊構造MD5摘要簽名參數。
MD5資訊摘要演算法(MD5 Message-Digest Algorithm),是一種被廣泛使用的密碼散列函數,可以產出一個128位(16位元組)的散列值(hash value),用於確保資訊傳輸完整一致。
加密內容為:<PBKDF2 Password>+"_"+<當前系統時間整數,單位:秒>
PBKDF2 Password為:1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****
當前系統時間為2024-09-20 17:12:13 ,則精確到秒的時間整數為:1726823533
所以需要MD5簽名的整體資訊為:1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****_1726823533
MD5簽名有多種方式:
-
搜尋常用網站工具加密,例如MD5。
-
Java代碼進行MD5簽名。
package main; import java.security.MessageDigest; public class MD5 { public static void main(String[] args) { String pbkdfPassword = "1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****"; long timeSeconds=System.currentTimeMillis()/1000; String key=pbkdfPassword+"_"+timeSeconds; System.out.println(MD5.getMD5String(key,"UTF-8")); } public static String getMD5String(String str, String charset) { try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); messageDigest.reset(); messageDigest.update(str.getBytes(charset)); byte[] byteArray = messageDigest.digest(); StringBuffer md5StrBuff = new StringBuffer(); for (int i = 0; i < byteArray.length; i++) { if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) { md5StrBuff.append("0").append( Integer.toHexString(0xFF & byteArray[i])); } else { md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); } } return md5StrBuff.toString().toLowerCase(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("MD5 error:"+e.getMessage()); } } } -
Go代碼進行MD5簽名。
package main import ( "crypto/md5" "encoding/hex" "fmt" "io" "time" "strconv" ) func main() { // 需要計算MD5的字串 pbkdfPassword := "1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****" timeSeconds:= time.Now().Unix() key:=pbkdfPassword+"_"+strconv.FormatInt(timeSeconds, 10) // 使用MD5包計算字串的MD5值 hash := md5.New() io.WriteString(hash, key) md5Str := hash.Sum(nil) // 將二進位的MD5值轉換成十六進位字串 md5StrHex := hex.EncodeToString(md5Str) fmt.Println("MD5 of", key, "is", md5StrHex) }
-
-
基於產生的MD5簽名資訊拼接連結參數。
參數說明:
參數名
說明
樣本
aliyun_api_key_sign
MD5簽名,隨時間變化。
例如:c3bf89b867cc88df72d507edc4d1****
aliyun_api_key_timestamp
簽章時間,當簽章時間和系統時間超過1分鐘後,簽名到期失效。
例如:1726823533
aliyun_api_key_name
Service Account Token的名稱。
例如:sa-1-******-*****-75c7-41ae-94da-*******
aliyun_api_key_org_id
Service Account Token所在的組織Org ID。
例如:1
aliyun_api_key_expire_seconds
MD5簽名登入後失效時間,單位:秒。
預設:3600
樣本如下:
https://grafana-example.grafana.aliyuncs.com/d/TZWea****/test?orgId=1&from=167081684****&to=167083844****&aliyun_api_key_sign=c3bf89b867cc88df72d507edc4d1****&aliyun_api_key_timestamp=1726823533&aliyun_api_key_name=test1&aliyun_api_key_org_id=1至此您可以基於程式碼每次動態產生更安全的免密登入Grafana的連結,同時可以避免Service Account Token泄露問題。
常見問題
-
通過共用串連訪問大盤時頁面報錯如下:訪問內嵌大盤頁面時,瀏覽器提示拒絕了我們的串連請求錯誤,無法正常載入頁面。
可能原因:內嵌大盤情況下,allow_embedding參數未設定。配置allow_embedding參數的操作,請參見上文步驟一。
-
無法顯示大盤資料。頁面顯示 Not found 提示,並彈出 Unauthorized 黃色警告通知,大盤主體地區無任何圖表或資料。
內嵌大盤情況下,Cookie無法寫入導致,可能原因如下:
-
跨域,即根網域名稱不同時,預設配置無法寫入Cookie。
-
cookie_samesite參數設定為none,但是cookie_secure參數設定為false。
-
網域名稱為HTTP。由於cookie_secure參數無法在HTTP下生效,因此網域名稱不支援HTTP。
解決方案:參考上文步驟一重新設定Grafana參數。
-
-
通過共用串連訪問大盤時頁面報錯如下:Grafana 報錯 If you're seeing this Grafana has failed to load its application files,頁面列出反向 Proxy配置、子路徑部署(需檢查
root_url或設定serve_from_sub_path = true)、本地開發構建、重啟 grafana-server 及瀏覽器安全色性等排查建議。可能原因:
-
瀏覽器版本過低。
-
內嵌大盤的情況下瀏覽器配置導致。
解決方案:
-
查看Cookie使用的配置,允許使用Cookie。
-
若使用Chrome瀏覽器,在無痕模式下需要配置允許所有Cookie。
-
-
-
如果用於大盤內嵌,API Key建議是設定較短的有效時間使用一次就更換,還是配置一個很長時間的免登Key?
您可以根據安全需要做配置,建議3個月換一次,若Key泄露可以通過刪除讓Key失效。
-
API Key是否有數量上限?
Key的建立官方源碼裡並沒有做限制,由於頁面查詢時最多展示100條,建議不要超過100個。
-
API Key配置的有效時間到期了,這個API Key會自動刪除嗎?
Key失效後,資料仍然存在,如果擔心佔用數量您可以手動刪除Key。Grafana頁面上失效的Key預設不展示,您可以單擊Include expired keys顯示已失效Key,然後刪除。不會自動刪除。在 API keys 管理頁面中,開啟 Include expired keys 開關後,可查看已到期的 API Key 記錄(包括名稱、角色和到期時間)。如需移除,可單擊對應記錄行末的刪除按鈕手動刪除。