You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS 15.0下Keyring Crate返回成功却未存储凭据至钥匙串

macOS钥匙串存储凭据成功但找不到问题排查

问题描述

我正在用Rust开发一款数据库客户端,使用keyring crate将凭据存储到Apple钥匙串中。存储凭据时代码返回成功消息,但在钥匙串中找不到这些凭据。

相关代码

use crate::structure::database::StoredConnectionParams;
use keyring::Entry;
use serde::{Deserialize, Serialize}; // Import the StoredConnectionParams struct

#[derive(Debug, Serialize, Deserialize)]
pub struct CredentialStorageResult {
    pub success: bool,
    pub message: String,
}

pub fn store_database_credentials(
    params: StoredConnectionParams,
) -> Result<CredentialStorageResult, String> {
    let connection_id = params.connection_id.to_string();
    let keyring_service = format!("datamizu-db-{}", connection_id);
    let entry = Entry::new(&keyring_service, &connection_id)
        .map_err(|e| format!("Failed to create keyring entry: {}", e))?;

    let credentials = serde_json::to_string(&params.credentials)
        .map_err(|e| e.to_string())?;

    match entry.set_password(&credentials) {
        Ok(_) => Ok(CredentialStorageResult {
            success: true,
            message: format!(
                "Credentials for {} connection {} stored successfully",
                params.data_source_type, connection_id
            ),
        }),
        Err(e) => Err(format!(
            "Failed to store credentials for {} connection {}: {}",
            params.data_source_type, connection_id, e
        )),
    }
}

pub fn fetch_database_credentials(
    connection_id: String,
) -> Result<StoredConnectionParams, String> {
    let keyring_service = format!("datamizu-db-{}", connection_id);
    let entry = Entry::new(&keyring_service, &connection_id)
        .map_err(|e| format!("Failed to create keyring entry: {}", e))?;

    match entry.get_password() {
        Ok(credentials) => serde_json::from_str(&credentials)
            .map_err(|e| format!("Failed to parse stored credentials: {}", e)),
        Err(e) => Err(format!("Failed to retrieve credentials: {}", e)),
    }
}

已执行步骤

  • 通过Keychain Access手动检查macOS钥匙串,未找到格式为datamizu-db-<connection_id>的条目;
  • 在终端使用security find-generic-password命令,未列出我的服务对应的密码条目;
  • 代码执行过程无错误,返回成功。

核心问题

为何keyring crate返回成功,但凭据却未出现在macOS钥匙串中?这是否与macOS的应用权限或沙箱机制有关?

补充信息

  • macOS版本:15.0
  • Rust版本:cargo 1.81.0
  • Keyring crate版本:3.3.0

问题分析与解决办法

1. 钥匙串分组/显示范围问题

macOS 15.0(Sequoia)的Keychain Access可能将凭据归类到特定分组:

  • 检查登录钥匙串下的密码分类,切换到所有项目视图;
  • 尝试查看系统钥匙串,确认条目是否被存储到了其他位置;
  • 命令行程序存储的凭据有时不会默认在图形界面显示,需调整搜索过滤条件。

2. 沙箱权限隔离

如果你的应用启用了App Sandbox(比如通过Xcode打包),沙箱会限制钥匙串访问范围:

  • 沙箱内的应用只能访问自身创建的隔离条目,这类条目不会出现在普通Keychain Access视图中;
  • 命令行工具无需启用沙箱,若误开启需关闭沙箱配置;
  • 如需跨应用访问凭据,需在签名配置中添加Keychain Sharing权限。

3. keyring crate的实现细节

keyring在macOS上依赖Security Framework,部分条目可能因ACL设置无法直接查看:

  • 使用更精确的命令查找条目:
    security find-generic-password -s "datamizu-db-<你的connection_id>" -g
    
    该命令会强制匹配服务名,即使条目有特殊权限配置。

4. 条目命名逻辑问题

代码中用connection_id同时作为服务名后缀和账户名,可能导致搜索混淆:

  • 修改服务名为固定值,账户名保留connection_id,更符合钥匙串常规命名:
    // 存储函数中修改服务名
    let keyring_service = "datamizu-db";
    let entry = Entry::new(keyring_service, &connection_id)
        .map_err(|e| format!("Failed to create keyring entry: {}", e))?;
    
    调整后在Keychain Access中搜索datamizu-db即可找到所有相关条目。

5. 验证实际存储状态

调用fetch_database_credentials函数读取凭据:

  • 若能成功读取,说明凭据已存储,仅为显示问题;
  • 若读取失败,需排查序列化/反序列化逻辑或潜在的权限问题。

内容的提问来源于stack exchange,提问作者Kida

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 12:57:09