TS项目实战三:Express实现登录注册功能后端

news2025/6/19 19:38:03

  使用express实现用户登录注册功能,使用ts进行代码开发,使用mysql作为数据库,实现用户登录、登录状态检测、验证码获取接口及用户注册相关接口功能的实现。
源码下载:[点击下载]
(https://download.csdn.net/download/m0_37631110/88909002)
讲解视频:

  1. 开发视频:

    TS实战项目十四:Express项目创建

  2. B站视频:

    TS实战项目十四:Express项目创建

  3. 西瓜视频:
    https://www.ixigua.com/7331627299491709477
    在这里插入图片描述

一、知识点

  1. tsc编译
  2. tsconfig.json配置项
  3. 模块定义及导入导出
  4. 类定义
  5. 参数属性
  6. 存取器
  7. 继承
  8. 抽象类
  9. 抽象方法
  10. 接口
  11. expres对接mysql数据库
  12. express-session
  13. bcryptjs
  14. express-jwt
  15. jsonwebtoken
  16. svg-captcha
  17. 枚举
  18. 静态变量
  19. 接口定义
  20. async/await

二、功能规划

  使用express框架搭建web服务器,切换js为ts方式开发,使用svg-captcha库提供图片验证码信息,使用express-jwt+jsonwebtoken实现登录状态检测及token生成。使用bcryptjs实现用户登录密码的加密及验证处理。
  实现三层分离,controller层进行接口控制,service进行业务逻辑处理,dao层进行数据库操作,使用mysql数据库。

三、项目创建

  1. 安装express生成器:npm install -g express-generator
    在这里插入图片描述

  2. 创建express项目:express tsexpress
    在这里插入图片描述

  3. 安装ts:npm install typescript --save
    在这里插入图片描述

  4. 安装ts相关依赖:
    (1) npm install @types/node --save-dev
    请添加图片描述
    (2) npm install @types/express --save-dev
    请添加图片描述

  5. 安装svg-captcha:npm install svg-captcha --save
    请添加图片描述

  6. 安装rotating-file-stream:npm install rotating-file-stream --save
    请添加图片描述

  7. 安装mysql:npm install mysql --save
    请添加图片描述
    npm install @types/mysql --save-dev
    请添加图片描述

  8. 安装morgan:npm install morgan --save
    请添加图片描述
    npm install @types/morgan --save-dev
    请添加图片描述

  9. 安装jsonwebtoken:npm install jsonwebtoken --save
    请添加图片描述
    npm install @types/jsonwebtoken --save-dev
    请添加图片描述

  10. 安装express-session:npm install express-session --save
    请添加图片描述
    npm install @types/express-session --save-dev
    请添加图片描述

  11. 安装express-jwt:npm install express-jwt --save
    请添加图片描述

  12. 安装bcryptjs:npm install bcryptjs --save
    请添加图片描述
    npm install @types/bcryptjs --save-dev
    请添加图片描述

  13. 安装cookie-parser:npm install cookie-parser --save
    请添加图片描述
    npm install @types/cookie-parser --save-dev
    请添加图片描述

  14. 使用ts-node ./bin/www启动项目,启动会报错,需要将app等文件中的代码进行调整,写成ts的写法。

  15. 项目目录结构:
    请添加图片描述

四、代码实现

  1. 项目配置文件package.json
{
  "name": "demo3",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "ts-node ./bin/www"
  },
  "dependencies": {
    "@types/bcryptjs": "^2.4.6",
    "@types/express-session": "^1.17.10",
    "@types/jsonwebtoken": "^9.0.5",
    "@types/mysql": "^2.15.25",
    "bcryptjs": "^2.4.3",
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "express": "~4.16.1",
    "express-jwt": "^8.4.1",
    "express-session": "^1.18.0",
    "http-errors": "~1.6.3",
    "jade": "~1.11.0",
    "jsonwebtoken": "^9.0.2",
    "morgan": "~1.9.1",
    "mysql": "^2.18.1",
    "rotating-file-stream": "^3.2.1",
    "svg-captcha": "^1.4.0",
    "typescript": "^5.3.3"
  },
  "devDependencies": {
    "@types/cookie-parser": "^1.4.6",
    "@types/express": "^4.17.21",
    "@types/morgan": "^1.9.9",
    "@types/node": "^20.11.16"
  }
}
  1. app.ts
import cookieParser from 'cookie-parser';
import express, {
  NextFunction,
  Request,
  Response,
} from 'express';
import session from 'express-session';
import createError from 'http-errors';
import jwt from 'jsonwebtoken';
import logger from 'morgan';
import path from 'path';
import * as rfs from 'rotating-file-stream'; // version 2.x

import { SYSTEM } from './src/config/Config';
import indexRouter from './src/routes/index';
import jwtToken, { lessPaths } from './src/security/jwtToken';

const app = express();

// view engine setup
app.set('views', path.join(__dirname, './src/views'));
app.set('view engine', 'jade');

