JWT认证下前端篡改本地存储越权编辑帖子的解决方案咨询
解决帖子编辑越权问题的方案
问题背景
我基于React+Node.js开发带JWT认证的帖子编辑功能,登录用户信息存储在localStorage中。目前发现用户可篡改localStorage内的username字段,匹配目标帖子的作者名后就能编辑不属于自己的帖子,存在越权风险,需要优化设计避免这个问题。
现有代码
React 帖子编辑组件代码
import React, { useEffect } from 'react'; import { useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import axios from 'axios'; import { useContext } from 'react'; import { AuthContext } from '../context/authContext'; import config from '../config.json'; const Edit = () => { const { currentUser } = useContext(AuthContext); const navigate = useNavigate(); const { id } = useParams(); const [post, setPost] = useState({ title: '', desc: '', cat: '', }); const handleChange = (e) => { setPost((prev) => ({ ...prev, [e.target.name]: e.target.value })); }; const handleSubmit = async (e) => { e.preventDefault(); try { await axios.put(`${config.SERVER_URL}/api/posts/${id}`, post); navigate('/'); } catch (err) { console.log(err); } }; const getData = async () => { try { const data = await axios.get(`${config.SERVER_URL}/api/posts/${id}`); console.log(data.data[0]); setPost(data.data[0]); } catch (err) { console.log(err); } }; useEffect(() => { getData(); }, []); if (currentUser) { return ( <> {currentUser.username === post.username ? ( <> <div className='auth text-center'> <div className='display-6 m-4'>Update Post</div> <form> <div> <input type='text' className='m-4 p-2 rounded-4' placeholder='Title' value={post.title} name='title' onChange={handleChange} /> </div> <div> <textarea type='text' className='m-4 p-2 rounded-4' placeholder='Description' value={post.desc} name='desc' onChange={handleChange} /> </div> <div> <input type='text' className='m-4 p-2 rounded-4' placeholder='Category' value={post.cat} name='cat' onChange={handleChange} /> </div> <div className='text-center m-4'> <button className='btn btn-primary' onClick={handleSubmit}> Update Post </button> </div> </form> </div> </> ) : ( <> <div className='auth display-6'>You cannot edit this post!</div> </> )} </> ); } else { return ( <> <div className='auth display-6'>Not Logged in!</div> </> ); } }; export default Edit;
Auth Context 代码
import axios from 'axios'; import { createContext, useEffect, useState } from 'react'; import config from '../config.json'; export const AuthContext = createContext(); export const AuthContexProvider = ({ children }) => { const [currentUser, setCurrentUser] = useState( JSON.parse(localStorage.getItem('user')) || null ); const login = async (inputs) => { const res = await axios.post(`${config.SERVER_URL}/api/auth/login`, inputs); setCurrentUser(res.data); }; const logout = async (inputs) => { await axios.post(`${config.SERVER_URL}/api/auth/logout`); setCurrentUser(null); }; useEffect(() => { localStorage.setItem('user', JSON.stringify(currentUser)); }, [currentUser]); return ( <AuthContext.Provider value={{ currentUser, login, logout }}> {children} </AuthContext.Provider> ); };
Node.js 后端更新帖子接口代码
export const updatePost = (req, res) => { const token = req.cookies.access_token; if (!token) return res.status(401).json('Not authenticated!'); jwt.verify(token, 'jwtkey', (err, userInfo) => { if (err) return res.status(403).json('Token is not valid!'); const values = [req.body.title, req.body.desc, req.body.cat]; const id = req.params.id; const q = 'Update posts set `title`=?,`desc`=?,`cat`=? where id = ? ;'; db.query(q, [...values, id], (err, data) => { if (err) return res.status(400).send(err); return res.status(200).send(data); }); }); };
核心优化方案
1. 后端强制校验权限(必须做,前端校验仅为用户体验)
前端的任何校验都可以被篡改,所以权限校验必须在后端完成。修改后端updatePost接口,在更新前先查询该帖子的作者ID,和JWT解析出的当前用户ID对比,只有匹配才允许更新:
export const updatePost = (req, res) => { const token = req.cookies.access_token; if (!token) return res.status(401).json('未认证!'); jwt.verify(token, 'jwtkey', (err, userInfo) => { if (err) return res.status(403).json('Token无效!'); const postId = req.params.id; // 先查询帖子的作者ID const checkOwnerQ = 'SELECT user_id FROM posts WHERE id = ?'; db.query(checkOwnerQ, [postId], (err, postData) => { if (err) return res.status(400).send(err); // 帖子不存在或作者ID不匹配当前用户 if (!postData.length || postData[0].user_id !== userInfo.id) { return res.status(403).json('无权编辑此帖子!'); } // 权限校验通过,执行更新 const values = [req.body.title, req.body.desc, req.body.cat]; const updateQ = 'UPDATE posts SET `title`=?,`desc`=?,`cat`=? WHERE id = ?'; db.query(updateQ, [...values, postId], (err, data) => { if (err) return res.status(400).send(err); return res.status(200).json('帖子更新成功!'); }); }); }); };
注意:你的JWT在签发时,需要把用户的
id(而不只是username)存入payload,比如登录接口签发token时:// 登录接口示例 jwt.sign({id: user.id, username: user.username}, 'jwtkey', {expiresIn: '1d'}, (err, token) => { // 返回token给前端,存入cookie });
2. 前端优化(提升用户体验,减少无效请求)
虽然后端已经做了强制校验,但前端可以优化逻辑,避免用户输入后才发现无权编辑:
- 不要依赖localStorage中的
username做校验,可使用jwt-decode库解析JWT中的用户ID,和帖子返回的作者ID对比(仅为体验优化,不能替代后端校验)。 - 或者在后端的帖子详情接口中,直接返回当前用户是否有权限编辑该帖子的标识,前端根据这个标识展示内容。
修改前端getData函数示例(假设后端返回的帖子数据包含authorId字段):
const getData = async () => { try { const data = await axios.get(`${config.SERVER_URL}/api/posts/${id}`); setPost(data.data[0]); } catch (err) { console.log(err); } };
前端判断时改为对比用户ID:
{currentUser.id === post.authorId ? (...) : (...)}
3. 加强前端用户信息存储安全性
- 避免在localStorage中存储敏感的用户权限信息,必要的用户标识优先存入HttpOnly Cookie(你的后端已经用Cookie存token,这是正确的做法)。
- 不要在前端直接暴露用户的敏感ID或权限字段,只保留必要的展示信息。
关键总结
- 后端权限校验是核心:所有操作必须在后端验证当前用户是否拥有对应权限,前端校验仅用于优化用户体验,不能作为安全屏障。
- JWT payload中要包含唯一且不可篡改的用户标识(比如用户ID),而不是仅用可被篡改的username。
- 永远不要信任前端传来的任何权限相关信息,所有权限判断都要基于后端从数据库或可靠来源获取的数据。
内容的提问来源于stack exchange,提问作者Rohith Badrinath
相关产品推荐
相关产品推荐

