You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

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

火山引擎 最新活动