//系统日志
var accessLogStream = rfs.createStream('access.log', {
  interval: '1d', // rotate daily
  path: path.join(__dirname, './logs/log')
})
app.use(logger(':method :url :status :res[content-length] - :response-time ms', { stream: accessLogStream }));

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());


app.use(session({
  secret: SYSTEM.privateKey,
  resave: false,
  saveUninitialized: true,
  cookie: { secure: false }
}))

app.use(express.static(path.join(__dirname, 'public')));


//加登陆的拦截
app.use(/.*/, function (err: any, req: Request, res: Response, next: NextFunction) {
  let less = lessPaths.find(item => {
    return req.url.startsWith(item);
  })
  if (less) {
    next();
    return;
  }
  if (!req.headers.authorization) {
    res.status(401).send({
      code: 401,
      msg: '请先登录!'
    });
  } else {
    jwt.verify(req.headers.authorization.split(" ")[1], SYSTEM.privateKey, (error, decoded) => {
      if (error) {
        res.status(401).send({
          code: 401,
          msg: 'token错误或已过期'
        });
      } else {
        req.headers.user = JSON.stringify(decoded);
        next();
      }
    })
  }
});

//是否登陆的判断
app.use(jwtToken);

//注册路由
app.use('/', indexRouter);

// catch 404 and forward to error handler
app.use(function (req: Request, res: Response, next: NextFunction) {
  next(createError(404));
});

// error handler
app.use(function (err: any, req: Request, res: Response, next: NextFunction) {
  if (err.name === "UnauthorizedError") {
    res.status(401).send({
      code: 401,
      msg: '请先登录!'
    });
  }
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});
console.log('启动成功');

module.exports = app;
  1. utils/DbUtils.ts
// const mysql = require('mysql')
import mysql, {
  Connection,
  ConnectionConfig,
} from 'mysql';

/**
 * 初始化链接
 * @param config 数据库的配置
 */
function init(config: ConnectionConfig): Connection {
    const connection = mysql.createConnection(config)
    connection.connect();
    return connection;
}

// connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => {
//     if (err) throw err

//     console.log('The solution is: ', rows[0].solution)
// })

// connection.end()

export default {
    init
}
  1. service/BaseService.ts
import BaseDao from '../dao/BaseDao';
import BaseEntity from '../entity/BaseEntity';
import DataEntity from '../entity/DataEntity';
import DelFlag from '../entity/DelFlag';

/**
 * 业务处理的基础类
 */
export default abstract class BaseService<T extends BaseEntity> {
    constructor(protected _dao: BaseDao<T>) {
    }

    /**
     * 保存数据
     * @param data  要保存的数据
     */
    save(data: T): void {
        if (data instanceof DataEntity) {
            (<DataEntity>data).createData = new Date();
            (<DataEntity>data).updateData = new Date();
            (<DataEntity>data).delflag = DelFlag.NOMAL;
        }
        this.beforeSave(data);
        this._dao.save(data).then(() => {
            //TODO 记录日志
        }).catch((err => {
            throw err;
        }));
    }
    /**
     * 数据保存之前,进行数据默认值的填充
     */
    protected abstract beforeSave(data: T): void;
    /**
     * 更新数据
     * @param data  要保存的数据
     */
    update<K extends DataEntity>(data: K): void {
        if (data instanceof DataEntity) {
            (<DataEntity>data).updateData = new Date();
        }
        this.beforeUpdate(data);
        let fileds: string[] = this.getFileds();
        this._dao.update(data, fileds).then(() => {
            //TODO 记录日志
        }).catch((err => {
            throw err;
        }));
    }
    /**
     * 获取需要更新的字段
     */
    protected abstract getFileds(): string[];
    /**
     * 数据保存之前,进行数据默认值的填充
     */
    protected abstract beforeUpdate<K extends DataEntity>(data: K): void;
    /**
     * 删除数据
     * @param data  要保存的数据
     */
    deleteData(id: number): void {
        this._dao.deleteData(id).then(() => {
            //TODO 记录日志
        }).catch((err => {
            throw err;
        }));
    }
    /**
     * 删除数据
     * @param data  要保存的数据
     */
    async get<K extends DataEntity>(id: number): Promise<K | null> {
        return this._dao.get(id);
    }
}
  1. service/system/UserService.ts
import bcrypt from 'bcryptjs';

import UserDao from '../../dao/system/UserDao';
import DataEntity from '../../entity/DataEntity';
import User, { UserStatus } from '../../entity/system/User';
import BaseService from '../BaseService';

