当前位置: 首页 > article >正文

Vben5登录过期无法再次登录问题,http状态码

个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview


前言

最近在做项目前端,使用的https://doc.vben.pro/,在登录过期时出现了无法再次登录的问题,在此记录一下。

image

项目前面那些直接略过,如果感兴趣直接看官方文档就可以 ,以下会根据解决过程附带部分官网说明。

分析原因

梳理流程

image

文档前面的都已按照要求配置修改好了,所有的认证接口也都完成了,登录也是没有问题的,就是在accessToken过期的时候无法回到登录页面,就算直接路由到/auth/login也永远在转圈圈。

在本项目中,在登录完成后将accessToken使用pinia加持久化插件存储到localStorage中,

// 项目配置 setup.ts

/**
 * @zh_CN 初始化pinia
 */
export async function initStores(app: App, options: InitStoreOptions) {
  const { createPersistedState } = await import('pinia-plugin-persistedstate');
  pinia = createPinia();
  const { namespace } = options;
  pinia.use(
    createPersistedState({
      // key $appName-$store.id
      key: (storeKey) => `${namespace}-${storeKey}`,
      storage: localStorage,
    }),
  );
  app.use(pinia);
  return pinia;
}

// 认证相关 access.ts
...

  persist: {
    // 持久化
    pick: ['accessToken', 'refreshToken', 'accessCodes'],
      },
  state: (): AccessState => ({
    accessCodes: [],
    accessMenus: [],
    accessRoutes: [],
    accessToken: null,
    isAccessChecked: false,
    loginExpired: false,
    refreshToken: null,
  }),
});

然后在每次请求时使用自定义配置了请求拦截器和响应拦截器的axios中带上accessToken

// axios配置 request.ts
  function formatToken(token: null | string) {
    return token ? `Bearer ${token}` : null;
  }

  // 请求头处理
  client.addRequestInterceptor({
    fulfilled: async (config) => {
      const accessStore = useAccessStore();

      config.headers.Authorization = formatToken(accessStore.accessToken);
      config.headers['Accept-Language'] = preferences.app.locale;
      return config;
    },
  });

  // response数据解构
  client.addResponseInterceptor<HttpResponse>({
    fulfilled: (response) => {
      const { data: responseData, status } = response;

      const { code, data } = responseData;
      if (status >= 200 && status < 400 && code === 0) {
        return data;
      }

      throw Object.assign({}, response, { response });
    },
  });

在此都没问题,因为使用登录和其他接口请求和响应都没问题,问题到底在哪?

image

后端命名也是正常接收到accessToken了呀,为什么永远在转圈圈,回不去了呢?

image

那既然这样,只能使出我并没把握的前端debug了。

debug

根据前面的[Vue Router warn]: Unexpected error when starting the router: {code: 401, msg: '账号未登录'}的输出和错误栈,大概可以知道是在bootstrap.ts:33出问题了,那么先在这断一下,结合代码判断,这一步就是设置路由啊,可知一定是初始化路由出错了,导致所有路由都出问题了。

image

那么在创建路由再加上断点,在export之前操作router都点一下

image

发现可以到下面的创建路由守卫处,说明前面都还没错,那么就继续分析路由守卫

image

在继续debug中发现,在到达96行之前就提示错误,开始转圈圈了,那么错误一定是89-96之间的。

image

最后发现是在fetchUserInfo后出错的,更准确的讲是getUserInfoApi出错的。

async function fetchUserInfo() {
  let userInfo: null | UserInfo = null;
  userInfo = await getUserInfoApi();
  userStore.setUserInfo(userInfo);
  return userInfo;
}

/**
 * 获取用户信息
 */
export async function getUserInfoApi() {
  return requestClient.get<UserInfo>('/user/info');
}

???不是已经正常返回了吗?我也提示账号未登录了啊,那一定是response的问题,继续看axios配置,这里设置三个拦截器,看其中token过期的处理响应拦截器,正常应该走doReAuthenticate重新认证逻辑啊,怎么不行呢?

