我们拥有 12 年建站编程经验
- 虚拟产品交易平台定制开发
- ant design pro & nodejs 多角色权限管理系统源码
- 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!);
基本上这样就完事了。
- ant design pro 如何去保存颜色
- ant design pro v6 如何做好角色管理
- ant design 的 tree 如何作为角色中的权限选择之一
- ant design 的 tree 如何作为角色中的权限选择之二
- ant design pro access.ts 是如何控制多角色的权限的
- ant design pro 中用户的表单如何控制多个角色
- ant design pro 如何实现动态菜单带上 icon 的
- ant design pro 的表分层级如何处理
- ant design pro 如何处理权限管理
- ant design pro 技巧之自制复制到剪贴板组件
- ant design pro 技巧之实现列表页多标签