export default class UserService extends BaseService<User> {
    protected getFileds(): string[] {
        return ['_account', '_name', '_phone', '_sex', '_status', '_remork'];
    }
    protected beforeSave(data: User) {
        data.status = UserStatus.NOMAL;
        let salt = bcrypt.genSaltSync(10);
        data.password = bcrypt.hashSync(data.password, salt);
    }
    protected beforeUpdate<K extends DataEntity>(data: K) {
    }
    constructor() {
        super(new UserDao())
    }
    /**
     * 通过账号查找对应的用户
     * @param account  账号信息
     */
    async findByAccount(account: string): Promise<User | null> {
        return (<UserDao>this._dao).findByAccount(account);
    }
    /**
    * 通过电话号码查找对应的用户
    * @param account  电话号码
    */
    async findByPhone(phone: string): Promise<User | null> {
        return (<UserDao>this._dao).findByPhone(phone);
    }

}
  1. security/jwtToken.ts
import express from 'express';
import { expressjwt } from 'express-jwt';

import { SYSTEM } from '../config/Config';

/**
 * 不需要鉴权的地址
 */
export const lessPaths = ["/register", "/register/captcha", "/login", "/login/captcha", "/public/"];

export default expressjwt({
    secret: SYSTEM.privateKey,
    algorithms: ['HS256'],
    getToken: (req: express.Request): string | Promise<string> | undefined => {
        if (
            req.headers.authorization &&
            req.headers.authorization.split(" ")[0] === "Bearer"
        ) {
            return req.headers.authorization.split(" ")[1];
        } else if (req.query && req.query.token) {
            return <string>req.query.token;
        }
        return undefined;
    }
}).unless({ path: lessPaths });
  1. routes/index.ts
// var express = require('express');
import express from 'express';

import index from '../controller/index';

var router = express.Router();
router.use(index)

// module.exports = router;
export default router;
  1. entity/DelFlag.ts
/**
 * 数据的删除状态标识
 */
enum DelFlag {
    //正常
    NOMAL = 1,
    //删除
    DELETE = 2
}
export default DelFlag;
  1. entity/DataEntity.ts
import BaseEntity from './BaseEntity';
import DelFlag from './DelFlag';

/**
 * 基础实体
 */
export default abstract class DataEntity extends BaseEntity {
    /**
     * 主键
     */
    private _id: number = 0;
    /**
     * 数据的创建时间
     */
    private _createDate: Date = new Date();
    /**
     * 数据的更新时间
     */
    private _updateDate: Date = new Date();
    /**
     * 备注
     */
    private _remork: string = "";
    /**
     * 状体:
     */
    private _delflag: DelFlag = 1;

    set id(id: number) {
        this._id = id;
    }
    get id() {
        return this._id;
    }
    set createData(createData: Date) {
        this._createDate = createData;
    }
    get createData() {
        return this._createDate;
    }
    set updateData(updateData: Date) {
        this._updateDate = updateData;
    }
    get updateData() {
        return this._updateDate;
    }
    set remork(remork: string) {
        this._remork = remork;
    }
    get remork() {
        return this._remork;
    }
    set delflag(delflag: DelFlag) {
        this._delflag = delflag;
    }
    get delflag() {
        return this._delflag;
    }
}
  1. entity/BaseEntity.ts
export default abstract class BaseEntity {
}
  1. entity/system/User.ts
import DataEntity from '../DataEntity';

/**
 * 用户的状态
 */
export enum UserStatus {
    NOMAL = 1,
    LOCK
}

/**
 * 用户实体
 */
export default class User extends DataEntity {
    /**
     * 账号
     */
    private _account: string = '';
    /**
     * 姓名
     */
    private _name: string = '';
    /**
     * 电话
     */
    private _phone: string = '';
    /**
     * 性别
     */
    private _sex: string = '';
    /**
     * 密码
     */
    private _password: string = '';
    /**
     * 状态1正常、2锁定
     */
    private _status: number = UserStatus.NOMAL;

    set account(account: string) {
        this._account = account;
    }
    get account() {
        return this._account;
    }
    set name(name: string) {
        this._name = name;
    }
    get name() {
        return this._name;
    }
    set phone(phone: string) {
        this._phone = phone;
    }
    get phone() {
        return this._phone;
    }
    set sex(sex: string) {
        this._sex = sex;
    }
    get sex() {
        return this._sex;
    }
    set password(password: string) {
        this._password = password;
    }
    get password() {
        return this._password;
    }
    set status(status: UserStatus) {
        this._status = status;
    }
    get status() {
        return this._status;
    }
}
  1. dao/BaseDao.ts
import {
    Connection,
    FieldInfo,
    MysqlError,
} from 'mysql';

import { DB } from '../config/Config';
import BaseEntity from '../entity/BaseEntity';
import DataEntity from '../entity/DataEntity';
import DbUtils from '../utils/DbUtils';

/**
 * dao基础类
 */
