node基础,快速入门node.js(5)

165 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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的,试着创建一个差不多的项目目录。

image-20220803194825187.png

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中处理。