如何阻止用户修改URL编辑过往答题内容 求适配Vue3+TS的解决方案
答题场景防止用户通过修改URL参数跳转过往题目的解决方案
首先明确前提:浏览器没有开放禁止用户修改地址栏的API,完全从前端限制用户操作URL无法实现,正确的思路是从「即使用户改了URL,也无法访问未授权题目」的角度做校验。
必做方案:后端校验(根源解决问题)
- 为每个正在答题的用户生成唯一的答题会话ID,存储在服务端(Session/Redis均可),实时记录用户当前已答到的最新题号
- 前端请求题目内容时,除了题目ID参数,额外携带答题会话ID,后端先校验会话对应的当前进度:如果请求的题目ID小于当前已到进度,直接拒绝返回题目内容,返回错误提示「已答题目不允许修改」
- 该方案完全不受前端操作影响,就算用户篡改URL、抓包改请求参数也无法绕过。
适配Vue3+TS的前端配套方案(优化体验)
1. 路由守卫全局拦截
配合Vue Router 4做路由前置校验,用户跳转到未授权题目时自动切回当前可答题:
// router/index.ts import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' import { useAnswerStore } from '@/store/answer' // 基于Pinia的答题状态管理 const routes: RouteRecordRaw[] = [ { path: '/question', name: 'Question', component: () => import('@/views/Question.vue') } ] const router = createRouter({ history: createWebHistory(), routes }) router.beforeEach(async (to, from, next) => { if (to.name === 'Question') { const answerStore = useAnswerStore() // 从后端拉取当前用户最新的可答题ID await answerStore.fetchCurrentAllowedQid() const reqQid = Number(to.query.id) const allowedQid = answerStore.currentAllowedQid // 禁止跳转到已答/未解锁的题目 if (reqQid < allowedQid || reqQid > allowedQid) { return next({ path: '/question', query: { id: allowedQid } }) } } next() }) export default router
2. 题目ID移除出URL(可选,彻底避免用户改参数)
如果没有题目分享需求,可以直接把当前题目ID存在Pinia全局状态里,URL固定为/question不携带ID参数,用户看不到自然无法修改:
- 切换题目时只更新全局状态的题目ID,请求后端拉取对应题目内容,URL全程无变化
- 就算用户刷新页面,后端直接返回当前会话对应的最新题目ID,前端渲染即可,无需依赖URL参数
3. URL变化监听(临时补充方案)
如果暂时无法调整后端逻辑,可以临时用前端监听逻辑做拦截(仅能防普通用户,必须配合后端校验才安全):
// 答题页面setup逻辑 import { onMounted, onUnmounted } from 'vue' import { useRouter } from 'vue-router' import { useAnswerStore } from '@/store/answer' const router = useRouter() const answerStore = useAnswerStore() // 记录当前允许的最大题号 const allowedQid = answerStore.currentAllowedQid const checkUrlValid = () => { const currentQid = Number(new URLSearchParams(window.location.search).get('id')) if (currentQid < allowedQid) { // 强制切回当前可答题 router.replace({ query: { id: allowedQid } }) alert('已完成的题目不允许修改') } } onMounted(() => { // 监听浏览器前进后退操作 window.addEventListener('popstate', checkUrlValid) // 监听路由切换 const unWatchRoute = router.afterEach(checkUrlValid) onUnmounted(() => { window.removeEventListener('popstate', checkUrlValid) unWatchRoute() }) })
重要提示:所有前端拦截逻辑都只能做体验优化,无法作为安全校验,懂技术的用户可以通过禁用JS、抓包改请求等方式绕过,必须搭配后端校验使用。
内容的提问来源于stack exchange,提问作者Cena
相关产品推荐
相关产品推荐