export default abstract class BaseDao<T extends BaseEntity> {
    /**
     * 获取数据库链接
     */
    protected getConn(): Connection {
        return DbUtils.init(DB);
    }
    /**
     * 获取要操作的实体表名
     */
    protected abstract getTableName(): string;
    /**
     * 创建新的实体
     */
    protected abstract newInsance(): T;
    /**
     * 保存数据
     * @param data 要保存的数据
     */
    save(data: T): Promise<unknown> {
        let conn = this.getConn();
        return new Promise((resolve, reject) => {
            conn.query('INSERT INTO ' + this.getTableName() + ' SET ?', data, function (err: MysqlError | null, results?: any, fields?: FieldInfo[]) {
                if (err) {
                    throw err
                };
                resolve({
                    err, results, fields
                });
            });
        }).finally(() => {
            conn.end();
        })
    }
    /**
     * 更新数据
     */
    update<K extends DataEntity>(data: K, fileds: string[]): Promise<unknown> {
        let conn = this.getConn();
        return new Promise((resolve, reject) => {
            let sql = 'UPDATE ' + this.getTableName() + ' SET ';
            let values: any[] = [];
            fileds.forEach((filed, index) => {
                if (index > 0) {
                    sql += ',';
                }
                sql += filed + '=?';
                let value: any = null;
                for (const key in data) {
                    if (key === filed) {
                        value = data[key];
                    }
                }
                values.push(value);
            })
            values.push(data.id);
            conn.query(sql + ' where _id=?', values, function (err: MysqlError | null, results?: any, fields?: FieldInfo[]) {
                if (err) {
                    throw err
                };
                resolve({
                    err, results, fields
                });
            });
        }).finally(() => {
            conn.end();
        })
    }
    /**
     * 删除数据
     * @param data 
     * @returns 
     */
    deleteData<K extends DataEntity>(id: Number): Promise<unknown> {
        let conn = this.getConn();
        return new Promise((resolve, reject) => {
            conn.query('DELETE FROM  ' + this.getTableName() + ' where _id=?', [id], function (err: MysqlError | null, results?: any, fields?: FieldInfo[]) {
                if (err) {
                    throw err
                };
                resolve({
                    err, results, fields
                });
            });
        }).finally(() => {
            conn.end();
        })
    }
    /**
     * 查询数据
     */
    async get<K extends DataEntity>(id: Number): Promise<K | null> {
        let conn = this.getConn();
        return await new Promise<K | null>((resolve, reject) => {
            conn.query('select * FROM  ' + this.getTableName() + ' where _id=?', [id], (err: MysqlError | null, results?: any, fields?: FieldInfo[]) => {
                if (err) {
                    throw err
                };
                if (!results || results === null || results.length <= 0) {
                    resolve(null);
                } else {
                    resolve(Object.assign(this.newInsance(), results[0]));
                }
            });
        }).then().finally(() => {
            conn.end();
        })
    }
}
  1. dao/system/UserDao.ts
import {
    FieldInfo,
    MysqlError,
} from 'mysql';

import User from '../../entity/system/User';
import BaseDao from '../BaseDao';

/**
 * 用户dao的处理
 */
export default class UserDao extends BaseDao<User> {
    protected newInsance(): User {
        return new User();
    }
    protected getTableName(): string {
        return 'user';
    }
    /**
     * 通过账号查找对应的用户信息
     */
    async findByAccount(account: string): Promise<User | null> {
        let conn = this.getConn();
        return await new Promise<User | null>((resolve, reject) => {
            conn.query('select * FROM  ' + this.getTableName() + ' where _account=?', [account], (err: MysqlError | null, results?: any, fields?: FieldInfo[]) => {
                if (err) {
                    throw err
                };
                if (!results || results === null || results.length <= 0) {
                    resolve(null);
                } else {
                    resolve(Object.assign(this.newInsance(), results[0]));
                }
            });
        }).then().finally(() => {
            conn.end();
        })
    }
    /**
    * 通过电话号码查找对应的用户信息
    */
    async findByPhone(phone: string): Promise<User | null> {
        let conn = this.getConn();
        return await new Promise<User | null>((resolve, reject) => {
            conn.query('select * FROM  ' + this.getTableName() + ' where _phone=?', [phone], (err: MysqlError | null, results?: any, fields?: FieldInfo[]) => {
                if (err) {
                    throw err
                };
                if (!results || results === null || results.length <= 0) {
                    resolve(null);
                } else {
                    resolve(Object.assign(this.newInsance(), results[0]));
                }
            });
        }).then().finally(() => {
            conn.end();
        })
    }
}
  1. controller/index.ts
// var express = require('express');
import express from 'express';

import LoginController from './login/LoginController';
import RegisterController from './register/RegisterController';
import UserController from './system/UserController';

var router = express.Router();

router.use(UserController);
router.use(LoginController);
router.use(RegisterController);

// module.exports = router;
export default router;
  1. controller/BaseViewController.ts
import {
    NextFunction,
    Request,
    Response,
} from 'express';

import DataEntity from '../entity/DataEntity';
import BaseService from '../service/BaseService';
import BaseController from './BaseController';

/**
 * 数据查看
 */
export default abstract class BaseViewContorller<T extends DataEntity> extends BaseController<T> {

