通过 Account API 进行账户设置
什么是 Logto Account API
Logto Account API 是一套全面的 API,允许终端用户直接通过 API 访问,无需经过 Management API。主要亮点如下:
- 直接访问:Account API 让终端用户可以直接访问和管理自己的账户资料,无需通过 Management API 中转。
- 用户资料与身份管理:用户可以完全管理自己的资料和安全设置,包括更新邮箱、手机号、密码等身份信息,以及管理社交连接。MFA 和 SSO 支持即将上线。
- 全局访问控制:管理员拥有对访问设置的完全全局控制,并可自定义每个字段。
- 无缝授权 (Authorization):授权 (Authorization) 变得前所未有的简单!只需使用
client.getAccessToken()获取 OP(Logto)不透明令牌 (Opaque token),并将其作为Bearer <access_token>附加到 Authorization 头部即可。
通过 Logto Account API,你可以构建一个与 Logto 完全集成的自定义账户管理系统,比如个人资料页。
常见的使用场景包括:
- 获取用户资料
- 更新用户资料
- 更新用户密码
- 更新用户身份信息(包括邮箱、手机号和社交连接)
- 管理 MFA 因子(验证方式)
- 管理用户会话
- 管理用户已授权应用(授权 (Authorization))
想了解更多可用 API,请访问 Logto Account API 参考文档 和 Logto Verification API 参考文档。
SSO 账户查看和账户删除功能目前通过 Logto Management API 提供。具体实现细节请参见 通过 Management API 进行账户设置。
如何启用 Account API
Account API 默认关闭,因此其访问控制设置为锁定。切换 启用 Account API 开关即可开启。
启用后,可以配置 近期认证 (Authentication) 窗口(1 到 120 分钟,默认 10 分钟),以及标识符、资料数据和第三方令牌访问的每字段权限。每个字段支持 关闭、只读 或 可编辑,默认是 关闭。
- 安全字段:
- 包括:主邮箱、主手机号、社交身份、密码、Passkey、MFA、多因素认证 (MFA)、个人访问令牌。
- 敏感操作需要来自一方应用的近期用户认证 (Authentication)——即 access token 的
acr满足urn:logto:acr:1fa,且在配置的近期认证 (Authentication) 窗口内(详见 OIDC 提升认证 (Authentication))。 - 若要使用 WebAuthn Passkey 作为 MFA,请将前端应用域名添加到 WebAuthn 相关来源,以便账户中心和登录体验共享 Passkey。详见 绑定新的 WebAuthn Passkey。
- 资料字段:
- 密钥保险库:
- 对于 OIDC 或 OAuth 社交和企业连接器,Logto 密钥保险库 会在认证 (Authentication) 后安全存储第三方访问令牌 (Access token) 和刷新令牌 (Refresh token)。应用可调用外部 API(如同步 Google 日历事件),无需再次提示用户登录。启用 Account API 后,令牌检索自动可用。
- 会话管理:
- 启用后,用户可查看和管理其活跃会话,包括设备信息和上次登录时间。用户还可撤销会话,从特定设备登出。
- 同一个
Sessions字段权限也控制用户已授权应用(授权 (Authorization))API(查看和撤销授权 (Authorization))。 - 访问会话和授权 (Authorization) 管理需与安全字段相同的近期用户认证 (Authentication)。
如何访问 Account API
为确保访问令牌 (Access token) 拥有合适权限,请确保你已在 Logto 配置中正确设置了对应的权限 (Scope)。
例如,POST /api/my-account/primary-email API 需要配置 email 权限 (Scope);POST /api/my-account/primary-phone API 需要配置 phone 权限 (Scope)。
import { type LogtoConfig, UserScope } from '@logto/js';
const config: LogtoConfig = {
// ...其他选项
// 添加适合你用例的权限 (Scope)
scopes: [
UserScope.Email, // 用于 `{POST,DELETE} /api/my-account/primary-email` API
UserScope.Phone, // 用于 `{POST,DELETE} /api/my-account/primary-phone` API
UserScope.CustomData, // 管理自定义数据
UserScope.Address, // 管理地址
UserScope.Identities, // 身份和 MFA 相关 API
UserScope.Profile, // 管理用户资料
UserScope.Sessions, // 管理用户会话和应用授权 (Authorization)
],
};
获取访问令牌 (Access token)
在应用中配置好 SDK 后,可以使用 client.getAccessToken() 方法获取访问令牌 (Access token)。该令牌为不透明令牌 (Opaque token),可用于访问 Account API。
如果你未使用官方 SDK,请在向 /oidc/token 申请访问令牌 (Access token) 时将 resource 设为空。
使用访问令牌 (Access token) 访问 Account API
在调用 Account API 时,应将访问令牌 (Access token) 以 Bearer 格式(Bearer YOUR_TOKEN)放在 HTTP 头部的 Authorization 字段中。
以下是获取用户账户信息的示例:
curl https://[tenant-id].logto.app/api/my-account \
-H 'authorization: Bearer <access_token>'
管理基础账户信息
获取用户账户信息
要获取用户数据,可以使用 GET /api/my-account 接口。
curl https://[tenant-id].logto.app/api/my-account \
-H 'authorization: Bearer <access_token>'
响应体示例:
{
"id": "...",
"username": "...",
"name": "...",
"avatar": "..."
}
响应字段会根据账户中心设置有所不同。
更新基础账户信息
基础账户信息包括用户名、姓名、头像、自定义数据及其他资料信息。
要更新 username、name、avatar 和 customData,可使用 PATCH /api/my-account 接口。
curl -X PATCH https://[tenant-id].logto.app/api/my-account \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"username":"...","name":"...","avatar":"..."}'
要更新其他资料信息,包括 familyName、givenName、middleName、nickname、profile(个人主页 URL)、website、gender、birthdate、zoneinfo、locale 和 address,可使用 PATCH /api/my-account/profile 接口。
curl -X PATCH https://[tenant-id].logto.app/api/my-account/profile \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"familyName":"...","givenName":"..."}'
管理标识符和其他敏感信息
出于安全考虑,敏感的 Account API 操作需要来自一方应用的近期用户认证 (Authentication):access token 必须携带 acr 满足 urn:logto:acr:1fa,且 auth_time 在租户的 近期认证 (Authentication) 窗口内(默认 600 秒 / 10 分钟,可配置为 60 到 7,200 秒)。
当 access token 不含 urn:logto:acr:1fa 或已超出窗口时,Account API 会返回 401 Unauthorized,并带有 RFC 9470 WWW-Authenticate 头(error="insufficient_user_authentication",acr_values="urn:logto:acr:1fa",max_age="<window>")。请使用这些参数发起一次 OIDC 提升认证 (Authentication) 请求,并用新颁发的 access token 重试。
绑定新邮箱、手机号、社交身份或 WebAuthn Passkey 时,也需通过 Verification API 验证新标识符或因子的所有权,并在请求体中传递 newIdentifierVerificationRecordId,详见下方各节。
更新用户密码
要更新用户密码,可使用 POST /api/my-account/password 接口。
curl -X POST https://[tenant-id].logto.app/api/my-account/password \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"password":"..."}'
与注册时设置密码一样,通过 Account API 设置的密码也必须符合你在 控制台 > 安全 > 密码策略 配置的 密码策略。如不符合,Logto 会返回详细的校验结果和错误信息。
更新或绑定新邮箱
使用此方法前,请先配置邮箱连接器,并确保已配置 BindNewIdentifier 模板。
要更新或绑定新邮箱,需先证明对该邮箱的所有权。
调用 POST /api/verifications/verification-code 接口请求验证码。
curl -X POST https://[tenant-id].logto.app/api/verifications/verification-code \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"identifier":{"type":"email","value":"..."}}'
你将在响应中获得 verificationId,并在邮箱中收到验证码,使用该验证码进行验证。
curl -X POST https://[tenant-id].logto.app/api/verifications/verification-code/verify \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"identifier":{"type":"email","value":"..."},"verificationId":"...","code":"..."}'
验证码验证通过后,可调用 PATCH /api/my-account/primary-email 更新用户邮箱,将 verificationId 作为 newIdentifierVerificationRecordId 放入请求体。
curl -X POST https://[tenant-id].logto.app/api/my-account/primary-email \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"email":"...","newIdentifierVerificationRecordId":"<verification_record_id_from_new_email>"}'
与注册时收集邮箱一样,通过 Account API 绑定的邮箱也必须通过你在 控制台 > 安全 > 黑名单 配置的 黑名单 校验。如不符合,Logto 会拒绝请求并返回详细错误。
移除用户邮箱
要移除用户邮箱,可使用 DELETE /api/my-account/primary-email 接口。
curl -X DELETE https://[tenant-id].logto.app/api/my-account/primary-email \
-H 'authorization: Bearer <access_token>'
管理手机号
使用此方法前,请先配置短信连接器,并确保已配置 BindNewIdentifier 模板。
与更新邮箱类似,可使用 PATCH /api/my-account/primary-phone 接口更新或绑定新手机号。使用 DELETE /api/my-account/primary-phone 接口移除用户手机号。
绑定新的社交连接
要绑定新的社交连接,首先需通过 POST /api/verifications/social 请求授权 (Authorization) URL。
curl -X POST https://[tenant-id].logto.app/api/verifications/social \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"connectorId":"...","redirectUri":"...","state":"..."}'
connectorId:对应 社交连接器 的 ID。redirectUri:用户授权 (Authorization) 应用后跳转的 URI,你应在此 URL 上托管网页并捕获回调。state:用户授权 (Authorization) 应用后返回的状态,是用于防止 CSRF 攻击的随机字符串。
响应中会有 verificationRecordId,请妥善保存。
用户授权 (Authorization) 应用后,你会在 redirectUri 收到带有 state 参数的回调。然后可使用 POST /api/verifications/social/verify 验证社交连接。
curl -X POST https://[tenant-id].logto.app/api/verifications/social/verify \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"connectorData":"...","verificationRecordId":"..."}'
connectorData 是用户授权 (Authorization) 应用后社交连接器返回的数据,你需在回调页面解析 redirectUri 的查询参数,并将其封装为 JSON 作为 connectorData 字段的值。
最后,可使用 POST /api/my-account/identities 绑定社交连接。
curl -X POST https://[tenant-id].logto.app/api/my-account/identities \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"newIdentifierVerificationRecordId":"<verification_record_id_from_social>"}'
移除社交连接
要移除社交连接,可使用 DELETE /api/my-account/identities 接口。
curl -X DELETE https://[tenant-id].logto.app/api/my-account/identities/[connector_target_id] \
-H 'authorization: Bearer <access_token>'
绑定新的 WebAuthn Passkey
使用此方法前,请在账户中心设置中启用 mfa 字段。
步骤 1:将前端应用域名添加到相关来源
WebAuthn Passkey 绑定到特定主机名(Relying Party ID (RP ID))。只有托管在 RP ID 所属域名的应用才能注册或认证 (Authentication) 这些 Passkey。
由于你的前端应用调用 Account API 的域名与 Logto 认证 (Authentication) 页面的域名不同,你需要配置 相关来源 以允许跨域 Passkey 操作。
Logto 如何确定 RP ID:
- 默认设置:仅使用 Logto 默认域名
https://[tenant-id].logto.app时,RP ID 为[tenant-id].logto.app - 自定义域名:如你配置了 自定义域名(如
https://auth.example.com),RP ID 为auth.example.com
配置相关来源:
使用 PATCH /api/account-center 接口添加前端应用的域名。例如,账户中心运行在 https://account.example.com:
curl -X PATCH https://[tenant-id].logto.app/api/account-center \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"webauthnRelatedOrigins":["https://account.example.com"]}'
WebAuthn 最多支持 5 个唯一 eTLD+1 标签作为相关来源。eTLD+1(有效顶级域加一标签)是可注册的域名部分。例如:
https://example.com、https://app.example.com、https://auth.example.com计为一个标签(example.com)https://shopping.com、https://shopping.co.uk、https://shopping.co.jp也计为一个标签(shopping)https://example.com和https://another.com计为两个标签
如需支持超过 5 个不同域名作为相关来源,请参考 Related Origin Requests 文档。
步骤 2:请求新的注册选项
使用 POST /api/verifications/web-authn/registration 接口请求注册新 Passkey。Logto 允许每个账户注册多个 Passkey。
curl -X POST https://[tenant-id].logto.app/api/verifications/web-authn/registration \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json'
你将获得如下响应:
{
"registrationOptions": "...",
"verificationRecordId": "...",
"expiresAt": "..."
}
步骤 3:在本地浏览器注册 Passkey
以 @simplewebauthn/browser 为例,可用 startRegistration 方法在本地浏览器注册 Passkey。
import { startRegistration } from '@simplewebauthn/browser';
// ...
const response = await startRegistration({
optionsJSON: registrationOptions, // 第 1 步服务器返回的数据
});
// 保存 response 以备后用
步骤 4:验证 Passkey 注册
使用 POST /api/verifications/web-authn/registration/verify 接口验证 Passkey 注册。
此步骤会验证认证器生成的加密签名,确保 Passkey 是合法创建且未被篡改。
curl -X POST https://[tenant-id].logto.app/api/verifications/web-authn/registration/verify \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"payload":"...","verificationRecordId":"..."}'
payload:第 2 步本地浏览器返回的响应。verificationRecordId:第 1 步服务器返回的验证记录 ID。
步骤 5:绑定 Passkey
最后,可通过 POST /api/my-account/mfa-verifications 接口将 Passkey 绑定到用户账户。
curl -X POST https://[tenant-id].logto.app/api/my-account/mfa-verifications \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"type":"WebAuthn","newIdentifierVerificationRecordId":"..."}'
type:MFA 因子类型,目前仅支持WebAuthn。newIdentifierVerificationRecordId:第 1 步服务器返回的验证记录 ID。
管理已有 WebAuthn Passkey
要管理已有 WebAuthn Passkey,可使用 GET /api/my-account/mfa-verifications 获取当前 Passkey 及其他 MFA 验证因子。
curl https://[tenant-id].logto.app/api/my-account/mfa-verifications \
-H 'authorization: Bearer <access_token>'
响应体示例:
[
{
"id": "...",
"type": "WebAuthn",
"name": "...",
"agent": "...",
"createdAt": "...",
"updatedAt": "..."
}
]
id:验证因子 ID。type:验证因子类型,WebAuthn Passkey 为WebAuthn。name:Passkey 名称,选填。agent:Passkey 的用户代理信息。
通过 PATCH /api/my-account/mfa-verifications/{verificationId}/name 更新 Passkey 名称:
curl -X PATCH https://[tenant-id].logto.app/api/my-account/mfa-verifications/{verificationId}/name \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"name":"..."}'
通过 DELETE /api/my-account/mfa-verifications/{verificationId} 删除 Passkey:
curl -X DELETE https://[tenant-id].logto.app/api/my-account/mfa-verifications/{verificationId} \
-H 'authorization: Bearer <access_token>'
绑定新的 TOTP
使用此方法前,请在账户中心设置中启用 mfa 字段。
步骤 1:生成 TOTP 密钥
使用 POST /api/my-account/mfa-verifications/totp-secret/generate 接口生成 TOTP 密钥。
curl -X POST https://[tenant-id].logto.app/api/my-account/mfa-verifications/totp-secret/generate \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json'
响应体示例:
{
"secret": "..."
}
步骤 2:向用户展示 TOTP 密钥
用该密钥生成二维码或直接展示给用户。用户应将其添加到自己的认证器应用(如 Google Authenticator、Microsoft Authenticator 或 Authy)。
二维码的 URI 格式为:
otpauth://totp/[Issuer]:[Account]?secret=[Secret]&issuer=[Issuer]
示例:
otpauth://totp/YourApp:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=YourApp
步骤 3:绑定 TOTP 因子
用户将密钥添加到认证器应用后,需要验证并绑定到账户。使用 POST /api/my-account/mfa-verifications 接口绑定 TOTP 因子。
curl -X POST https://[tenant-id].logto.app/api/my-account/mfa-verifications \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"type":"Totp","secret":"..."}'
type:必须为Totp。secret:第 1 步生成的 TOTP 密钥。
每个用户只能有一个 TOTP 因子。如已存在 TOTP 因子,尝试添加会返回 422 错误。
替换已有 TOTP
如用户已配置 TOTP 因子,可通过 PUT /api/my-account/mfa-verifications/totp 替换为新密钥。此操作幂等,会移除旧 TOTP 并绑定新密钥。
使用此方法前,请在账户中心设置中启用 mfa 字段。
步骤 1:生成新 TOTP 密钥
使用 POST /api/my-account/mfa-verifications/totp-secret/generate 生成新密钥,并按 绑定新的 TOTP 展示给用户。
步骤 2:替换 TOTP 因子
curl -X PUT https://[tenant-id].logto.app/api/my-account/mfa-verifications/totp \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"secret":"...","code":"..."}'
secret:第 1 步生成的新 TOTP 密钥。code:用新密钥生成的 TOTP 验证码,用于确认绑定。
管理备份码
请先启用 MFA 和备份码。
使用此方法前,请在账户中心设置中启用 mfa 字段。
步骤 1:生成新备份码
使用 POST /api/my-account/mfa-verifications/backup-codes/generate 生成一组新的 10 个备份码。
curl -X POST https://[tenant-id].logto.app/api/my-account/mfa-verifications/backup-codes/generate \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json'
响应体示例:
{
"codes": ["...", "...", "..."]
}
步骤 2:向用户展示备份码
在绑定备份码到用户账户前,必须展示给用户,并提示:
- 立即下载或抄写这些备份码
- 妥善保管在安全位置
- 每个备份码只能使用一次
- 这些备份码是丢失主 MFA 方法后的最后救援手段
建议以清晰、易复制的格式展示,并提供下载选项(如文本文件或 PDF)。
步骤 3:绑定备份码到用户账户
使用 POST /api/my-account/mfa-verifications 绑定备份码到用户账户。
curl -X POST https://[tenant-id].logto.app/api/my-account/mfa-verifications \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"type":"BackupCode","codes":["...","...","..."]}'
type:必须为BackupCode。codes:上一步生成的备份码数组。
- 每个用户只能有一组备份码。如全部用完,需重新生成并绑定新备份码。
- 备份码不能作为唯一 MFA 因子。用户必须至少启用一种其他 MFA 因子(如 WebAuthn 或 TOTP)。
- 每个备份码只能使用一次。
查看已有备份码
要查看已有备份码及其使用状态,使用 GET /api/my-account/mfa-verifications/backup-codes 接口:
curl https://[tenant-id].logto.app/api/my-account/mfa-verifications/backup-codes \
-H 'authorization: Bearer <access_token>'
响应体示例:
{
"codes": [
{
"code": "...",
"usedAt": null
},
{
"code": "...",
"usedAt": "2024-01-15T10:30:00.000Z"
}
]
}
code:备份码。usedAt:该备份码被使用的时间戳,未使用为null。
管理两步验证设置
使用这些方法前,请在账户中心设置中启用 mfa 字段。
终端用户可为账户开启或关闭两步验证。常见场景是用户已配置 MFA 因子(如 Passkey),但只想用于登录而不希望作为额外验证步骤——关闭两步验证后,登录时可跳过 MFA 提示,同时保留 MFA 因子。该功能由每个用户的 skipMfaOnSignIn 设置控制。
获取当前 MFA 设置
使用 GET /api/my-account/mfa-settings 获取用户当前两步验证设置。
curl https://[tenant-id].logto.app/api/my-account/mfa-settings \
-H 'authorization: Bearer <access_token>'
响应体示例:
{
"skipMfaOnSignIn": false
}
更新 MFA 设置
要开启或关闭两步验证,使用 PATCH /api/my-account/mfa-settings 接口。
curl -X PATCH https://[tenant-id].logto.app/api/my-account/mfa-settings \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json' \
--data-raw '{"skipMfaOnSignIn":true}'
skipMfaOnSignIn:设为true表示为该用户关闭两步验证,false表示开启。
该设置仅在 MFA 策略为可选时生效。如 MFA 策略为强制或自适应,则忽略 skipMfaOnSignIn,始终要求 MFA。
管理用户会话
列出活跃会话
要列出用户的活跃会话,可使用 GET /api/my-account/sessions 接口。
- 访问此接口需
UserScope.Sessions权限 (Scope)。 - 账户中心设置中的
Sessions字段需设为只读或可编辑。
curl https://[tenant-id].logto.app/api/my-account/sessions \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json'
按会话 ID 撤销会话
要撤销指定会话,使用 DELETE /api/my-account/sessions/{sessionId} 接口。
- 访问此接口需
UserScope.Sessions权限 (Scope)。 - 账户中心设置中的
Sessions字段需设为可编辑。
curl -X DELETE https://[tenant-id].logto.app/api/my-account/sessions/{sessionId} \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json'
可选查询参数:
revokeGrantsTarget:可选,指定撤销会话时要撤销的授权 (Authorization) 目标。可选值:all:撤销与该会话关联的所有授权 (Authorization)。firstParty:仅撤销与该会话关联的一方应用授权 (Authorization)。(大多数场景推荐,能撤销自有应用访问权限,同时保留第三方应用授权 (Authorization),提升用户体验。)- 未指定:默认行为为撤销未包含
offline_access权限 (Scope) 的授权 (Authorization),通常意味着撤销该会话的非刷新令牌授权 (Authorization)。
管理用户已授权应用(授权 (Authorization))
当用户需要在账户设置页查看和撤销已授权应用时,可使用用户授权 (Authorization) 应用 API。
- 应用授权 (Authorization) API 与会话 API 权限模型一致。
- 需
UserScope.Sessions权限 (Scope)。 - 账户中心设置中的
Sessions字段需启用:只读或可编辑可列出授权 (Authorization)。可编辑可撤销授权 (Authorization)。
列出活跃应用授权 (Authorization)
要列出当前用户的活跃应用授权 (Authorization),使用 GET /api/my-account/grants 接口。
curl https://[tenant-id].logto.app/api/my-account/grants \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json'
可选查询参数:
appType=firstParty:仅返回一方应用授权 (Authorization)。appType=thirdParty:仅返回三方应用授权 (Authorization)。- 不传
appType:返回所有活跃授权 (Authorization)。
curl "https://[tenant-id].logto.app/api/my-account/grants?appType=thirdParty" \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json'
按授权 (Authorization) ID 撤销应用授权 (Authorization)
要撤销指定应用授权 (Authorization),使用 DELETE /api/my-account/grants/{grantId} 接口。
curl -X DELETE https://[tenant-id].logto.app/api/my-account/grants/{grantId} \
-H 'authorization: Bearer <access_token>' \
-H 'content-type: application/json'
撤销授权 (Authorization) 后,之前颁发的该授权 (Authorization) 的不透明访问令牌 (Opaque token) 和刷新令牌 (Refresh token) 均会失效。