本文由图雀社区认证作者 布拉德特皮 写作而成,点击阅读原文查看作者掘金链接,感谢作者的优质输出,让我们的技术世界变得更加美好????
前言
上一篇介绍了如何使用 Sequelize 连接 MySQL,接下来,在原来代码的基础上进行扩展,实现用户的注册和登录功能。
这里需要简单提一下两个概念 JWT 和 单点登录:
JWT
JWT(JSON Web Token)是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519)。该 Token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 Token 也可直接被用于认证,也可被加密。
具体原理可以参考《JSON Web Token 入门教程 \- 阮一峰》[1]
单点登录
单点登录(Single Sign On),简称为 SSO,是比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
所以 JWT 实现【单点登录】的大致流程是:
客户端用户进行登录请求;
服务端拿到请求,根据参数查询用户表;
若匹配到用户,将用户信息进行签证,并颁发 Token;
客户端拿到 Token 后,存储至某一地方,在之后的请求中都带上 Token ;
服务端接收到带 Token 的请求后,直接根据签证进行校验,无需再查询用户信息;
下面,就开始我们的实战:
GitHub 项目地址[2],欢迎各位大佬 Star。
一、编写加密的工具函数
在 src
目录下,新建文件夹 utils
,里面将存放各种工具函数,然后新建 cryptogram.ts
文件:
import * as crypto from 'crypto';
/**
* Make salt
*/
export function makeSalt(): string {
return crypto.randomBytes(3).toString('base64');
}
/**
* Encrypt password
* @param password 密码
* @param salt 密码盐
*/
export function encryptPassword(password: string, salt: string): string {
if (!password || !salt) {
return '';
}
const tempSalt = Buffer.from(salt, 'base64');
return (
// 10000 代表迭代次数 16代表长度
crypto.pbkdf2Sync(password, tempSalt, 10000, 16, 'sha1').toString('base64')
);
}
上面写了两个方法,一个是制作一个随机盐(salt),另一个是根据盐来加密密码。
这两个函数将贯穿注册和登录的功能。
二、用户注册
在写注册逻辑之前,我们需要先修改一下上一篇写过的代码,即 user.service.ts
中的 findeOne()
方法:
// src/logical/user/user.service.ts
import { Injectable } from '@nestjs/common';
import * as Sequelize from 'sequelize'; // 引入 Sequelize 库
import sequelize from '../../database/sequelize'; // 引入 Sequelize 实例
@Injectable()
export class UserService {
/**
* 查询是否有该用户
* @param username 用户名
*/
async findOne(username: string): Promise<any | undefined> {
const sql = `
SELECT
user_id userId, account_name username, real_name realName, passwd password,
passwd_salt salt, mobile, role
FROM
admin_user
WHERE
account_name = '${username}'
`; // 一段平淡无奇的 SQL 查询语句
try {
const user = (await sequelize.query(sql, {
type: Sequelize.QueryTypes.SELECT, // 查询方式
raw: true, // 是否使用数组组装的方式展示结果
logging: true, // 是否将 SQL 语句打印到控制台
}))[0];
// 若查不到用户,则 user === undefined
return user;
} catch (error) {
console.error(error);
return void 0;
}
}
}
现在,findOne()
的功能更符合它的方法名了,查到了,就返回用户信息,查不到,就返回 undefined
。
接下来,我们开始编写注册功能:
// src/logical/user/user.service.ts
import { Injectable } from '@nestjs/common';
import * as Sequelize from 'sequelize'; // 引入 Sequelize 库
import sequelize from '../../database/sequelize'; // 引入 Sequelize 实例
import { makeSalt, encryptPassword } from '../../utils/cryptogram'; // 引入加密函数
@Injectable()
export class UserServic