tips:node+mysql

用户登录

在服务端可以设计一套用户登录的方法,用户可以使用这套方法来申请登录,服务端验证以后可以给用户签发一个登录凭证,下回这个用户再请求或访问应用的时候可以带着这个凭证,这样服务端就知道这个用户是谁了,用户在登录前先得注册一个账号,服务端会把用户的账号信息放在数据仓库里面保存起来,比如用户名和 hash 之后的用户密码。


创建身份验证模块

auth/auth.controller.ts
auth/auth.middleware.ts
auth/auth.service.ts
auth/auth.router.ts


定义用户登录接口

auth.controller.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import {Request,Response,NextFunction} from 'express';

/**
* 用户登录
*/
export const login = async (
request:Request,
response:Response,
next:NextFunction
)=>{
// 准备数据
const {name,password} = request.body;

//做出响应
response.send({message:`欢迎回来,${name}`});
}

auth.router.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import express, { request } from 'express';
import * as authController from './auth.controller';

const router = express.Router();

/**
* 用户登录
*/
router.post('/login',authController.login)


/**
* 导出路由
*/
export default router;

验证用户登录数据

auth.middleware.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import {Request,Response,NextFunction} from 'express';
import * as userService from '../user/user.service';

/**
* 验证用户登录数据
*/
export const validateLoginData = async(
request:Request,
response:Response,
next:NextFunction
)=>{
console.log('验证用户登录数据');

//准备数据
const {name,password} = request.body;

//验证必填数据
if(!name) return next(new Error('NAME_IS_REQUIRED'));
if(!password) return next(new Error('PASSWORD_IS_REQUIRED'));

//验证用户名
const user = await userService.getUserByName(name);
if(!user) return next(new Error("USER_DOES_NOT_EXIST"));

//下一步
next();
}

app.middleware.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 默认异常处理器
*/
export const defaultErrorHandler = (
error: any,
request: Request,
response: Response,
next: NextFunction
) => {
if (error.message) {
console.log(error.message);
}
let statusCode: number, message: string;

/**
* 处理异常
*/
switch (error.message) {
case "NAME_IS_REQUIRED":
statusCode = 400;
message = "请提供用户名";
break;
case "PASSWORD_IS_REQUIRED":
statusCode = 400;
message = "请提供用户密码";
break;
case "USER_ALREADY_EXIST":
statusCode = 409;
message = "用户名已被占用";
break;
case "USER_DOES_NOT_EXIST": //设置用户不存在
statusCode = 400;
message = "用户不存在";
break;
default:
statusCode = 500;
message = "服务暂时出了点问题~";
break;
}
response.status(statusCode).send({ message });
};

auth.router.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import express, { request } from "express";
import * as authController from "./auth.controller";
import { validateLoginData } from "./auth.middleware";

const router = express.Router();

/**
* 用户登录
*/
router.post("/login", validateLoginData, authController.login); //这里使用中间件

/**
* 导出路由
*/
export default router;

重构按用户名查找用户

user.service.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 按用户名查找用户
*/
interface GetUserOptions {
password?: boolean;
}
export const getUserByName = async (
name: string,
options: GetUserOptions = {}
) => {
//准备选项
const { password } = options;

//准备查询
const statement = `
SELECT
id,
name
${password ? ",password" : ""}
FROM user
WHERE name = ?
`;

//执行查询
const [data] = await connection.promise().query(statement, name);

//提供数据
return data[0];
};

验证用户密码是否匹配

auth.middleware.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { Request, Response, NextFunction } from "express";
import * as userService from "../user/user.service";
import bcrypt from "bcrypt";

/**
* 验证用户登录数据
*/
export const validateLoginData = async (
request: Request,
response: Response,
next: NextFunction
) => {
console.log("验证用户登录数据");

//准备数据
const { name, password } = request.body;

//验证必填数据
if (!name) return next(new Error("NAME_IS_REQUIRED"));
if (!password) return next(new Error("PASSWORD_IS_REQUIRED"));

//验证用户名
const user = await userService.getUserByName(name, { password: true });
if (!user) return next(new Error("USER_DOES_NOT_EXIST"));

//验证用户密码
const matched = await bcrypt.compare(password, user.password);
if(!matched) return next(new Error('PASSWORD_DOES_NOT_MATCH'));

//下一步
next();
};

app.middleware.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 默认异常处理器
*/
export const defaultErrorHandler = (
error: any,
request: Request,
response: Response,
next: NextFunction
) => {
if (error.message) {
console.log(error.message);
}
let statusCode: number, message: string;

/**
* 处理异常
*/
switch (error.message) {
case "NAME_IS_REQUIRED":
statusCode = 400;
message = "请提供用户名";
break;
case "PASSWORD_IS_REQUIRED":
statusCode = 400;
message = "请提供用户密码";
break;
case "USER_ALREADY_EXIST":
statusCode = 409;
message = "用户名已被占用";
break;
case "USER_DOES_NOT_EXIST":
statusCode = 400;
message = "用户不存在";
break;
case "PASSWORD_DOES_NOT_MATCH":
statusCode = 400;
message = "密码不对";
break;
default:
statusCode = 500;
message = "服务暂时出了点问题~";
break;
}
response.status(statusCode).send({ message });
};