安全凭据库
Keychain 存储敏感字段,读取前使用系统验证。
演示 keychain 存储敏感字段,读取前用 biometric.authenticate_with_passcode 校验。
#预期效果
运行后会出现安全凭据库,敏感字段保存到 Keychain,读取前会先走系统验证。
#完整示例
已复制
import appui
import biometric
import haptics
import keychain
state = appui.State(
service="pythonide.demo",
account="default",
secret="",
revealed="",
status="未保存",
auth_type="",
)
def update_service(value):
state.service = value
def update_account(value):
state.account = value
def update_secret(value):
state.secret = value
def refresh_auth_type():
state.auth_type = biometric.biometric_type()
def save_secret():
if not state.service.strip() or not state.account.strip() or not state.secret:
state.status = "请填写服务名、账号和 Token"
haptics.notification("warning")
return
keychain.set_password(state.service.strip(), state.account.strip(), state.secret)
state.batch_update(status="已保存到 Keychain", secret="", revealed="")
haptics.notification("success")
def reveal_secret():
result = biometric.authenticate_with_passcode("验证身份以读取 Keychain 凭据")
if not result.get("success"):
state.status = f"验证失败:{result.get('error', 'canceled')}"
haptics.notification("error")
return
value = keychain.get_password(state.service.strip(), state.account.strip())
if value is None:
state.batch_update(status="未找到对应凭据", revealed="")
haptics.notification("warning")
return
state.batch_update(status="读取成功", revealed=value)
haptics.notification("success")
def delete_secret():
keychain.delete_password(state.service.strip(), state.account.strip())
state.batch_update(status="已删除", secret="", revealed="")
haptics.notification("success")
def body():
return appui.NavigationStack(
appui.Form(
[
appui.Section(
[
appui.LabeledContent("当前验证方式", value=state.auth_type or "未知"),
appui.Button("刷新验证能力", action=refresh_auth_type),
],
header="设备验证",
footer="读取敏感凭据前使用系统验证;不要自己实现密码弹窗替代系统验证。",
),
appui.Section(
[
appui.TextField("服务名", text=state.service, on_change=update_service),
appui.TextField("账号", text=state.account, on_change=update_account),
appui.SecureField("Token 或密码", text=state.secret, on_change=update_secret),
],
header="凭据",
),
appui.Section(
[
appui.Button("保存到 Keychain", action=save_secret)
.button_style("bordered_prominent"),
appui.Button("验证并读取", action=reveal_secret),
appui.Button("删除", action=delete_secret).foreground_color("systemRed"),
],
header="操作",
),
appui.Section(
[
appui.LabeledContent("状态", value=state.status),
appui.Text(state.revealed or "验证后显示读取结果")
.font("callout")
.foreground_color("secondaryLabel"),
],
header="结果",
),
]
).navigation_title("凭据库")
)
appui.run(body, state=state, presentation="sheet")
#关键技巧
- 敏感字符串进
keychain,不要写入storage。 - 写入/读取/删除放在按钮动作里;
get_password返回None时要能恢复 UI 状态。 - 录入用
SecureField;展示前走系统生物识别/密码验证。