express+mySql实现用户注册、登录和身份认证
注册
注册时需要对用户密码进行加密入库,提高账户的安全性。用户登录时再将密码以相同的方式进行加密,再与数据库中存储的密码进行比对,相同则表示登录成功。
安装加密依赖包bcryptjs
javascript
cnpm install -S bcryptjs
在注册接口中添加加密功能
javascript
// 引入对密码进行加密的包
const bcryptjs = require("bcryptjs");
class User {
register(req, res) {
let { username, nick_name, password } = req.body;
// 先查找注册的用户名是否在数据库中已存在
const sql = "select * from sys_user where user_name=?";
pool.query(sql, username, (err, results) => {
if (err) return res.sendError(err);
// 找到了要注册的用户名
if (results.length >= 1) return res.sendError("当前用户名已被占用!");
// 对密码进行加密,第二个参数可以提高密码的安全性为任意数字
const password1 = bcryptjs.hashSync(password, 10);
const SQl = `Insert into sys_user (user_name,password,nick_name) values('${username}', '${password1}', '${nick_name}')`
pool.query(SQl, (err, data) => {
if (err) return res.sendError(err);
if (data.affectedRows !== 1) {
res.sendError('用户注册失败"');
} else {
res.sendSuccess(data);
}
})
})
}
}
效果展示
登录
安装加密依赖包jsonwebtoken
jsonwebtokenuaokeyi生成用户登录需要的token信息。
javascript
cnpm install -S jsonwebtoken
增加全局token配置文件
在项目根目录的config文件夹下新增taken.js文件,并加入如下配置。
javascript
// 全局的配置文件
module.exports = {
// 设置token加密和解密用到的密钥
jwtSecretKey: 'qwertyuiop',
// 设置token的有效期
expiresIn: '10h',
}
在登录接口中返回taken信息
javascript
// 导入jsonwebtoken
const jwt = require("jsonwebtoken");
// 导入全局配置文件
const taken = require("../config/taken");
class User {
login(req, res) {
const { username, password } = req.body;
// 先查找用户名是否在数据库中,定义sql语句
const sql = "select * from sys_user where user_name=?";
pool.query(sql, username, (err, results) => {
if (err) return res.sendError(err);
if (results.length !== 1) return res.sendError("当前用户不存在!");
// 比较密码 compareSync(客户端的密码,数据库中存储的经过加密后的密码)会返回true或false
const compareResult = bcryptjs.compareSync(password, results[0].password);
if (!compareResult) {
return res.sendError("用户密码错误,登录失败!");
}
// 密码比对正确,在服务端根据用户信息(用户密码需置空)生成token信息
const user = { ...results[0], password: "" };
// 对用户的信息进行加密,生成token字符串
const tokenStr = jwt.sign(user, taken.jwtSecretKey, {
expiresIn: taken.expiresIn,
});
// 调用res.send将token响应给客户端
res.sendSuccess("Bearer " + tokenStr)
})
}
}
效果展示
配置系统白名单
白名单是指那些接口不需要提供token信息。
安装解析token的依赖包express-jwt
express-jwt 包需要使用和生成token时相同的密钥。
javascript
cnpm install -S express-jwt
修改app.js文件设置系统白名单
javascript
// 在路由之前配置解析token的中间件
const { expressjwt: jwt } = require("express-jwt")
// 解析token时需要token的密钥
const taken = require("./config/taken");
// 定义中间件,
// .unless指定哪些接口不需要进行token身份认证(过滤掉swagger页面和login接口)
app.use(
jwt({ secret: taken.jwtSecretKey, algorithms: ["HS256"] }).unless({
path: [/^\/api-docs/, '/user/login', '/user/register'],
})
)
效果展示
在其他接口中通过token获取具体的用户信息
在接口中通过req.auth中获取:具体代码如下:
javascript
class mineSeam {
getAll(req, res) {
console.log(req.auth)
}
}
app.js全部代码如下
javascript
// 引入express
const express = require("express");
const path = require('path');
const router = require('./routes/index.js');
// 创建服务器的实例对象
const app = express();
// 配置解析表单数据的中间件,内置中间件只能解析application/x-www-form-urlencoded格式的数据
app.use(express.urlencoded({ extended: false }));
// 搭建静态文件服务
app.use(express.static(path.join(__dirname, 'public')));
// 引入swagger配置项
const swaggerSpec = require('./config/swagger')
app.get('/swagger.json', function(req, res) {
res.setHeader('Content-Type', 'application/json');
res.send(swaggerSpec);
});
/**
* 在路由之前封装res.send()
*/
app.use((req, res, next) => {
// 定义一个输出的函数
res.sendError = function (err) {
res.send({
code: 400,
msg: err instanceof Error ? err.message : err
})
}
// 定义一个输出的函数
res.sendSuccess = function (data = null) {
res.send({
code: 200,
msg: '成功',
data
})
}
next();
})
// 在路由之前配置解析token的中间件
const { expressjwt: jwt } = require("express-jwt");
// 解析token需要token的密钥
const taken = require("./config/taken");
// 定义中间件,需要哪个密钥解析
// algorithms:设置jwt的算法
// .unless指定哪些接口不需要进行token身份认证
app.use(
jwt({ secret: taken.jwtSecretKey, algorithms: ["HS256"] }).unless({
path: [/^\/api-docs/, '/user/login', '/user/register'],
})
)
// 引入路由
router(app);
// 引入校验规则的包,在定义错误级别的中间件时会用到
const joi = require('joi')
// 在所有路由下面调用错误级别的中间件
app.use((err, req, res, next) => {
// 验证失败导致的错误
if (err instanceof joi.ValidationError) return res.sendError(err);
// 未知的错误
res.sendError(err);
next();
})
// 启动服务器,3007为端口号,选择一个空闲的端口号
app.listen(3007, () => {
console.log("Server running at http://127.0.0.1:3007");
})