0
点赞
收藏
分享

微信扫一扫

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

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

  1. B站视频: https://player.bilibili.com/player.html?aid=1300163021

  2. 西瓜视频: https://www.ixigua.com/7331627299491709477 微信截图_20240226111658.png

一、知识点

  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 微信截图_20240226111801.png
  2. 创建express项目:express tsexpress 微信截图_20240226111840.png
  3. 安装ts:npm install typescript --save 微信截图_20240226111924.png
  4. 安装ts相关依赖: (1) npm install @types/node --save-dev 微信截图_20240226112045.png (2) npm install @types/express --save-dev 微信截图_20240226112113.png
  5. 安装svg-captcha:npm install svg-captcha --save 微信截图_20240226112140.png
  6. 安装rotating-file-stream:npm install rotating-file-stream --save 微信截图_20240226112207.png
  7. 安装mysql:npm install mysql --save 微信截图_20240226112233.png npm install @types/mysql --save-dev 微信截图_20240226112257.png
  8. 安装morgan:npm install morgan --save 微信截图_20240226112426.png npm install @types/morgan --save-dev 微信截图_20240226112459.png
  9. 安装jsonwebtoken:npm install jsonwebtoken --save 微信截图_20240226112528.png npm install @types/jsonwebtoken --save-dev 微信截图_20240226112558.png
  10. 安装express-session:npm install express-session --save 微信截图_20240226112634.png npm install @types/express-session --save-dev 微信截图_20240226112700.png
  11. 安装express-jwt:npm install express-jwt --save 微信截图_20240226112743.png
  12. 安装bcryptjs:npm install bcryptjs --save 微信截图_20240226112807.png npm install @types/bcryptjs --save-dev 微信截图_20240226112834.png
  13. 安装cookie-parser:npm install cookie-parser --save 微信截图_20240226112859.png npm install @types/cookie-parser --save-dev 微信截图_20240226112921.png
  14. 使用ts-node ./bin/www启动项目,启动会报错,需要将app等文件中的代码进行调整,写成ts的写法。
  15. 项目目录结构: 微信截图_20240223172013.png

四、代码实现

  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 /login POST 登录接口
2 /login/captcha GET 获取登录验证码接口
3 /register POST 用户注册接口
4 /register/captcha GET 获取用户注册验证码接口
5 /get GET 加载用户详情
6 /save POST 保存用户信息
7 /update POST 更新用户信息
8 /delete GET 删除用户信息

六、问题汇总

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

举报

相关推荐

0 条评论