    constructor(protected service: BaseService<T>) {
        super();
    }
    /**
     * 创建实体实例
     */
    protected abstract newInstance(): T;
    /**
     * 获取数据详情
     * @param req 请求
     * @param res 返回
     * @param next 
     */
    get(req: Request, res: Response, next: NextFunction) {
        let id = req.query.id;
        if (!id) {
            res.status(400).send({
                code: 400,
                msg: '参数为空'
            });
            return;
        }
        this.service.get(parseInt(<string>id)).then((value: DataEntity | null) => {
            res.send({
                code: 200,
                msg: '',
                data: value
            })
        }).catch((err: Error) => {
            res.status(500).send({
                code: 500,
                msg: '处理失败'
            });
        });
    }
}
  1. controller/BaseEditController.ts
import {
    NextFunction,
    Request,
    Response,
} from 'express';

import DataEntity from '../entity/DataEntity';
import BaseService from '../service/BaseService';
import BaseViewContorller from './BaseViewController';

/**
 * 数据查看
 */
export default abstract class BaseEntityContorller<T extends DataEntity> extends BaseViewContorller<T> {
    constructor(service: BaseService<T>) {
        super(service);
    }
    /**
     * 校验数据的合法性
     */
    protected abstract validate(data: T): boolean;
    /**
     * 创建实体实例
     */
    protected abstract newInstance(): T;

    /**
     * 添加数据
     * @param req 请求
     * @param res 返回
     * @param next 
     */
    save(req: Request, res: Response, next: NextFunction) {
        let param = req.body;
        if (!param) {
            res.status(400).send({
                code: 400,
                msg: '参数为空'
            });
            return;
        }
        //做数据的具体校验
        let entity: T = this.newInstance();
        if (!this.validate(Object.assign(entity, param))) {
            res.status(400).send({
                code: 400,
                msg: '参数不合法'
            });
            return;
        }
        //数据权限校验
        try {
            this.service.save(entity);
            res.send({
                code: 200,
                msg: ''
            })
        } catch (error) {
            console.error(error);
            res.status(500).send({
                code: 500,
                msg: '处理失败'
            });
        }
    }
    /**
     * 更新数据
     * @param req 请求
     * @param res 返回
     * @param next 
     */
    update(req: Request, res: Response, next: NextFunction) {
        let param = req.body;
        if (!param) {
            res.status(400).send({
                code: 400,
                msg: '参数为空'
            });
            return;
        }
        //做数据的具体校验
        let entity: T = this.newInstance();
        if (!this.validate(Object.assign(entity, param))) {
            res.status(400).send({
                code: 400,
                msg: '参数不合法'
            });
            return;
        }
        //数据权限校验
        try {
            this.service.update(entity);
            res.send({
                code: 200,
                msg: ''
            })
        } catch (error) {
            console.error(error);
            res.status(500).send({
                code: 500,
                msg: '处理失败'
            });
        }
    }

    /**
     * 删除数据
     * @param req 
     * @param res 
     * @param next 
     */
    deleteData(req: Request, res: Response, next: NextFunction) {
        let id = req.query.id;
        if (!id) {
            res.status(400).send({
                code: 400,
                msg: '参数为空'
            });
            return;
        }
        try {
            this.service.deleteData(parseInt(<string>id));
            res.send({
                code: 200,
                msg: ''
            })
        } catch (error) {
            console.error(error);
            res.status(500).send({
                code: 500,
                msg: '处理失败'
            });
        }
    }
}
  1. controller/BaseController.ts
/**
 * 接口的基类
 */
export default abstract class BaseController<T> {
}
  1. controller/system/UserController.ts
// var express = require('express');
import express from 'express';

import User from '../../entity/system/User';
import UserService from '../../service/system/UserService';
import BaseEntityContorller from '../BaseEditController';

/**
 * 用户的接口
 */
export class UserController extends BaseEntityContorller<User> {
    constructor() {
        super(new UserService());
    }
    protected validate(data: User): boolean {
        if (!data.account) {
            return false;
        }
        if (!data.name) {
            return false;
        }
        return true;
    }
    protected newInstance(): User {
        return new User();
    }
}

var router = express.Router();
const userController = new UserController();

//获取
router.get('/get', userController.get.bind(userController));
//保存
router.post('/save', userController.save.bind(userController));
//更新
router.post('/update', userController.update.bind(userController));
//删除
router.get('/delete', userController.deleteData.bind(userController));

// module.exports = router;
export default router;
  1. controller/register/RegisterController.ts
// var express = require('express');
import express from 'express';
import svgCaptcha from 'svg-captcha';

import { SYSTEM } from '../../config/Config';
import User from '../../entity/system/User';
import UserService from '../../service/system/UserService';
import BaseController from '../BaseController';

/**
 * 登陆接口
 */
