微服务的编程测评系统6-管理员登录前端-前端路由优化

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • [1. 管理员登录前端](#1. 管理员登录前端)
    • [1.1 测试](#1.1 测试)
    • [1.2 同源策略](#1.2 同源策略)
    • [1.3 修改前端端口号](#1.3 修改前端端口号)
    • [1.4 跨域问题](#1.4 跨域问题)
    • [1.5 接收响应数据](#1.5 接收响应数据)
    • [1.6 js-cookie](#1.6 js-cookie)
    • [1.7 错误消息提示](#1.7 错误消息提示)
    • [1.8 优化](#1.8 优化)
    • [1.9 响应拦截器](#1.9 响应拦截器)
    • [1.10 @用法](#1.10 @用法)
  • [2. 后台管理-布局](#2. 后台管理-布局)
    • [2.1 点击跳转不同页面](#2.1 点击跳转不同页面)
  • [3. 获取当前用户信息](#3. 获取当前用户信息)
    • [3.1 数据库修改](#3.1 数据库修改)
    • [3.2 设计](#3.2 设计)
    • [3.3 开发后端](#3.3 开发后端)
    • [3.4 开发前端](#3.4 开发前端)
  • [4. 退出登录](#4. 退出登录)
    • [4.1 业务分析](#4.1 业务分析)
    • [4.2 后端开发](#4.2 后端开发)
    • [4.3 前端开发](#4.3 前端开发)
  • [5. 前端路由优化](#5. 前端路由优化)
    • [5.1 重定向](#5.1 重定向)
    • [5.2 全局前置守卫](#5.2 全局前置守卫)
    • [5.3 token过期处理](#5.3 token过期处理)
  • 总结

前言

1. 管理员登录前端

1.1 测试

测试一下发现报了这个错

这个主要是因为我们配置的前置url没有含有http协议,所以浏览器就会自动加上静态资源的url

java 复制代码
const service = axios.create({
    baseURL: "http://127.0.0.1:19090/system",
    timeout: 1000,
})

这样就Ok了

但是又出了一个新的问题

这个就是跨域问题

1.2 同源策略

1.3 修改前端端口号

在vite.config.js里面添加

java 复制代码
  server: {
    port: 5555,
  }

这样就可以了

1.4 跨域问题

我们可以用一个代理服务器来处理

浏览器前端先请求同源的代理服务器,然后代理服务器把请求转发到后端

因为浏览器有同源策略的约束

但是代理服务器是没有同源策略的约束的

java 复制代码
  server: {
    proxy: {
      "/dev-api": {
      target: "http://127.0.0.1:19090/system",
      rewrite: (p) => p.replace(/^\/dev-api/, ""),
    },
  },
 },

还是在vite.config.js里面,这样设置就可以了

这个就是对代理规则的配置

然后修改request.js里面的前置url

java 复制代码
const service = axios.create({
    baseURL: "/dev-api",
    timeout: 1000,
})

如果没有加协议的话,浏览器会把前端的url拼接到baseURL上

所以请求地址为

http://localhost:5173/dev-api/sysUser/login

这个不会发生跨域问题

这个是同源的

会报404,找不到吗

不会

因为配置了代理规则

代理规则就是前缀包含/dev-api的时候,就会把请求转发到http://127.0.0.1:19090/system

java 复制代码
  rewrite: (p) => p.replace(/^\/dev-api/, ""),

这个就是把/dev-api变为空的字符串

所以http://localhost:5173/dev-api/sysUser/login就会变为

http://127.0.0.1:19090/system/sysUser/login

先把http://localhost:5173替换为http://127.0.0.1:19090/system,然后去掉/dev-api,就OK了

补充一下,axios可以自动转换JSON数据,所以那样写没有问题

测试一下也是没有问题的

1.5 接收响应数据

但是axios这个调用接口的过程是一个异步的过程

往往不那么好拿到返回结果

所以要用await去获取异步操作结果

但是对应的调用他的函数也要为async

java 复制代码
async function  loginFun() {
  const res = await loginService(userAccount.value, password.value);
  if(res.data.code === 1000){
    console.log("登录成功:" , res.data);
  }else{
    console.log(res.data.msg)
  }
}

async 表示这个函数要使用await,await表示调用这个方法接口的时候用异步的方式

console.log("登录成功:" , res.data);这里,如果是 console.log("登录成功:" +res.data);

那么res.data打印出的内容是object

如果要打印出类的详细数据的话,还是得用逗号隔开

登录成功要跳转页面

我们用router的push方法就可以了

java 复制代码
    {
      path: '/oj/system',
      name: 'system',
      component: () => import('../views/System.vue')
    }

记得还要配置路由

java 复制代码
import router from '@/router'
router.push("/oj/system")

这样就可以跳转了

登录成功以后要存储token

怎么存储呢

存储方式有很多种

比如cookie和local-storige

我们这里使用cookie存储

js-cookie就是来操作cookie的

java 复制代码
npm install js-cookie

在utils下创建cookie.js

java 复制代码
import Cookies from "js-cookie";
const TokenKey = "Admin-Oj-b-Token";
export function getToken() {
 return Cookies.get(TokenKey);
}
export function setToken(token) {
 return Cookies.set(TokenKey, token);
}
export function removeToken() {
 return Cookies.remove(TokenKey);
}

这样就可以了

java 复制代码
import { setToken } from '@/utils/cookie'
setToken(res.data.data)

在appication.cookies那里就可以看到我们设置的cookie了

1.7 错误消息提示

java 复制代码
import { ElMessage } from 'element-plus'
ElMessage.error(res.data.msg)

1.8 优化

java 复制代码
          <el-input v-model="password"  type="password" show-password placeholder="请输入密码" />

给按钮加上 type="password"

就可以把密码隐藏起来了

show-password就是显示是不是显示小眼睛

1.9 响应拦截器

我们在request.js里面设置

java 复制代码
service.interceptors.response.use(
    (res) => {
        // 未设置状态码则默认成功状态
        const code = res.data.code;
        const msg = res.data.msg;
        if (code !== 1000) {
            ElMessage.error(msg);
            return Promise.reject(new Error(msg));
        } else {
            return Promise.resolve(res.data);
        }
    },
    (error) => {
        return Promise.reject(error);
    }
);

为什么要用响应拦截器呢

因为我们可以用响应拦截器对响应进行拦截,,可以直接返回后端返回的result数据

现在我们就在登录成功和失败的情况下分别测试一下

java 复制代码
  const res = await loginService(userAccount.value, password.value);
  console.log("登录成功:" , res.data);


我们可以看出登录成功返回的数据就是后端返回的result,没有进行分装了

这个就是Promise.resolve(res.data)的作用

而Promise.reject(new Error(msg))就是相当于返回一个异常了

直接报错了

java 复制代码
    (error) => {
        return Promise.reject(error);
    }

这里是属于错误返回,其他的都是正常返回

我们控制台肯定不能这样打印的,因为这样打印就是相当于出错了

所以我们还要捕获异常

就和后端捕获异常是一样的写法

java 复制代码
async function  loginFun() {
  try{
      const res = await loginService(userAccount.value, password.value);
      setToken(res.data.data)
      router.push("/oj/system")
      console.log("登录成功:" , res.data);
  }catch(err){
      console.log("登录失败:" , err);
  }
}

这样就可以了

1.10 @用法

java 复制代码
import { setToken } from '@/utils/cookie'

这里的@是什么意思呢

java 复制代码
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    },
  },

其实在vite.config.js里面就配置过了

@就是./src,所以很方便使用

相应的router这里的两点我们也可以改为@

2. 后台管理-布局

创建一个布局的文件

Layout.vue

java 复制代码
<template>
  <el-container class="layout-container">
    <el-header class="el-header">
      <el-dropdown>
        <span class="el-dropdown__box">
          <div>
            <strong>当前用户:</strong>超级管理员
          </div>
          <el-icon>
            <ArrowDownBold />
          </el-icon>
          <!-- <el-icon><Lock /></el-icon> -->
        </span>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item @click="logout" :icon="SwitchButton">退出登录</el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
    </el-header>
    <el-main class="layout-bottom-box">
      <div class="left">
        <el-aside width="200px" class="el-aside">
          <el-menu class="el-menu" router>
            <el-menu-item index="/oj/layout/cuser">
              <el-icon>
                <Management />
              </el-icon>
              <span>用户管理</span>
            </el-menu-item>
            <el-menu-item index="/oj/layout/question">
              <el-icon>
                <Management />
              </el-icon>
              <span>题目管理</span>
            </el-menu-item>
            <el-menu-item index="/oj/layout/exam">
              <el-icon>
                <Management />
              </el-icon>
              <span>竞赛管理</span>
            </el-menu-item>
          </el-menu>
        </el-aside>
      </div>
      <div class="right">
        <RouterView />
      </div>
    </el-main>

  </el-container>
</template>

<script setup>
import {
  Management,
  ArrowDownBold,
  Lock,
  SwitchButton
} from '@element-plus/icons-vue'
</script>

<style lang="scss" scoped>
.layout-container {
  height: 100vh;
  background: #f7f7f7;

  .layout-bottom-box {
    display: flex;
    justify-content: space-between;
    height: calc(100vh - 100px);
    overflow: hidden;

    .left {
      margin-right: 20px;
      background: #fff;
      display: flex;

      :deep(.el-menu) {
        flex: 1;

        .el-menu-item.is-active {
          color: #32c5ff;
        }

        .el-menu-item:hover {
          background: #fff;
          color: #32c5ff;
        }
      }
    }

    .right {
      flex: 1;
      overflow-y: auto;
      background: #fff;
      padding: 20px;
    }
  }

  .el-aside {
    background-color: #fff;

    &__logo {
      height: 120px;
      // background: url('@/assets/logo.png') no-repeat center / 120px auto;
    }

    .el-menu {
      border-right: none;
    }
  }

  .el-header {
    background-color: #fff;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    height: 40px;

    .el-dropdown__box {
      display: flex;
      align-items: center;

      .el-icon {
        color: #4c4141;
        margin-left: 20px;
      }

      &:active,
      &:focus {
        outline: none;
      }
    }
  }

  .el-footer {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    color: #666;
  }
}
</style>

然后配置路由

java 复制代码
    {
      path: '/oj/layout',
      name: 'layout',
      component: () => import('@/views/Layout.vue')
    }

这样就可以了

现在分析一下

分成了三个部分

我们用的是是这个container布局容器

java 复制代码
  <el-container class="layout-container">
  
    <el-header class="el-header">

    </el-header>
    
    <el-main class="layout-bottom-box">
    
      <div class="left">
        <el-aside width="200px" class="el-aside">
         
        </el-aside>
      </div>
      
      <div class="right">
        <RouterView />
      </div>
      
    </el-main>

  </el-container>

这个就是布局

整体结构

dropdown是一个下拉菜单


ArrowDownBold是图标

java 复制代码
import {
  Management,
  ArrowDownBold,
  Lock,
  SwitchButton
} from '@element-plus/icons-vue'

但是使用图标要在js里面import

SwitchButton也是图标

el-aside我们用的是el-menu来写的

2.1 点击跳转不同页面

java 复制代码
      <div class="right">
        <RouterView />
      </div>

这里就是根据不同url,渲染不同页面,就是主要的页面内容

先创建对应的vue文件

然后是配置router

怎么实现点击切换不url呢

官网的menu组件有一个router属性

这样就可以实现点击切换url了

首先先加上属性router

因为默认为false,不启用,加上就启动了

java 复制代码
          <el-menu class="el-menu" router>

在el-menu这里加上router属性,表示启动router

然后还不行,因为点击要跳转到哪里呢,

这下就要设置index了

所以index设置为路径就可以了

java 复制代码
            <el-menu-item index="/oj/question">

这样设置就可以了

但是点击跳转直接跳转到新的页面了,而不是在那个主页面展示

为什么会这样呢

因为我们配置的路径是/oj/question,与/oj/layout是同一级的

这个路由发生改变之后

会触发routerview,但是这个触发的routerview是app.vue那里的

java 复制代码
    {
      path: '/oj/layout',
      name: 'layout',
      component: () => import('@/views/Layout.vue')
    },
    {
      path: '/oj/cuser',
      name: 'cuser',
      component: () => import('@/views/Cuser.vue')
    },

这里的配置路径就是同一级的,所以改变路径的时候就是触发的同一个routerview,因为这两个的路径的配置是类似的,所以layout能触发那个routerview,为什么cuser不行呢

所以cuser也是用的app.vue的routerview

所以我们需要把cuser的路由配置到layout里面去

因为cuser是在layout内部进行的页面渲染

因为渲染的顺序就是先渲染layout,然后是点击在layout里面渲染cuser

所以路径的配置,就必须在layout里面进行配置

所以cuser的路由就是layout下的路由

路由提供了一个child的属性就可以配置了,这个是数组

java 复制代码
    {
      path: '/oj/layout',
      name: 'layout',
      component: () => import('@/views/Layout.vue'),
      children: [
        {
          path: '/cuser',
          name: 'cuser',
          component: () => import('@/views/Cuser.vue')
        },
        {
          path: '/exam',
          name: 'exam',
          component: () => import('@/views/Exam.vue')
        },
        {
          path: '/question',
          name: 'question',
          component: () => import('@/views/Question.vue')
        },
      ]
    },

这样就可以了

其中cuser的路径就是/oj/layout/cuser

会自动加上父组件的路径的

java 复制代码
      router.push("/oj/layout")

然后登录成功的跳转也要改了

java 复制代码
 <el-menu-item index="/oj/layout/cuser">

然后这里也要改

但是还是不行

java 复制代码
      <div class="right">
        <RouterView />
      </div>

这里的routerview是二级目录

这里跳转显示的是二级路由,而app.vue里面显示跳转的是一级路由

这里的情况是在一级小的页面里面有二级页面,所以对应也要在app.vuede的routerview里面在嵌套一个routerview

因为页面有嵌套关系

所以routerview也要有嵌套关系,路由配置也要有嵌套关系

java 复制代码
    {
      path: '/oj/layout',
      name: 'layout',
      component: () => import('@/views/Layout.vue'),
      children: [
        {
          path: 'cuser',
          name: 'cuser',
          component: () => import('@/views/Cuser.vue')
        },
        {
          path: 'exam',
          name: 'exam',
          component: () => import('@/views/Exam.vue')
        },
        {
          path: 'question',
          name: 'question',
          component: () => import('@/views/Question.vue')
        },
      ]
    },

注意这里的cuser的路径前面就不要加上/了,因为这样可能表示是以/cser开头的,是绝对路径,如果是二级路径,就最前面不要加/了

子路由 path 不加 /:路径会自动拼接父路由路径,保持嵌套关系(正确用法)。

子路由 path 加 /:路径被视为绝对路径,脱离父路由,成为独立的一级路由(不符合二级路由的设计意图)。

3. 获取当前用户信息

3.1 数据库修改

给数据库添加用户昵称字段

java 复制代码
    nick_name varchar(20) not null comment '昵称',

要么重新创建数据库

要么用alter

java 复制代码
alter table  tb_sys_user add nick_name varchar(20)  null  after user_account ;
java 复制代码
update tb_sys_user set nick_name = '超级管理员' where user_account = 'aaa'
java 复制代码
[HY000][1366] Incorrect string value: '\xE8\xB6\x85\xE7\xBA\xA7...' for column 'nick_name' at row 1

在update的时候报错了,这个是因为不支持中文的原因

就是编码出问题

改一下配置文件就可以了

找到etc/my.cnf

加上配置

java 复制代码
character-set-server=utf8mb4
collation-server = utf8mb4_general_ci

保存一下,然后重启容器生效

但是就算这样修改了执行还是不行,因为这个表提前就创建好了,编码已经确定了

所以不行

所以我们要重新创建一个表

但是就算创建一个新的表还是有编码问题

怎么回事呢

因为数据库的编码没有变

得创建一个新的库才可以

我们先用root用户创建新的库

所以说改了配置文件以后,删除以前的库才可以,或者创建新的库才可以生效

右键表的数据,然后生成sql,生成insertSQL就可以保存数据了

这样就成功了

3.2 设计

3.3 开发后端

java 复制代码
    @GetMapping("/info")
    public R<LoginUserVO> info(@RequestHeader(HttpConstants.AUTHENTICATIO) String token){
        return sysUserService.info(token);
    }

我们的token直接从header里面获取就可以了,用的是RequestHeader注解

java 复制代码
@Data
public class LoginUserVO {
    private String nickName;
}
java 复制代码
@Data
public class LoginUser {
    //存储在redis中的用户信息
    private Integer identity;
    private String nickName;
}

这里也要完善一下,存储到redis中的基本数据,还有数据库对应的类也要增加字段,记得还要修改对应的代码,登录存储基本用户数据的时候记得修改代码,存储昵称

tokenService里面分装方法

java 复制代码
    public LoginUser getLoginUser(String token, String secret   ) {
        String userKey = getUserKey(token, secret);
        if(userKey == null){
            return null;
        }
        String tokenKey = getTokenKey(userKey);
        return redisService.getCacheObject(tokenKey, LoginUser.class);
    }

    private String getTokenKey(String userKey) {
        return CacheConstants.LOGIN_TOKEN_KEY + userKey;
    }

    private String getUserKey(String token, String secret) {
        Claims claims;
        try {
            claims = JwtUtils.parseToken(token, secret); //获取令牌中信息 解析payload中信息
            if (claims == null) {
                log.error("令牌已过期或验证不正确!");
                return null;
            }

        } catch (Exception e) {
            log.error("令牌已过期或验证不正确!e:",e);
            return null;
        }
        return JwtUtils.getUserKey(claims); //获取jwt中的key
    }
java 复制代码
    @Override
    public R<LoginUserVO> info(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        LoginUser loginUser = tokenService.getLoginUser(token,secret);
        if(loginUser == null){
            return R.fail();
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        return R.ok(loginUserVO);
    }

然后测试一下

del+key可以删除redis数据


这样就成功了

这个是根据登录设置的header自动进行查询的,不用传json

然后测试一下延长redis时间的接口也是没有问题的

3.4 开发前端

java 复制代码
export function getUserInfoService(){
    return service({
        url: '/sysUser/info',
        method: 'get'
    })
}
java 复制代码
import { reactive } from 'vue';
import { getUserInfoService } from '@/apis/suser';

const loginUser = reactive({
  nickName: ''
})

async function getUserInfo(){
  const userInfo = await getUserInfoService();
  loginUser.nickName = userInfo.data.nickName;
}

await getUserInfo();

然后再request.js里面定义请求拦截器

java 复制代码
//请求拦截器
service.interceptors.request.use(
  (config) => {
    if (getToken()) {
      config.headers["Authorization"] = "Bearer " + getToken();
    }
    return config;
  },
  (error) => {
    console.log(error)
    Promise.reject(error);
  }
);

这个请求拦截器就是拦截每个给后端发起的请求,然后判断是否有token,有的话,就在请求头中加上token

java 复制代码
          <div>
            <strong>当前用户:</strong>{{loginUser.nickName}}
          </div>

注意登录的前端接口写错了

改一下为这个样子

java 复制代码
async function  loginFun() {
  try{
      const res = await loginService(userAccount.value, password.value);
      setToken(res.data)
      router.push("/oj/layout")
      console.log("登录成功:" , res.data);
  }catch(err){
      console.log("登录失败:" , err);
  }
}

这样就可以了

4. 退出登录

4.1 业务分析

就是让token不为空,然后解析一下,解析出来不能执行正常业务了。是可以进行解析的

所以点击退出登录,让redis中的数据不存在就可以了

这样就可以避免多次登录,redis中的数据增多了

后端返回请求以后,如果是成功的,前端就清楚存储的token

所以就是后端清楚redis,前端清楚token

4.2 后端开发

java 复制代码
    @DeleteMapping("/logout")
    @Operation(summary = "退出登录", description = "退出登录")
    public R<Void> logout(@RequestHeader(HttpConstants.AUTHENTICATION) String token){
        log.info("退出登录...,token:{}", token);
        return toR(sysUserService.logout(token));
    }

因为退出登录后端会删除redis所以是DeleteMapping

java 复制代码
    @Override
    public boolean logout(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token,secret);
    }
java 复制代码
    public boolean deleteLoginUser(String token, String secret) {
        String userKey = getUserKey(token, secret);
        if(userKey == null){
            return false;
        }
        String tokenKey = getTokenKey(userKey);
        return redisService.deleteObject(tokenKey);
    }

然后测试一下

4.3 前端开发

点击退出登录的时候会有一个消息弹窗

我们用的就是elementplus的消息弹窗

观察一下我们可以发现

ElMessageBox.confirm 方法返回一个 Promise 对象。当用户点击确认按钮时,Promise 会进入 resolved 状态,此时会执行 .then() 中的回调函数;而当用户点击取消按钮或者关闭对话框时,Promise 会进入 rejected 状态,这时就会执行 .catch() 中的回调函数。

所以说用户点击取消按钮或者关闭对话框时,就是相当于抛出了一个异常,所以弹窗如果后面还有代码就不会执行了

而点击了确定按钮的话(其实点击什么都是返回Promise ),会返回一个 Promise 对象,返回这个对象是一个异步的过程,所以要await,不然异步的话,就去判断Promise 对象,可能会判断失误

所以说点击了确定按钮的话,就会执行弹窗后面的代码了

java 复制代码
export function logoutService(){
    return service({
        url: '/sysUser/logout',
        method: 'delete'
    })
}

如果是函数抛出异常,也会结束后面代码执行

java 复制代码
async function logout(){
    await ElMessageBox.confirm(
    '退出登录',
    '温馨提示',
    {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      type: 'warning',
    }
  )
  await logoutService();
  removeToken();
  router.push('/oj/login');
}

因为logoutService抛出的异常我们可以直接输出错误,对于异常的情况没有什么好处理的,就什么都不干就可以了,所以我们不用try和catch

5. 前端路由优化

5.1 重定向

这个的问题是什么呢

就是我们点击这个地址不用直接到login,而是要手动输入地址才可以了

什么做到点击这个http://localhost:5173/,就可以自动跳转到login呢,这个就要使用重定向了

java 复制代码
    {
      path: '/',
      redirect: '/oj/login'
    },

这样配置就可以了

5.2 全局前置守卫

我们要求

未登录要求不管点击哪个页面都要跳回登录页面

登录过后,未过期,点回login自动跳转功能页面

登录过后,点击login,就直接不用登录就可以使用功能了

就是要在路由跳转之前进行判断处理

谁来进行页面跳转呢,就是router,router在哪里呢,就是在index.js里面配置的,所以对router进行配置即可,就是对页面跳转之前进行的配置

java 复制代码
router.beforeEach((to, from, next) => {
  if (getToken()) {  //已经登陆过
    /* has token*/
    if (to.path === '/oj/login') {
      next({ path: '/oj/layout/question' })
    } else {
      next()
    }
  } else {
    if (to.path !== '/oj/login') {
      next({path:'/oj/login'})
    } else {
      next()
    }
  }
})

这样就可以了

to是目的路由

from是源路由

next是真正的目的路由是去哪里

next()就是和to一样的

这样我们在没有登录的情况下输入http://localhost:5173/oj/layout

就会自动跳转为http://localhost:5173/oj/login

登录下输入http://localhost:5173/oj/login

就会自动变为http://localhost:5173/oj/layout/question

但是如果登录状态过期呢

这个也是和没有登录是一样的,怎么判断呢,前端是无法判断token是否过期的

5.3 token过期处理

java 复制代码
        boolean isLogin = redisService.hasKey(getTokenKey(userKey));
        if (!isLogin) {
            return unauthorizedResponse(exchange, "登录状态已过期");
        }
java 复制代码
    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String
            msg) {
        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
        return webFluxResponseWriter(exchange.getResponse(), msg,
                ResultCode.FAILED_UNAUTHORIZED.getCode());
    }
java 复制代码
    FAILED_UNAUTHORIZED (3001, "未授权"),

我们可以这样处理

因为后端对于token过期会报错的,就在网关中,会报登录状态已过期,会报3001的错误,,,而且我们还有响应拦截器,所以就可以在响应拦截器中进行处理了,如果过期了就自动跳转到login

所以说登录状态由浏览器的token和token是否过期一起决定

我们可以去redis中删除数据,手动弄为过期

因为过期了,redis就会自动删除数据,所以我们删除它,就是模仿的过期

java 复制代码
service.interceptors.response.use(
    (res) => {
        // 未设置状态码则默认成功状态
        const code = res.data.code;
        const msg = res.data.msg;
        if(code === 3001){
            ElMessage.error(msg);
            router.push('/oj/login')
            removeToken();
            return Promise.reject(new Error(msg));
        }else if (code !== 1000) {
            ElMessage.error(msg);
            return Promise.reject(new Error(msg));
        } else {
            return Promise.resolve(res.data);
        }
    },
    (error) => {
        return Promise.reject(error);
    }
);

为什么要removeToken呢,因为已经过期了,没用了,但是不删掉的话,就可以一直去layout页面

注意如果是刷新的话,在全局前置守卫中,to就是自身url,而from则是根路径

这样就OK了

调试也没有错误

总结

相关推荐
be or not to be20 小时前
CSS 布局机制与盒模型详解
前端·css
没有bug.的程序员20 小时前
Sentinel 流控原理深度解析:从SlotChain到热点参数限流的设计哲学
jvm·微服务·云原生·eureka·sentinel·服务发现
runepic21 小时前
Vue3 + Element Plus 实现PDF附件上传下载
前端·pdf·vue
程序员修心21 小时前
CSS 盒子模型与布局核心知识点总结
开发语言·前端·javascript
elangyipi12321 小时前
前端面试题:CSS BFC
前端·css·面试
程序员龙语21 小时前
CSS 核心基础 —— 长度单位、颜色表示与字体样式
前端·css
shuishen4921 小时前
视频尾帧提取功能实现详解 - 纯前端Canvas API实现
前端·音视频·尾帧·末帧
IT_陈寒21 小时前
Python性能调优实战:5个不报错但拖慢代码300%的隐藏陷阱(附解决方案)
前端·人工智能·后端
jingling55521 小时前
uni-app 安卓端完美接入卫星地图:解决图层缺失与层级过高难题
android·前端·javascript·uni-app
酌沧21 小时前
编程智能体Cline的核心架构
架构·状态模式