Vue 3 + Laravel 9 Sanctum环境下,后端删除Token后前端安全移除Token并处理用户登出重定向的实现方案
Vue 3 + Laravel 9 Sanctum环境下,后端删除Token后前端安全移除Token并处理用户登出重定向的实现方案
我完全理解你的顾虑——既要保证单浏览器单会话的安全性,又要在后端主动失效Token后,前端能优雅且安全地处理用户登出,毕竟涉及用户数据安全的环节容不得半点马虎。结合你用的Vue3 + Laravel9 Sanctum技术栈,我给你梳理几个经过实践验证的安全实现步骤:
一、全局拦截API请求,捕获未授权状态
所有和后端交互的API请求都会经过Axios,我们可以在这里统一拦截401「Unauthenticated」响应,触发登出逻辑:
// 在你的Axios配置文件(比如src/utils/axios.js)中添加响应拦截器 import axios from 'axios' import router from '@/router' import { useUserStore } from '@/stores/user' // 假设用Pinia管理用户状态 const api = axios.create({ baseURL: import.meta.env.VITE_API_URL, withCredentials: true // 必须开启,配合Sanctum的CSRF验证 }) // 响应拦截器 api.interceptors.response.use( response => response, error => { // 精准捕获Sanctum返回的未授权状态 if (error.response?.status === 401 && error.response.data.message === 'Unauthenticated.') { // 触发登出清理逻辑 handleLogout() } return Promise.reject(error) } ) // 封装登出处理函数 function handleLogout() { // 1. 移除localStorage中的Token localStorage.removeItem('sanctum_token') // 键名和你存储Token时保持一致 // 2. 清空状态管理中的用户数据 const userStore = useUserStore() userStore.clearUserInfo() // 3. 重定向到登录页,同时清空路由历史防止后退漏洞 router.replace({ name: 'Login' }).then(() => { // 可选:强制刷新页面,确保所有状态彻底重置 window.location.reload() }) } export default api
二、路由守卫同步校验Token有效性
除了API拦截,还要在路由层面做兜底校验,防止用户直接通过URL访问授权页面:
// 在src/router/index.js中配置全局前置守卫 import { createRouter, createWebHistory } from 'vue-router' import routes from './routes' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes }) router.beforeEach((to, from, next) => { const hasToken = localStorage.getItem('sanctum_token') !== null const requiresAuth = to.matched.some(record => record.meta.requiresAuth) // 需要授权但无Token,重定向登录 if (requiresAuth && !hasToken) { next({ name: 'Login' }) } // 已登录却访问登录/注册页,重定向首页 else if ((to.name === 'Login' || to.name === 'Register') && hasToken) { next({ name: 'Home' }) } else { next() } }) export default router
记得给需要授权的路由添加元信息标记:
{ path: '/dashboard', name: 'Dashboard', component: () => import('../views/Dashboard.vue'), meta: { requiresAuth: true } }
三、安全注意事项(必看)
- 彻底清理状态:不要只删localStorage的Token,一定要同步清空状态管理中的用户信息(比如用户对象、权限列表),避免页面残留敏感数据;
- 精准匹配未授权:前端判断401时,建议同时校验响应的
message字段是否为「Unauthenticated.」,防止其他类型的401错误(比如路由不存在)误触发登出; - 阻断后退路径:用
router.replace()替代router.push()跳转登录页,或者刷新页面,清空浏览器历史记录,避免用户通过后退按钮回到授权页面; - 友好提示用户:可以在
handleLogout中加入全局提示(比如Element Plus的ElMessage.error('会话已过期,请重新登录')),让用户清楚登出原因。
四、后端配合(补充完善)
你提到的登录时删除所有旧Token的逻辑,可以在Laravel登录接口中这样实现,确保彻底失效旧会话:
// 在LoginController的login方法中 public function login(Request $request) { $credentials = $request->validate([ 'email' => 'required|email', 'password' => 'required' ]); if (Auth::attempt($credentials)) { // 删除当前用户所有旧Token Auth::user()->tokens()->delete(); // 生成新的会话Token $token = Auth::user()->createToken('sanctum_session_token')->plainTextToken; return response()->json([ 'token' => $token, 'user' => Auth::user() ]); } return response()->json(['message' => '登录失败'], 401); }
这样一套流程下来,既能保证后端删除Token后前端能及时响应,又能最大程度避免安全漏洞,同时兼顾用户体验。
备注:内容来源于stack exchange,提问作者Mateus




