【图文并茂】ant design pro 如何对接登录接口

发布于:2024-08-22 ⋅ 阅读:(34) ⋅ 点赞:(0)

在这里插入图片描述
在这里插入图片描述
我们拥有 12 年建站编程经验

  1. 虚拟产品交易平台定制开发
  2. ant design pro & nodejs 多角色权限管理系统源码
  3. WordPress 外贸电商独立站建站

我的网站

ant design pro 如何去对接登录呢。

首先你后端要有登录接口。

例如我的:

const login = handleAsync(async (req: Request, res: Response) => {
  const { email, password } = req.body;

  const user = await User.findOne({ $or: [{ email }, { name: email }] });

  if (!user) {
    res.status(400);
    throw new Error('User not found');
  }

  if (!user.live) {
    res.status(401);
    throw new Error('User is not live');
  }

  if (await bcrypt.compare(password, user.password)) {
    const refreshToken = generateRefreshToken(user._id);

    res.json({
      success: true,
      name: user.name || user.email,
      token: generateToken(user.id),
      refreshToken,
    });
  } else {
    res.status(401);
    throw new Error('Invalid email or password');
  }
});

登录接口比较简单的,无外乎查下数据库,匹配下用户名和密码,找到这条用户

主要是返回的信息,我这里要返回的是 token ,这个信息给到前端,

前端要拿到这个 token。

我这里用的是 json web token , 其实就相当于一个人的身份证一样,有了身份证就能进入系统。

只是这种身份证并不是固定的,每次都可以生成别的,也可能有时间限制,反正能代表一个人就是了。

如何生成 token 呢:

参考下我的

import jwt from 'jsonwebtoken';

const generateToken = (id: string): string => {
  return jwt.sign({ id }, process.env.JWT_SECRET as string, {
    expiresIn: process.env.JWT_EXPIRE,
  });
};

const generateRefreshToken = (id: string): string => {
  return jwt.sign({ id }, process.env.REFRESH_JWT_SECRET as string, {
    expiresIn: process.env.REFRESH_JWT_EXPIRE,
  });
};

export { generateToken, generateRefreshToken };

前端实现

前端比较简单了,总体代码是这样的:

src/pages/User/Login/index.tsx

import { useIntl } from '@umijs/max';
import { Footer } from '@/components';
import { login } from '@/services/ant-design-pro/api';
import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { LoginForm, ProFormText } from '@ant-design/pro-components';
import { FormattedMessage, history, SelectLang, useModel, Helmet } from '@umijs/max';
import { message } from 'antd';
import Settings from '../../../../config/defaultSettings';
import React from 'react';
import { flushSync } from 'react-dom';
import { createStyles } from 'antd-style';

const useStyles = createStyles(({ token }) => {
  return {
    action: {
      marginLeft: '8px',
      color: 'rgba(0, 0, 0, 0.2)',
      fontSize: '24px',
      verticalAlign: 'middle',
      cursor: 'pointer',
      transition: 'color 0.3s',
      '&:hover': {
        color: token.colorPrimaryActive,
      },
    },
    lang: {
      width: 42,
      height: 42,
      lineHeight: '42px',
      position: 'fixed',
      right: 16,
      borderRadius: token.borderRadius,
      ':hover': {
        backgroundColor: token.colorBgTextHover,
      },
    },
    container: {
      display: 'flex',
      flexDirection: 'column',
      height: '100vh',
      overflow: 'auto',
      backgroundImage:
        "url('https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/V-_oS6r-i7wAAAAAAAAAAAAAFl94AQBr')",
      backgroundSize: '100% 100%',
    },
  };
});