export class RegisterController extends BaseController<null> {
    /**
     * 用户服务
     */
    useService: UserService;
    constructor() {
        super();
        this.useService = new UserService();
    }
    /**
     * 生成验证码
     */
    captcha(req: express.Request, res: express.Response, next: express.NextFunction) {
        var captcha = svgCaptcha.create();
        req.session.captcha = captcha.text;
        req.session.captchaData = new Date().getTime();
        res.type('svg');
        res.status(200).send(captcha.data);
    }
    /**
     * 用户注册功能
     */
    async register(req: express.Request, res: express.Response, next: express.NextFunction) {
        /**
         * 1.检测参数是否合法,账号、密码、验证码等不能为空
         * 2.校验验证码是否合法:验证码是否正确、验证码是否过期
         * 3.检测账号是否重复
         * 4.保存账号信息
         */
        try {
            //验证码校验
            let captcha = req.body.captcha;
            if (!captcha) {
                res.status(400).send({
                    code: 400,
                    msg: '验证码不能为空'
                })
                return;
            }
            //参数校验
            let user: User = new User();
            user.account = req.body.account;
            user.name = req.body.name;
            user.password = req.body.password;
            user.sex = req.body.sex;
            user.phone = req.body.phone;
            if (!user || !user.account || !user.name || !user.password || !user.phone) {
                res.status(400).send({
                    code: 400,
                    msg: '参数不合法'
                })
                return;
            }
            //验证码校验
            let systemCaptcha = req.session.captcha;
            let systemCaptchaDate = req.session.captchaData;
            if (!systemCaptcha || !systemCaptchaDate) {
                res.status(500).send({
                    code: 500,
                    msg: '验证码不合法'
                })
                return;
            }
            if (systemCaptcha !== captcha) {
                res.status(500).send({
                    code: 500,
                    msg: '验证码错误'
                })
                return;
            }
            if (new Date().getTime() - systemCaptchaDate > SYSTEM.captchaTimeOut) {
                res.status(500).send({
                    code: 500,
                    msg: '验证码已失效'
                })
                return;
            }
            //账号校验
            let systemUser = await this.useService.findByAccount(user.account);
            if (systemUser && systemUser.id) {
                res.status(500).send({
                    code: 500,
                    msg: '改账号已注册,无法重复注册'
                })
                return;
            }
            //电话号码检测
            systemUser = await this.useService.findByPhone(user.phone);
            if (systemUser && systemUser.id) {
                res.status(500).send({
                    code: 500,
                    msg: '改账号已注册,无法重复注册'
                })
                return;
            }
            this.useService.save(user);
            res.send({
                code: 200,
                msg: '注册成功'
            });
        } catch (error) {
            console.error(error);
            res.status(503).send({
                code: 503,
                msg: error
            })
            return;
        }
    }
}

var router = express.Router();
const registerController = new RegisterController();

//注册接口
router.post('/register', registerController.register.bind(registerController));

//获取验证码
router.get('/register/captcha', registerController.captcha.bind(registerController));
// module.exports = router;
export default router;
  1. controller/login/LoginController.ts
import bcrypt from 'bcryptjs';
// var express = require('express');
import express from 'express';
import jwt from 'jsonwebtoken';
import svgCaptcha from 'svg-captcha';

import { SYSTEM } from '../../config/Config';
import UserService from '../../service/system/UserService';
import BaseController from '../BaseController';

/**
 * 登陆接口
 */
export class LoginController extends BaseController<null> {
    /**
     * 用户服务
     */
    useService: UserService;
    constructor() {
        super();
        this.useService = new UserService();
    }
    /**
     * 生成验证码
     */
    captcha(req: express.Request, res: express.Response, next: express.NextFunction) {
        var captcha = svgCaptcha.create();
        req.session.captcha = captcha.text;
        req.session.captchaData = new Date().getTime();
        res.type('svg');
        res.status(200).send(captcha.data);
    }
    /**
     * 登陆操作
     */
    async login(req: express.Request, res: express.Response, next: express.NextFunction) {
        /**
         * 1.检测参数是否合法,账号、密码、验证码等不能为空
         * 2.校验验证码是否合法:验证码是否正确、验证码是否过期
         * 3.账号密码是否合法的校验
         */
        let captcha = req.body.captcha;
        let account = req.body.account;
        let password = req.body.password;
        if (!captcha || !account || !password) {
            res.status(400).send({
                code: 400,
                msg: '参数为空'
            })
            return;
        }
        //验证码合法性的判断"wBxqQKlIOWlecyawht2W7dJTCI9DD_qC"
        let systemCaptcha = req.session.captcha;
        let systemCaptchaDate = req.session.captchaData;
        if (!systemCaptcha || !systemCaptchaDate) {
            res.status(500).send({
                code: 500,
                msg: '验证码不合法'
            })
            return;
        }
        if (systemCaptcha !== captcha) {
            res.status(500).send({
                code: 500,
                msg: '验证码错误'
            })
            return;
        }
        if (new Date().getTime() - systemCaptchaDate > SYSTEM.captchaTimeOut) {
            res.status(500).send({
                code: 500,
                msg: '验证码已失效'
            })
            return;
        }
        //账号密码合法性的判断
        let systemUser = await this.useService.findByAccount(account).catch(error => {
            console.error(error);
            res.status(503).send({
                code: 503,
                msg: '系统错误:' + error.message
            })
        });
        if (!systemUser) {
            res.status(500).send({
                code: 500,
                msg: '账号错误'
            })
            return;
        }
        if (!bcrypt.compareSync(password, systemUser.password)) {//TODO 需进行加密判断
            res.status(500).send({
                code: 500,
                msg: '密码错误'
            })
            return;
        }
        //生成token,并且返回登陆成功信息
        let token = jwt.sign({
            id: systemUser.id,
            name: systemUser.name
        }, SYSTEM.privateKey, {
            expiresIn: '1d' // 1天 
        });
        res.header("authorization", token);
        res.send({
            code: 200,
            msg: ''
        })
    }
}

