userId存储方案选型:存localStorage还是从JWT解码获取?
前端userId存储:localStorage还是JWT?哪种更靠谱?
一、两种方案的优缺点
1. 直接存在localStorage里
- 优势:实现超简单,一行代码就能获取,就像你现在的写法:
const userId = localStorage.getItem("highfitid")
- 劣势:完全没安全性可言。localStorage是明文存在浏览器中的,用户打开控制台就能随意修改userId的值——比如把
highfitid改成别人的ID,你的接口直接就会把别人的购物车数据返回给我,只要后端没做额外校验,这就是个严重漏洞。另外用户清除浏览器缓存后,userId会丢失,还得重新登录。
2. 存在JWT令牌里,前端解码获取
- 优势:安全性高得多。JWT虽然是base64编码(可解码,不是加密),但后端会给令牌添加签名,前端篡改令牌后签名会直接失效,后端能立刻识别并拒绝请求。而且令牌本身就是身份凭证,payload里可以存放userId、用户名这类非敏感信息,前端解码就能使用,不用额外存储。
- 劣势:比直接存localStorage多几步操作,需要写解码代码或用轻量库。另外令牌有过期时间,过期后得用刷新令牌重新获取新的,稍显麻烦,但换来的安全性完全值得。
二、选哪种?肯定是JWT!
别犹豫,优先用JWT携带userId的方案。核心原因就是不能信任前端传的任何数据——你现在把localStorage里的userId直接传给后端,相当于把数据安全的主动权交给了用户,风险极高。用JWT的话,后端只认自己签发的令牌,从令牌里提取userId,不管前端怎么篡改请求里的userId,都没用。
三、更优的实现方式
标准流程
- 登录成功后,后端返回JWT(通常叫
access_token),你把它存在sessionStorage里(比localStorage更安全,关闭浏览器就自动清除,减少被盗用风险)。 - 每次发送请求时,把令牌放在请求头的
Authorization字段中,格式为Bearer <你的令牌>。 - 前端需要userId时,解码JWT的payload部分即可:
// 简易JWT解码函数 function decodeJwt(token) { const payloadPart = token.split('.')[1]; const decodedStr = decodeURIComponent(atob(payloadPart).split('').map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2) ).join('')); return JSON.parse(decodedStr); } // 获取令牌并解码userId const token = sessionStorage.getItem('access_token'); const userInfo = decodeJwt(token); const userId = userInfo.userId; // 修改后的请求代码 axios.post("http://localhost:6007/getusercart", {}, { headers: { 'Content-Type': "application/json", 'Authorization': `Bearer ${token}` } }).then((data) => { setCartproduct(data.data); });
- 后端处理接口时,先验证JWT签名的合法性,再从令牌中取出userId,最后查询该用户的购物车数据。这样就算前端篡改请求里的userId,后端也会用令牌中的真实userId,不会返回错误数据。
额外优化点
- JWT的payload不要放敏感信息(比如密码),因为base64编码可解码,只存userId、用户名这类非敏感内容。
- 实现刷新令牌机制:当
access_token过期时,用refresh_token向后端换取新的access_token,避免用户频繁登录。 - 可以考虑把令牌存到HttpOnly的cookie中,前端JS无法获取,能避免XSS攻击窃取令牌,但跨域时需要注意配置。
四、对你现有代码的修改建议
把直接传localStorage中userId的逻辑,改成传递JWT令牌,后端从令牌中提取userId,而非信任前端传入的值。按上面的示例修改代码,同时调整后端接口逻辑,先校验令牌合法性再处理业务,彻底解决篡改风险。
内容的提问来源于stack exchange,提问作者webdevShubhamKumar
相关产品推荐
相关产品推荐