const Lang = () => {
  const { styles } = useStyles();

  return (
    <div className={styles.lang} data-lang>
      {SelectLang && <SelectLang />}
    </div>
  );
};

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

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

  const handleSubmit = async (values: API.LoginParams) => {
    try {
      // 登录
      const response = await login({ ...values });
      if (response && response.success) {
        const defaultLoginSuccessMessage = intl.formatMessage({
          id: 'pages.login.success',
          defaultMessage: '登录成功!',
        });
        message.success(defaultLoginSuccessMessage);
        localStorage.setItem('token', response.token!);
        localStorage.setItem('refreshToken', response.refreshToken!);
        await fetchUserInfo();
        const urlParams = new URL(window.location.href).searchParams;
        history.push(urlParams.get('redirect') || '/');
        return;
      }
    } catch (error: any) {
      const defaultLoginFailureMessage = intl.formatMessage({
        id: 'pages.login.failure',
        defaultMessage: '登录失败,请重试!',
      });
      console.log(error);
      message.error(error?.response?.data?.message || defaultLoginFailureMessage);
    }
  };

  return (
    <div className={styles.container}>
      <Helmet>
        <title>
          {intl.formatMessage({
            id: 'menu.login',
            defaultMessage: '登录页',
          })}
          - {Settings.title}
        </title>
      </Helmet>
      <Lang />
      <div
        style={{
          flex: '1',
          padding: '32px 0',
        }}
      >
        <LoginForm
          contentStyle={{
            minWidth: 280,
            maxWidth: '75vw',
          }}
          logo={<img alt="logo" src="/logoipsum-295.svg" />}
          title={process.env.UMI_APP_APP_NAME || 'antd-ts-admin'}
          subTitle={intl.formatMessage({ id: 'pages.layouts.userLayout.title' })}
          onFinish={async (values) => {
            await handleSubmit(values as API.LoginParams);
          }}
        >
          <>
            <ProFormText
              name="email"
              fieldProps={{
                size: 'large',
                prefix: <UserOutlined />,
              }}
              placeholder={intl.formatMessage({
                id: 'pages.login.username.placeholder',
                defaultMessage: '用户名',
              })}
              rules={[
                {
                  required: true,
                  message: (
                    <FormattedMessage
                      id="pages.login.username.required"
                      defaultMessage="请输入用户名!"
                    />
                  ),
                },
              ]}
            />
            <ProFormText.Password
              name="password"
              fieldProps={{
                size: 'large',
                prefix: <LockOutlined />,
              }}
              placeholder={intl.formatMessage({
                id: 'pages.login.password.placeholder',
                defaultMessage: '密码',
              })}
              rules={[
                {
                  required: true,
                  message: (
                    <FormattedMessage
                      id="pages.login.password.required"
                      defaultMessage="请输入密码!"
                    />
                  ),
                },
              ]}
            />
          </>
        </LoginForm>
      </div>
      <Footer />
    </div>
  );
};

export default Login;

表单内容是可以随时改的。

主要是 这里

 <ProFormText
              name="email"
              fieldProps={{
                size: 'large',
                prefix: <UserOutlined />,
              }}
              placeholder={intl.formatMessage({
                id: 'pages.login.username.placeholder',
                defaultMessage: '用户名',
              })}
              rules={[
                {
                  required: true,
                  message: (
                    <FormattedMessage
                      id="pages.login.username.required"
                      defaultMessage="请输入用户名!"
                    />
                  ),
                },
              ]}
            />

这里面有个

name=“email”

这里关系到提到给后端的参数。

还有一个 name=“password” 的

在这里插入图片描述
就是这里。

后端就要拿到 name 和 password

在这里插入图片描述
最后看下这里:

  const handleSubmit = async (values: API.LoginParams) => {
    try {
      // 登录
      const response = await login({ ...values });
      if (response && response.success) {
        const defaultLoginSuccessMessage = intl.formatMessage({
          id: 'pages.login.success',
          defaultMessage: '登录成功!',
        });
        message.success(defaultLoginSuccessMessage);
        localStorage.setItem('token', response.token!);
        localStorage.setItem('refreshToken', response.refreshToken!);
        await fetchUserInfo();
        const urlParams = new URL(window.location.href).searchParams;
        history.push(urlParams.get('redirect') || '/');
        return;
      }
    } catch (error: any) {
      const defaultLoginFailureMessage = intl.formatMessage({
        id: 'pages.login.failure',
        defaultMessage: '登录失败,请重试!',
      });
      console.log(error);
      message.error(error?.response?.data?.message || defaultLoginFailureMessage);
    }
  };

const response = await login({ …values });

这里有指定了请求路径和请求方法。

export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
  return request<API.LoginResult>(`/auth/login`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}

请求地址 /auth/login 要对上。还有请求方法 post

后端

在这里插入图片描述
上面都对了之后,就要拿到响应,

这里可以多用 console.log 输出一下,或看调试工具。

在这里插入图片描述
响应里是有 success: true ,用来判断刚刚好。

if (response && response.success) {

最后,就是把响应的 token 拿到并存起来。

localStorage.setItem(‘token’, response.token!);
localStorage.setItem(‘refreshToken’, response.refreshToken!);

基本上这样就完事了。


网站公告

今日签到

点亮在社区的每一天
去签到