react组件在登陆失效后,重新登陆,localstore没有被更新,导致登陆失败


修改这个token值,重新输入账号密码,永远登陆失败

备注:删除这token,登陆流程就没问题了

咱们现在使用的 Guard 版本是什么~,使用的登录方式是什么呢~

版本: “@authing/react-ui-components”: “3.1.7”
登陆方式:账号密码方式

我现在按照截图来看 咱们使用的是 AuthingGuard 这个组件吧~ 目前我们 一直在迭代更新的是 Guard 组件,并且 Guard 组件已经废弃主动将 token、user 写到 LocalStorage 中,看一下咱们的组件可以进行替换吗?如果不行的话 遇到的问题可以继续反馈~

可以替换,有文档说明吗,我这里是根据这个文档对接的。React 登录组件 | Authing 文档

方便发一下 目前项目中这部分的代码吗?我现在只能判断是 咱们的引用有些问题~ 如果有完整的示例代码我这里更好判断一些~

登陆页面代码

import React from 'react';
import { history, useModel } from 'umi';

import styles from './index.less';
import SelectDomain from '@/components/SelectDomain';
// import useRequest from '@ahooksjs/use-request';

import { AuthingGuard } from '@authing/react-ui-components';
// 引入 css 文件
import '@authing/react-ui-components/lib/index.min.css';


const Login: React.FC = () => {
  const { initialState, setInitialState } = useModel('@@initialState');

  const fetchUserInfo = async () => {
    const userInfo = await initialState?.fetchUserInfo?.();
    if (userInfo) {
      await setInitialState((s) => ({
        ...s,
        currentUser: userInfo,
      }));
    }
  };


  return (
    <div className={styles.container}>
      <div className={styles.lang} data-lang>
        <div style={{ display: 'block', float: 'right' }}>
          <SelectDomain />
        </div>
      </div>
      <AuthingGuard
        appId="xxxxx"
        onLogin={async (userinfo) => {
          // console.log(userinfo);
          await fetchUserInfo();
          /** 此方法会跳转到 redirect 参数所在的位置 */
          if (!history) return;
          const { query } = history.location;
          const { redirect } = query as { redirect: string };
          history.push(redirect || '/');
        }}
      />
    </div>
  );
};

export default Login;

登陆成功后读取用户信息。fetchUserInfo

import type { Settings as LayoutSettings } from '@ant-design/pro-layout';
import type { RunTimeLayoutConfig, RequestConfig } from 'umi';
import { history, Link } from 'umi';
import RightContent from '@/components/RightContent';

import { BookOutlined, LinkOutlined } from '@ant-design/icons';
import { debug as createDebug } from 'debug';
import { initAuthClient, useAuthing } from '@authing/react-ui-components';


initAuthClient({
  appId: 'xxxxx',
});
const { authClient } = useAuthing();
globalThis.d = createDebug('debug');
// global.debug = d('default');

const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login';
let token: Maybe<string> | undefined;

/** 获取用户信息比较慢的时候会展示一个 loading */
export const initialStateConfig = {
  // loading: <Spin />,
};

/**
 * @see  https://umijs.org/zh-CN/plugins/plugin-initial-state
 * */
export async function getInitialState(): Promise<{
  settings?: Partial<LayoutSettings>;
  currentUser?: API.CurrentUser;
  fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
}> {
  const fetchUserInfo = async () => {
    let r = await authClient?.getCurrentUser();
    // debugger
    if (r) {
      token = r.token;
      return {
        name: r.username,
        avatar: r.photo,
      } as API.CurrentUser;
    }
    return undefined;

  };
  // 如果是登录页面,不执行
  if (history.location.pathname !== loginPath) {
    const currentUser = await fetchUserInfo();
    return {
      fetchUserInfo,
      currentUser,
      settings: {},
    };
  }
  return {
    fetchUserInfo,
    settings: {},
  };
}

// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState }) => {
  return {
    rightContentRender: () => <RightContent />,
    disableContentMargin: false,
    // waterMarkProps: {
    //   content: initialState?.currentUser?.name,
    // },
    // footerRender: () => <Footer />,
    onPageChange: () => {
      const { location } = history;
      // 如果没有登录,重定向到 login
      if (!initialState?.currentUser && location.pathname !== loginPath) {
        history.push(loginPath);
      }
    },
    links: isDev
      ? [
          <Link to="/umi/plugin/openapi" target="_blank">
            <LinkOutlined />
            <span>OpenAPI 文档</span>
          </Link>,
          <Link to="/~docs">
            <BookOutlined />
            <span>业务组件文档</span>
          </Link>,
        ]
      : [],
    menuHeaderRender: undefined,
    // 自定义 403 页面
    // unAccessible: <div>unAccessible</div>,
    ...initialState?.settings,
  };
};
export const request: RequestConfig = {
  errorConfig: {
    adaptor: (resData) => {
      // debugger
      return {
        ...resData,
        success: resData.success,
        errorMessage: resData.message,
        showType: 4,
      };
    },
  },
  credentials: 'include',
  requestInterceptors: [
    (url, options) => {

      const requestDomain = localStorage.getItem('requestDomain') || '';
      let u = url;
      let h = options.headers as HeadersInit;
      h['Authorization'] = `Bearer ${token}`;

      if (!u.startsWith('http')) {
        if (requestDomain) {
          u = requestDomain + url;
        }
      }
      
      return {
        // 修改请求路径
        url: u,
        options: {
          ...options,
        },
      };
    },
  ],
};

其实问题很简单。就是我看network的时候,登陆请求,是已经显示登陆成功了。
然后在获取用户信息,返回了当前状态是未登陆,看了下network的cookie,授权的token是老的,没有用到登陆成功后的token。所以说到底就是token的本地缓存没更新成功。下面我提供下截图,我把本地token改成一个错误的,前面加了个“1”。如果有其他的sdk包,可以提供下,我这边切换也方便

主要是咱们的使用方式有些问题,拆分为三个问题

  1. 老版本 Guard 替换成新版本 Guard
    新版本中的 Guard 组件已经废弃主动将 token、user 写到 LocalStorage 中,需要使用者手动进行维护。
import { AuthingGuard } from '@authing/react-ui-components';

将这部分引用替换为 Guard 即可,Props 传值的方式不用变化

import { Guard } from '@authing/react-ui-components';
  1. AuthClient 的使用
    代码中咱们直接使用的是 initAuthClient,按照您提供的业务场景中并不推荐这样使用,如果想使用 AuthClient 可以直接初始化 SDK,或者可以使用 onLogin 回调返回的 AuthClient。直接使用 useAuthing + initAuthClient 的话回导致 SDK 内部状态不一致,导致一些不可控的错误。

  2. Token 有效性校验问题
    在获取当前用户时,应该优先判断 Token 是否被篡改,检查的方法为 checkLoginStatus 以及 Token 相关文档

我还有个疑问。你这里说新的Guard,不再维护本地token,那么是不是说,用户每次打开页面,登陆状态都会被重置,需要重新登陆才能后续操作了。AuthClient需要通过onLogin来获取。

  1. 在 onLogin 的回调中会返回 user 对象,user 对象中有 Token 属性,咱们可以手动保存在 LocalStorage 中或者其他的地方,不用再依赖 Guard 的保存了
  2. 在 onLogin 中获取 AuthClient 只是其中的一个方法,咱们可以直接使用 SDK 初始化 AuthClient。
  3. 通过 Token 可以获取到用户的详情信息的,我们的 SDK 中提供了方法 getCurrentUser,具体的使用方式可以看一下这个方法的文档。

我简单的画了一下 业务流程图 可以参考一下