Vue 3中短信验证码登录需分层管理状态:Pinia统一维护isAuthenticated/token等全局登录态,setup中用ref控制phone/code/countdown等页面级状态,登录成功后同步写入localStorage、清空临时数据并触发全局更新,同时严守后端频次限制与前端二次校验等安全细节。

Vue 3 中登录状态管理与手机短信验证码登录的结合,核心在于:**前端负责交互与状态驱动,后端承担安全验证与凭证签发,状态管理需兼顾响应性、持久性和安全性**。不能把 Token 存在 Vue 的 reactive 数据里就完事,也不能让倒计时或表单状态脱离整体登录流程。
一、登录状态应由统一 store 管理(推荐 Pinia)
使用 Pinia 创建 auth store,集中管理登录态关键字段:
- isAuthenticated:布尔值,反映当前是否已通过验证
- token:JWT 字符串,仅在登录成功后写入
- userInfo:可选,缓存基础用户信息(如手机号、昵称)
-
loginMethod:标记本次登录方式(
'sms'/'wechat'/'password'),便于后续行为区分
关键点:token 必须同步写入 localStorage 或 sessionStorage(根据有效期选择),并在应用启动时尝试恢复登录态;但不要仅依赖前端存储——每次请求仍需携带 token 并由后端校验有效性。
二、短信验证码流程中状态要分层响应
不是所有状态都该进 store。建议分三级:
立即学习“前端免费学习笔记(深入)”;
Vue 3 组合式 API 与 最佳实践,涵盖代码组织、useXxx 模式、组合式函数设计、Store 集成、响应式优化及功能抽取。
-
全局状态(Pinia):如
isAuthenticated、token -
页面级状态(setup 中 ref):如
phone、code、countdown、isCountingDown -
表单校验状态(Element Plus 或 VeeValidate):如
rules、validateField结果
例如点击“获取验证码”时:
→ 先用正则校验手机号格式(/^1[3-9]\d{9}$/)
→ 校验通过再调用 sendSmsCode(phone.value) 接口
→ 成功后启动倒计时,并禁用按钮(isCountingDown = true)
→ 失败则提示错误(如“发送频繁,请稍后再试”),不修改倒计时状态
三、登录成功后必须完成三件事
调用 loginBySms(phone, code) 接口返回成功响应后,不能只跳转首页:
- 写入 token 到 Pinia store 和 localStorage:确保路由守卫、API 请求拦截器能立即读取
-
清除敏感临时状态:如清空
phone、code、重置倒计时变量 - 触发全局登录事件(可选):比如通知侧边栏更新用户头像、刷新未读消息数
示例逻辑片段(Pinia action 内):
const loginWithSms = async (phone: string, code: string) => {const res = await api.loginBySms({ phone, code });
if (res.data.token) {
token.value = res.data.token;
isAuthenticated.value = true;
localStorage.setItem('auth_token', res.data.token);
resetForm(); // 清空输入框和倒计时
}
};
四、安全细节不能绕过
短信登录看似简单,但几个关键点常被忽略:
- 验证码提交前必须二次校验手机号格式:防止用户绕过前端直接发请求
- 后端必须限制单手机号单位时间内的发送次数(如 60 秒/1 条)和验证次数(如 5 次/15 分钟)
-
前端倒计时必须基于服务端时间戳校准:避免用户篡改本地时间导致逻辑失效;可在发送验证码成功后,从响应头或数据中获取过期时间(如
expires_at: "2026-09-25T09:12:00Z"),再计算剩余秒数 - Token 必须带 HttpOnly 属性(若走 Cookie)或严格校验 Authorization Header(若走 Bearer):Electron 环境下尤其注意主进程协助安全存储

