// response数据解构
  client.addResponseInterceptor<HttpResponse>({
    fulfilled: (response) => {
      const { data: responseData, status } = response;

      const { code, data } = responseData;
      if (status >= 200 && status < 400 && code === 0) {
        return data;
      }

      throw Object.assign({}, response, { response });
    },
  });

  // token过期的处理
  client.addResponseInterceptor(
    authenticateResponseInterceptor({
      client,
      doReAuthenticate,
      doRefreshToken,
      enableRefreshToken: preferences.app.enableRefreshToken,
      formatToken,
    }),
  );

  // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
  client.addResponseInterceptor(
    errorMessageResponseInterceptor((msg: string, error) => {
      // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
      // 当前mock接口返回的错误字段是 error 或者 message
      const responseData = error?.response?.data ?? {};
      const errorMessage = responseData?.error ?? responseData?.msg ?? '';
      // 如果没有错误信息,则会根据状态码进行提示
      message.error(errorMessage || msg);
    }),
  );

authenticateResponseInterceptor响应拦截器细看,终于抓到你了,没有进入重新认证的逻辑是因为在

response?.status !== 401true直接抛异常了。

image

啊啊啊啊,响应码不就是401吗?好吧,还真不是。

image

根因

根因就是我使用了后端的统一响应处理,将所有异常都以正常的响应返回,也就是说并没有使用http的响应码,所有能被后端处理的异常都会以响应体里的code来表示。

/**
 * 认证失败
 */
@ExceptionHandler(NotLoginException.class)
public CommonResult<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
    String requestUri = request.getRequestURI();
    log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestUri, e.getMessage());
    return CommonResult.error(UNAUTHORIZED);
}

可是这个项目中使用了http状态码,也是axiosstatus

export interface AxiosResponse<T = any, D = any> {
  data: T;
  status: number;
  statusText: string;
  headers: RawAxiosResponseHeaders | AxiosResponseHeaders;
  config: InternalAxiosRequestConfig<D>;
  request?: any;
}

解决

解决起来也很简单,无非改造前端响应体或改在后端使用http响应,我选择了后者。

像下面这样使用org.springframework.http.ResponseEntity;再包装一下就可以了,

image

这样本次请求状态码就不是200了,正常走认证逻辑了。

image

进度

最后提一下进度吧!忘了上次提到的进度是哪里了,那就从指标版本控制之后讲吧。

1、【重要】策略集和规则的版本控制,还没做完

2、【重要】调整了项目结构,优化组件和上下文,新增规则action(发消息、打tag、加名单等)

3、【一般】新增节点、字段逻辑等通用接口,修复一些问题

4、【重要】策略的权重模式,顺序&最坏&投票&权重,在计算权重和动态字段时使用QLExpress

5、【重要】放弃逻辑删除,新增数据接入elasticsearch,增加docker-compose配置

这些是后端的,前端也要开始,有三大挑战:1、条件组合组件;2、公式类输入时联想词;3、LiteFlow编排。

image

image

image

写在最后

拙作艰辛,字句心血,望诸君垂青,多予支持,不胜感激。


个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview


http://www.kler.cn/a/458714.html

相关文章:

  • android stdudio环境: gradle一直安装失败
  • Linux -- 死锁、自旋锁
  • 案例分析-采样率对模拟链路的带宽的影响
  • vim里搜索关键字
  • debian系linux安装mysql
  • V90伺服PN版组态配置<一>
  • PVE虚拟化平台之开启虚拟机IP显示方法
  • Spring Boot项目接收前端参数的11种方式
  • 深度学习笔记(9)——神经网络和反向传播
  • HarmonyOs DevEco studio小技巧40--应用名称、图标与启动动画修改全攻略
  • 高仿CSDN编辑器,前端博客模板
  • 基于NodeMCU的物联网窗帘控制系统设计
  • 神经网络-AlexNet
  • Android笔试面试题AI答之非技术问题(1)
  • Asp.NET Core - 尝试一下在NET9中使用Yarp作为Api Proxy
  • C语言基础
  • Spring Boot实战:构建一个简单的RESTful API
  • vue2 升级为 vite 打包
  • Unity-Editor扩展显示文件夹大小修复版 FileCapacity.cs
  • HarmonyOS Next“说书人”项目 单机版 实践案例
  • AI与云计算:天作之合
  • 如何高效学习PHP框架源码
  • (长期更新)《零基础入门 ArcGIS(ArcMap) 》实验四----城市用地适宜性评价(超超超详细!!!)
  • unity使用代码在动画片段中添加event
  • 汽车网络安全基线安全研究报告
  • vue.js普通组件的注册-局部注册