var router = express.Router();
const loginController = new LoginController();

//登陆接口
router.post('/login', loginController.login.bind(loginController));

//获取验证码
router.get('/login/captcha', loginController.captcha.bind(loginController));
// module.exports = router;
export default router;
  1. config/Config.ts
/**
 * 数据库配置
 */
export class DB {
    static host = 'localhost';
    static port = 3306;
    static user = "root";
    static password = 'Root123456*';
    static database = 'expressdemo';
    static charset = 'utf8mb4'
}
/**
 * 系统的一些配置项
 */
export class SYSTEM {
    /**
     * 验证码的超时时间
     */
    static captchaTimeOut = 3 * 60 * 1000;
    /**
     * token加密的密钥
     */
    static privateKey = "junjunjun006"
}

五、接口列表

序号接口请求方式备注
1/loginPOST登录接口
2/login/captchaGET获取登录验证码接口
3/registerPOST用户注册接口
4/register/captchaGET获取用户注册验证码接口
5/getGET加载用户详情
6/savePOST保存用户信息
7/updatePOST更新用户信息
8/deleteGET删除用户信息

六、问题汇总

问题一: session设置中的cookie的secure设置为true时,cookie只有在https时生效,导致postman测试接口时出现session不一致的问题。将cookie中的secure设置为false即可。
问题二: 数据库保存时id设置,可以设置为数据库自增长或者使用uuid,也可以手动查询最新id,并且进行id的更新,但这种方式不建议。
问题三: 使用async及await时,需要返回Promise类型才可以,不然ts编译器会报错。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1503481.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式-行为型模式-观察者模式

观察者模式定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时&#xff0c;会通知所有观察者对象&#xff0c;使它们能够自动更新自己。[DP] //首先是Observer接口&#xff0c;定义了观察者需要实现的更新方法&…

【Claude 3】一文谈谈Anthropic(Claude) 亚马逊云科技(Bedrock)的因缘际会

文章目录 前言1. Anthropic的诞生2. Anthropic的“代表作”——Claude 3的“三驾马车”3. 亚马逊云科技介绍4. 强大的全托管服务平台——Amazon Bedrock5. 亚马逊云科技(AWS)和Anthropic的联系6. Claude 3模型与Bedrock托管平台的关系7. Clude 3限时体验入口分享【⚠️截止3月1…

[贰],万能开篇HelloWorld

1&#xff0c;新建项目 File/New/Project Android/Android Application Project 输入程序名字HelloWorld Next Next 选择Blank Activity 修改为HelloWorldActivity 2&#xff0c;异常点 No resource found that matches the given name Theme.AppCompat.Light import andro…

c++中string的使用!!!(适合初学者 浅显易懂)

我们先初步的认识一下string,string底层其实是一个模版类 typedef basic_string<char> string; 我们先大致的把string的成员函数列举出来 class string { private: char * str; size_t size; size_t capacity; }; 1.string的六大默认函数 1.1 构造函数、拷贝构造 注&am…

Hadoop生态选择(一)

一、项目框架 1.1技术选型 技术选型主要考虑因素:维护成本、总成本预算、数据量大小、业务需求、行业内经验、技术成熟度。 数据采集传输:Flume&#xff0c;Kafka&#xff0c;DataX&#xff0c;Maxwell&#xff0c;Sqoop&#xff0c;Logstash数据存储:MySQL&#xff0c;HDFS…

【数据库】多表查询:子查询|关联查询 inner join 、left join、right join

一、外键&#xff1a; 就是把一张表的主键拿到另一张表中作为一个普通的字段 通过外键 可以把两张表连接起来 比如&#xff0c;下面【部门】表里的主键 拿到【员工】表里做普通字段&#xff08;外键&#xff09; 员工 部门 1员工&#xff0c;XXX&#xff0c;1部门 1部门&a…

CraxsRat7.4 安卓手机远程管理软件

CRAXSRAT 7.4 最新视频 https://v.douyin.com/iFjrw2aD/ 官方网站下载 http://craxsrat.cn/ 不要问我是谁&#xff0c;我是活雷锋。 http://craxsrat.cn/ CraxsRat CraxsRat7 CraxsRat7.1 CraxsRat7.2 CraxsRat7.3 CraxsRat7.4

