深度解析pg2mysql:PostgreSQL到MySQL数据迁移的架构设计与实战
2026/6/14 1:50:49
前端保存用户登录信息的核心目标是持久化登录状态(减少重复登录)、提升用户体验,同时必须兼顾安全性(防止信息泄露、伪造、劫持)。本文从存储方案选型、安全防护、最佳实践、常见问题等维度,全面解析前端登录信息的存储与管理。
前端无需存储用户明文密码(绝对禁止),只需存储服务端颁发的身份凭证:
sessionId(服务端会话标识,关联用户信息);Token(如JWT,包含加密用户信息,服务端可验签)。| 需求维度 | 具体要求 |
|---|---|
| 持久性 | 支持“记住我”(长期登录)/会话级登录 |
| 安全性 | 防XSS、CSRF、信息泄露 |
| 可用性 | 跨页面/标签页共享登录状态 |
| 兼容性 | 兼容主流浏览器 |
前端存储登录信息的核心方案有4类,各有适用场景,需结合安全要求选择:
| 存储方案 | 存储位置 | 生命周期 | 容量 | 随HTTP请求传输 | 核心特性 | 安全等级 |
|---|---|---|---|---|---|---|
| Cookie(推荐) | 浏览器+服务端 | 可配置(会话/长期) | ~4KB | 是(同域) | 支持HttpOnly/Secure/SameSite配置 | 高 |
| LocalStorage | 浏览器本地 | 持久化(手动清除) | ~5MB | 否 | 易受XSS攻击,跨标签页共享 | 中 |
| SessionStorage | 浏览器本地 | 会话级(标签页关闭清除) | ~5MB | 否 | 仅当前标签页可用,无跨页共享 | 中 |
| IndexedDB | 浏览器本地 | 持久化(手动清除) | 无上限 | 否 | 大容量、异步,适合多账号存储 | 中 |
Cookie是浏览器存储的小型文本片段,核心优势是支持服务端控制和安全配置项,是存储登录凭证(sessionId/Token)的最优选择。
| 配置项 | 作用 |
|---|---|
HttpOnly | 禁止JS访问Cookie(防XSS攻击),仅浏览器与服务端通信时携带 |
Secure | 仅在HTTPS协议下传输Cookie(防止明文传输被劫持) |
SameSite | 限制Cookie跨域发送(防CSRF): - Strict:仅同站请求携带- Lax:宽松同站(推荐)- None:跨域需配合Secure |
Expires/Max-Age | 生命周期: - Expires:绝对时间(如2025-12-31 23:59:59)- Max-Age:相对秒数(如60*60*24*7=7天)- 不配置:会话级(关闭浏览器清除) |
Domain | 限定Cookie生效的域名(如.example.com,子域名共享) |
Path | 限定Cookie生效的路径(如/api,仅该路径请求携带) |
Set-Cookie响应头配置(自动带安全项):# 服务端响应头(示例:Node.js/Express) Set-Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9; HttpOnly; Secure; SameSite=Lax; Max-Age=604800; Domain=.example.com; Path=/// 读取所有Cookie(仅非HttpOnly)console.log(document.cookie);// "token=xxx; username=xxx"// 设置Cookie(无HttpOnly,不推荐存敏感凭证)document.cookie="username=zhangsan; Max-Age=604800; Path=/";// 删除Cookie(设置Max-Age=0)document.cookie="token=; Max-Age=0; Path=/; Domain=.example.com";sessionId/Token):必须配置HttpOnly + Secure + SameSite;Expires/Max-Age,关闭浏览器自动清除;Max-Age为7天/30天(根据业务需求)。LocalStorage是浏览器本地键值对存储,不随HTTP请求传输,但易受XSS攻击(JS可直接读取),仅适合存储非敏感登录信息。
// 存储登录信息(非敏感)localStorage.setItem("userInfo",JSON.stringify({nickname:"张三",avatar:"https://example.com/avatar.png",loginTime:newDate().getTime()}));// 读取登录信息constuserInfo=JSON.parse(localStorage.getItem("userInfo")||"{}");// 删除登录信息localStorage.removeItem("userInfo");// 清空所有LocalStoragelocalStorage.clear();SessionStorage仅在当前标签页生效,关闭标签页后自动清除,适合临时登录状态(如多标签页隔离登录)。
// 存储临时登录凭证(仅当前标签页)sessionStorage.setItem("tempToken","xxx");// 读取consttempToken=sessionStorage.getItem("tempToken");IndexedDB是浏览器端NoSQL数据库,支持大容量、异步操作,适合存储多账号登录信息、登录历史等复杂数据。
localForage)// 安装:npm i localforageimportlocalforagefrom"localforage";// 初始化constloginDB=localforage.createInstance({name:"loginDB",storeName:"userAccounts"});// 存储多账号信息awaitloginDB.setItem("account_13800138000",{phone:"13800138000",token:"xxx",// 需加密!lastLoginTime:newDate().getTime()});// 读取账号信息constaccount=awaitloginDB.getItem("account_13800138000");// 删除账号awaitloginDB.removeItem("account_13800138000");JWT(JSON Web Token)是主流的无状态登录凭证,前端存储JWT需遵循“安全优先”原则:
| 存储位置 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| HttpOnly Cookie | 防XSS、支持SameSite | 跨域需配置CORS(withCredentials) | 同域/宽松跨域场景 |
| LocalStorage(加密) | 跨域方便 | 易受XSS攻击 | 非核心系统(不推荐) |
为避免Token过期导致用户登出,采用“双Token”策略:
accessToken:短期有效(15分钟),用于接口鉴权,存在HttpOnly Cookie;refreshToken:长期有效(7天),用于刷新accessToken,存在HttpOnly Cookie(独立配置)。// 1. 登录成功,服务端返回双Token(Set-Cookie设置)// 2. 前端请求接口时,浏览器自动携带accessToken// 3. 接口返回401(token过期),前端调用刷新Token接口asyncfunctionrefreshToken(){try{constres=awaitfetch("/api/refresh-token",{method:"POST",credentials:"include"// 携带Cookie(refreshToken)});constdata=awaitres.json();if(data.code===200){// 服务端重新设置accessToken Cookie,继续请求原接口returntrue;}else{// refreshToken过期,跳转登录页window.location.href="/login";returnfalse;}}catch(err){window.location.href="/login";returnfalse;}}// 4. 请求拦截器(Axios示例)axios.interceptors.response.use((res)=>res,async(err)=>{constoriginalRequest=err.config;// 避免重复刷新Tokenif(err.response.status===401&&!originalRequest._retry){originalRequest._retry=true;constsuccess=awaitrefreshToken();if(success){returnaxios(originalRequest);}}returnPromise.reject(err);});前端登录信息泄露/伪造是高频攻击点,需从存储、传输、代码三层防护:
XSS攻击可注入恶意JS窃取LocalStorage/Cookie(非HttpOnly),防护措施:
HttpOnly Cookie;encodeHTML);<metahttp-equiv="Content-Security-Policy"content="default-src'self'; script-src'self'https://cdn.example.com;">eval、innerHTML(改用textContent);CSRF攻击利用用户登录状态伪造请求,防护措施:
SameSite=Lax/Strict;Origin/Referer是否为可信域名;X-Requested-With: XMLHttpRequest)。登录页 → 输入账号密码 → 前端哈希加密密码 → 提交登录请求(HTTPS/POST)→ 服务端验证 → 生成sessionId/双Token → 服务端Set-Cookie(HttpOnly+Secure+SameSite)→ 前端存储非敏感信息到LocalStorage → 跳转首页 → 接口请求自动携带Cookie → Token过期自动刷新 → 登出:前端清除存储 + 服务端失效凭证// utils/auth.js(登录信息管理工具类)classAuth{// 存储非敏感用户信息staticsetUserInfo(info){localStorage.setItem("userInfo",JSON.stringify(info));}// 获取非敏感用户信息staticgetUserInfo(){try{returnJSON.parse(localStorage.getItem("userInfo")||"{}");}catch(err){return{};}}// 登出(清除所有登录信息)staticlogout(){// 清除LocalStoragelocalStorage.removeItem("userInfo");// 清除非HttpOnly Cookie(如昵称)document.cookie="nickname=; Max-Age=0; Path=/";// 调用服务端登出接口(失效凭证)fetch("/api/logout",{method:"POST",credentials:"include"});// 跳转登录页window.location.href="/login";}// 判断是否登录(前端校验,最终以服务端为准)staticisLogin(){constuserInfo=this.getUserInfo();return!!userInfo.token||!!userInfo.userId;}}exportdefaultAuth;withCredentials: true(携带Cookie);.example.com,子域www.example.com);Secure;SameSite=None+Secure,并配置CORS允许凭证。Domain=.example.com(子域共享);Access-Control-Allow-Credentials: true+Access-Control-Allow-Origin: 具体域名)。Expires/Max-Age、浏览器自动清理Cookie;Max-Age(如30天);前端保存登录信息的核心是**“安全优先,体验为辅”**:
HttpOnly + Secure + SameSite的Cookie中,禁止存LocalStorage;最终,前端登录信息的存储需结合服务端策略(如会话管理、Token验签),仅前端防护不足以保证安全,需前后端协同。