携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情
crypto模块
crypto模块提供加密功能,其中包括一组用于 OpenSSL 的哈希、HMAC、密码、解密、签名和验证功能的包装器。
createHash
MD5是一种常用的哈希算法,用于给任意数据一个“签名”。这个签名通常用一个十六进制的字符串表示:
const {createHash} = require('crypto');
//hash算法
const hash = createHash('md5');
hash.update("123456");
console.log(hash.digest("hex")); //e10adc3949ba59abbe56e057f20f883e
createHmac
Hmac算法也是一种哈希算法,它可以利用MD5或SHA1等哈希算法。不同的是,Hmac还需要一个密钥。
只要密钥发生了变化,那么同样的输入数据也会得到不同的签名,因此,可以把Hmac理解为用随机数“增强”的哈希算法。
const { createHmac } = require('crypto'); //引入模块
const secret = 'abcdefg'; //密钥
/**
* 'sha256': 算法名
* 'I am JiaJi':加密字段
* 'hex':16进制
**/
//以16进制返回加密后的字符串
const hash = createHmac('sha256', secret)
.update('I am JiaJi')
.digest('hex');
console.log(hash);
// Prints:
// cd532957324189d10a2bffc7c7d78f0646b97d4ebc1d434e1d4a3412219b1af0
createCipheriv
AES是一种常用的对称加密算法,加解密都用同一个密钥。crypto模块提供了AES支持,但是需要自己封装好函数,便于使用。
const crypto = require('crypto');
//对称加密算法
/**
* @description: 加密方法
* @param {*} key 加密密钥 必须为16个字符
* @param {*} iv 偏移向量 必须为16个字符
* @param {*} pwd 加密内容
* @return {*} 加密后的内容
*/
function encrypt(key, iv, pwd) {
let dep = crypto.createCipheriv("aes-128-cbc", key, iv);
return dep.update(pwd, 'binary', 'hex') + dep.final('hex');
}
/**
* @description: 解密方法
* @param {*} key 加密密钥
* @param {*} iv 偏移向量
* @param {*} cryptoed 加密后的内容
* @return {*} 解密后的内容
*/
function decrypt(key, iv, cryptoed) {
//把加密后的数据转换成Buffer对象再转换成二进制
cryptoed = Buffer.from(cryptoed, 'hex').toString('binary');
let dep = crypto.createDecipheriv("aes-128-cbc", key, iv);
return dep.update(cryptoed, 'binary', 'utf8') + dep.final('utf8');
}
//16*8 = 128因为选择的算法是128位,所以密钥必须是128位,16字节
let key = "abcdef1234567890";
let iv = "abcdef1234567890";
//密码
let pwd = "abcdef1234567890";
let cryptoed = encrypt(key, iv, pwd);
console.log("加密结果-", cryptoed);
let encrypted = decrypt(key, iv, cryptoed);
console.log("解密结果-", encrypted);
小结
介绍了三种比较常用的加密手段,加密API和算法有很多,感兴趣可以自行尝试crypto
三、路由
基础
在前端渲染数据的时候,都是不同的页面去请求不同的后端接口,然后返回对应的数据。如注册接口可能就是将用户信息插入数据库,登录接口就是验证一下数据库是否这个数据且是否正确。而所划分的这些不同的地址就可以称之为“路由”。
- 注册:/api/register
- 登录:/api/login
- 查询列表:/api/list
const http = require('http');
const server = http.createServer((req, res) => {
let myobj = new URL(req.url, "http://localhost:3000");
res.writeHead(200, { 'Content-Type': 'text/plain;charset=utf-8' });
if (req.method === 'GET') {
switch (myobj.pathname) {
case "/home":
case "/":
res.end("首页")
break;
case "/list":
res.end("列表接口")
break;
default:
res.end("404 not found")
}
} else if (req.method === 'POST') {
switch (myobj.pathname) {
case "/api/register":
res.end("注册接口")
break;
case "/api/login":
res.end("登录接口")
break;
default:
res.end("404 not found")
}
}
});
server.listen(3000, () => {
console.log("http://localhost:3000");
});
启动服务,接下来访问不同的地址就能得到不同的响应了,路由的原理差不多就酱紫
进阶
但是现在所有接口都在同一个文件,不利于开发和维护,在项目开发中,我们的项目目录结构大概长酱紫,不同的文件负责做不同的事,比如api是返回后端数据,router是返回页面html的,试着创建一个差不多的项目目录。
app.js
const http = require('http');
const Router = {};
const router = require('./router');
const api = require('./api');
/**
* @description: 合并对象函数,把所有路由对象合并在一起
* @param {*} obj 每次合并的对象
* @return {*}
*/
function use(obj) {
Object.assign(Router, obj);
}
//合并/注册路由
use(router);
use(api);
const server = http.createServer();
server.on("request", (req, res) => {
let sub_url = new URL(req.url, "http://localhost:3000/");
try {
Router[sub_url.pathname](req, res);
} catch (err) {
console.log(err);
Router['/404'](req,res);
}
res.end();
})
server.listen(3000, () => {
console.log("http://localhost:3000");
})
api.js
/**
* @description: 根据请求路径返回请求结果
* @param {*} res 响应信息
* @param {*} data 返回数据
* @param {*} type 返回数据类型
* @return {*}
*/
function render(req, res, data, type = 'application/json') {
res.writeHead(200, { 'Content-Type': `${type};charset=utf8` });
res.write(data);
}
const apiRouter = {
'/api/login': (req, res) => {
let sub_obj = new URL(req.url, 'http://localhost:3000');
let account = sub_obj.searchParams.get('account');
let password = sub_obj.searchParams.get('password');
console.log(account === "jiaji", password === "123456");
if (account === "jiaji" && password === "123456") {
render(req, res, `{
"code":200,
"msg": "登录成功"
}`);
}else{
render(req, res, `{
"code":200,
"msg": "账号或密码错误"
}`);
}
}
}
module.exports = apiRouter;
router.js
const fs = require('fs');
/**
* @description: 根据请求路径返回请求结果
* @param {*} res 响应信息
* @param {*} path 请求路径
* @param {*} type 返回数据类型
* @return {*}
*/
function render(req, res, path, type = 'text/html') {
res.writeHead(200, { 'Content-Type': `${type};charset=utf8` });
res.write(fs.readFileSync(path, "utf8"));
}
const router = {
'/': (req, res) => {
render(req, res, "./static/home.html");
},
'/home': (req, res) => {
render(req, res, "./static/home.html");
},
'/login': (req, res) => {
render(req, res, "./static/login.html");
},
'/favicon.ico': (req, res) => {
render(req, res, "./static/baidu.ico", "images/x-icon");
},
'/404': (req, res) => {
res.writeHead(404, { 'Content-Type': 'text/html;charset=utf8' });
res.write(fs.readFileSync("./static/404.html", "utf8"));
}
}
module.exports = router;
启动服务,现在访问不同的url路径就可以返回对应的信息了。
小结
路由其实很简单,就是写在一个页面的接口解耦,对应的文件做对应的事儿,最后引用到app.js文件中,访问不同的路径就到不同的router中处理。