Nest学习:使用React + Nest 做的登录验证码案例

使用技术

前端 React + Antd

后端 Nest + svg-captcha + express-session

代码示例
前端
import { FC, memo, useState } from "react";
import { Col, Row, Button, Form, Input, Image, message } from "antd";

const login: FC = memo(() => {
  const [code, setCode] = useState("/api/user/code");
  //提示框hooks
  const [messageApi, contextHolder] = message.useMessage();

  //表单类型
  interface user {
    username: string;
    passwrd: number;
    code: number;
  }

  //重置验证码
  const resetCode = () => setCode(code + "?" + Math.random());
  //点击登录按钮事件
  const onsubmit = async (value: user) => {
    await fetch("/api/user/login", {
      method: "POST",
      body: JSON.stringify(value),
      headers: {
        "content-type": "application/json",
      },
    })
      .then((res) => res.json())
      .then((res) => {
        if (res.code === 200) {
          messageApi.open({
            type: "success",
            content: res.msg,
          });
        } else {
          messageApi.open({
            type: "error",
            content: res.msg,
          });
        }
      });
  };

  //表单验证失败事件
  const onFinishFailed = (errorInfo: any) => {
    console.log("Failed:", errorInfo);
  };

  return (
    <>
      {contextHolder}
      < Row align="middle" style={{ height: "500px" }} >
        < Col className="gutter-row" span={6} offset={8} >
          < Form
            name="basic"
            style={{ maxWidth: 600 }}
            onFinish={onsubmit}
            onFinishFailed={onFinishFailed}
            autoComplete="off"
          >
            < Form.Item
              label="用户名"
              name="username"
              rules={[{ required: true, message: "请输入用户名!" }]}
            >
              < Input size="large" />
            < />
            < Form.Item
              label=" 密码"
              name="password"
              rules={[{ required: true, message: "请输入密码!" }]}
            >
              < Input.Password size="large" />
            < />
            < div style={{ display: "flex" }}>
              < Form.Item
                label="验证码"
                name="inputcode"
                style={{ width: "80%" }}
                rules={[{ required: true, message: "请输入验证码!" }]}
              >
                < Input size="large" />
              < />
              < Image
                style={{ marginLeft: "10px" }}
                width={100}
                src={code}
                preview={false}
                onClick={() => resetCode()}
              />
            < />

            < Form.Item wrapperCol={{ offset: 8, span: 16 }}>
              < Button type="primary" htmlType="submit">
                登录
              < />
            < />
          < />
        < />
      < /Row>
    < />
  );
});

export default login;
跨域配置 vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  server: {
    proxy: {
      "/api": {
        target: "http://localhost:3000",
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ""),
      },
    },
  },
});

在这里插入图片描述

后端
主文件 main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
//导出session包
import * as session from "express-session";
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  //使用session
  app.use(
    session({
      secret: "code", //生成服务端session 签名
      rolling: true, //在每次请求时强行设置 cookie
    })
  );
  await app.listen(3000);
}
bootstrap();

express-session 参数配置详解

secret生成服务端 session 签名 可以理解为加盐
name生成客户端 cookie 的名字 默认 connect.sid
cookie设置返回到前端 key 的属性,默认值为{ path: ‘/’, httpOnly: true, secure: false, maxAge: null }。
rolling在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false)

在这里插入图片描述

模块文件 app.module.ts

@Module() 装饰器提供 Nest 用来组织应用结构的元数据。

import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
服务模块 app.service.ts

服务将负责数据存储和检索,并设计为供 Controller 使用

import { Injectable } from "@nestjs/common";
import * as svgcaptcha from "svg-captcha";

@Injectable()
export class AppService {
  createCode(res, session) {
    //创建验证码
    const { data, text } = svgcaptcha.create({
      size: 4,
      fontSize: 50,
      width: 100,
      height: 40,
      background: "#cc9966",
    });
    //设置session
    session.code = text;
    //返回验证码
    res.type("image/svg+xml");
    res.send(data);
  }

  createUser(body, session) {
    //验证码判断
    if (
      session.code.toLocaleLowerCase() === body.inputcode.toLocaleLowerCase()
    ) {
      return {
        code: 200,
        msg: "验证码正确",
      };
    } else {
      return {
        code: 500,
        msg: "验证码错误",
      };
    }
  }
}
控制模块 app.controller.ts

使用 @Controller() 装饰器将指定的可选路由路径前缀。 在 @Controller() 装饰器中使用路径前缀可以让我们轻松地对一组相关路由进行分组,并最大限度地减少重复代码。

import { Controller, Body, Post, Get, Res, Session } from "@nestjs/common";
import { AppService } from "./app.service";

@Controller("user")
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get("code")
  createCode(@Res() res, @Session() session) {
    return this.appService.createCode(res, session);
  }

  @Post("login")
  createUser(@Body() body, @Session() session) {
    return this.appService.createUser(body, session);
  }
}
案例展示

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值