[java基础揉碎]super关键字

super关键字: 基本介绍 super代表父类的引用&#xff0c;用于访问父类的属性、方法、构造器 super给编程带来的便利/细节 1.调用父类的构造器的好处(分工明确&#xff0c;父类属性由父类初始化&#xff0c;子类的属性由子类初始化) 2.当子类中有和父类中的成员(属性和方法)重…

R语言更新版本

目录 一、更新R语言 1、安装最新的R语言版本 2、移动之前安装的packages 3、将Rstudio连接到最新的R语言 二、Rstudio更新 一、更新R语言 1、安装最新的R语言版本 查看当前R语言版本&#xff1a; R.version.string 下载最新的R语言安装包&#xff1a;R: The R Project…

链表|19.删除链表的倒数第N个节点

力扣题目链接 struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {//定义虚拟头节点dummy 并初始化使其指向headstruct ListNode* dummy malloc(sizeof(struct ListNode));dummy->val 0;dummy->next head;//定义 fast slow 双指针struct ListNode* f…

SkyWalking链路追踪上下文TraceContext的traceId生成的实现原理剖析

结论先行 【结论】 SkyWalking通过字节码增强技术实现&#xff0c;结合依赖注入和控制反转思想&#xff0c;以SkyWalking方式将追踪身份traceId编织到链路追踪上下文TraceContext中。 是不是很有趣&#xff0c;很有意思&#xff01;&#xff01;&#xff01; 【收获】 skywal…

CSS的盒子模型:掌握网页设计的基石!

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

C#学习:初识各类应用程序

编写我们第一个程序——Hello,World! 1.编程不是“学”出来的&#xff0c;而是“练”出来的 2.在反复应用中积累&#xff0c;忽然有一天就会顿悟 3.学习原则&#xff1a; 3.1从感官到原理 3.2从使用别人的到创建自己的 3.3必需亲自动手 3.4必需学以致用&#xff0c;紧跟实际…

P4551 最长异或路径

最长异或路径 题目描述 给定一棵 n n n 个点的带权树&#xff0c;结点下标从 1 1 1 开始到 n n n。寻找树中找两个结点&#xff0c;求最长的异或路径。 异或路径指的是指两个结点之间唯一路径上的所有边权的异或。 输入格式 第一行一个整数 n n n&#xff0c;表示点数…

机器人扫地 二分答案

#include<bits/stdc.h> using namespace std; int i,n,a[100009],k,ans0; bool check(int mid){int pos0,t;//pos前面清扫过的位置for(i0;i<k;i){ //已经清扫的位置还没到当前机器人的位置a[i]//一个位置机器人是要去回&#xff0c;一个格子消耗两个时间 tmid;//贪心…

turtle绘制小猪佩奇

turtle绘制小猪佩奇 import turtle as t 使用python的turtle绘制小猪佩奇 # 设置线条粗细为4 t.pensize(4) # 使海龟不可见 #t.hideturtle() # 后续表示三原色的r,g,b必须在0~255之间 t.colormode(255) # 设置画笔颜色和填充颜色 t.color((255, 155, 192), "pink")…

记录汇川:IO隔离编程

IO隔离&#xff1a;方便程序修改 无论是输入点坏了还是输出点坏了&#xff0c;或者人为接错线&#xff0c;或者对调点&#xff0c;我们只需要更改IO隔离得输入输出就可以了。方便。 停止按钮外接常闭&#xff0c;里面也使用常闭&#xff0c;为了断线检测功能(安全)&#xff…

【vue2基础教程】vue指令

文章目录 前言一、内容渲染指令1.1 v-text1.2 v-html1.3 v-show1.4 v-if1.5 v-else 与 v-else-if 二、事件绑定指令三、属性绑定指令总结 前言 Vue.js 是一款流行的 JavaScript 框架&#xff0c;广泛应用于构建交互性强、响应速度快的现代 Web 应用程序。Vue 指令是 Vue.js 中…

职场成功的秘诀:如何高效管理时间

在职场中&#xff0c;时间管理是一项至关重要的技能。高效的时间管理不仅能够提高工作效率&#xff0c;还能够帮助我们更好地平衡工作与生活&#xff0c;实现职场成功。本文将分享一些职场成功人士都在使用的时间管理秘诀&#xff0c;帮助你更好地管理时间&#xff0c;提升职场…

Linux上安装torch-geometric(pyg)1.7.2踩坑记录

重点&#xff1a;1.一定要在创建虚拟环境的时候设置好python版本。2.一定要先确定使用1.X还是2.X的pyg库&#xff0c;二者不兼容。3.一定要将cuda、torch、pyg之间的版本对应好。所以&#xff0c;先确定pyg版本&#xff0c;再确定torch和cuda的版本。 结论&#xff1a;如果在u…