node.js入门笔记(六)——mongoDB数据库的基本使用、JWT、socket编程

node.js入门笔记(六)

      • 1.MongDB的使用
        • 1.1MongDB的安装
        • 1.2数据库的基本操作指令
        • 1.3数据集合(表)的基本操作指令
        • 1.4文档(记录)的基本操作指令
      • 2.JWT的使用
      • 3.socket编程
        • 3.1原生socket编程
        • 3.2WebSocket第三方依赖
        • 3.3socket.io的使用

1.MongDB的使用

    如果你玩过微信小程序的云开发或者学过MySQL,这玩意儿相对要好学很多。作为非关系型数据库,MongDB由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongDB其实使用面还是比较广的,每条记录有点像json数据,一般和node.js在一起作为后端架构,在Java中也有一定的使用空间(主要用于存储可丢失的数据)。好,简单介绍完毕,接下来就是基本的使用介绍了。

1.1MongDB的安装

    首先自然是(从官网下载)了,应该需要注册啥的,这里用的是社区版,企业版应该会收费,学习的话用社区版应该足够了。下载时,选择custom,修改文件路径到E盘,节省C盘空间。下载完成之后配置环境变量(eg.E:\MongoDB\bin)。配置完成之后:
node.js入门笔记(六)——mongoDB数据库的基本使用、JWT、socket编程_第1张图片

1.2数据库的基本操作指令
//查询数据库
show dbs

//创建或者切换数据库
use 数据库名称

//查看当前使用的数据库
db/db.getName()

//显示当前的数据库状态
db.stats()

//查看当前的数据库版本
db.version()

//查看当前数据库连接的主机地址
db.getMongo()

//删除数据库
db.dropDatabase()
1.3数据集合(表)的基本操作指令
//创建集合
db.createCollection("collName",{size:20,capped:true,max:100})
//创建一个名为collName的集合,大小为20(字节,capped表示固定集合,即使是true也需要指定size的值),
//max表示的是集合中包含文档的最大数量,也就是MySQL中常说的一条记录。

//展示当前数据库下面的所有集合
db.getCollectionNames()

//展示当前数据库所有集合的状态
db.printCollectionStats()

//删除集合
db.collection名称.drop()
1.4文档(记录)的基本操作指令
//假定被操作的集合名为test

//插入数据
db.test.insert({name:"张三",age:22})
//注意,save就是insert的别名,和thinkPHP中的save,insert不一样。

//数据修改
db.test.update({name:"张三",age:22},{$set:{age:19}});
db.test.update({name:"张三",age:22},{$inc:{age:100}});
//对于数据的修改,第一个表示的是匹配文档
//第二个参数是修改内容,其中$set表示设置成新的值,$inc表示值的增加
//第三个参数表示如果没有找到数据就创建数据(默认为false)
//第四个参数表示的是修改全部匹配的文档(默认为false)

//数据删除
db.test.remove({data:1},false)
//注意,这里的集合不能是固定集合(固定集合不能删除数据)
//第二个参数是是否只删除一条数据(默认为true)

//数据查询
db.test.find()//查询所有数据
db.test.distinct('name')//去重查询
db.test.find({data:1})//基本条件查询
db.test.find({data:{$gt:1}})//查询data>1
db.test.find({data:{$gte:1}})//查询data>=1
db.test.find({data:{$lt:1}})//查询data<1
db.test.find({data:{$gt:1,$lt:5}})//查询(1,5)
db.test.find({name:/nightowl/})//查询name中包含有nightowl的数据
db.test.find({name:/^nightowl/})//查询name中以nightowl开头的数据
db.test.find({name:/nightowl$/})//查询name中以nightowl结尾的数据
db.test.find({},{name:0,_id:0})//查询并返回的数据中去除name和_id字段
db.test.find().sort(data:1)//查询并按照data升序排序
db.test.find().sort(data:-1)//查询并按照data降序排序
db.test.find().limit(5)//查询并限制返回数据的条数
db.test.find().skip(3)//查询并跳过指定数据条数
db.test.find({$or:[data:{$gt:1},data:{$lte:-3}]})//查询data(-∞,-3]和(1,+∞]的文档
db.test.findOne()//查询第一条数据
db.test.find({条件}).count()//查询并得到符合条件的文档条数

2.JWT的使用

    首先,简单说一下JWT(Json Web Token),主要应用场景是前后端鉴权。为了保护数据库里面的数据,避免非法用户攻击,前端首次登录之后,后端会利用某种加密算法,将用户名(或者其他能够唯一标识用户的信息,当然为了提高安全性,也可能涉及其他信息)生成一个token(这里的token是一般使用的代号,其他名字也可以),将token发送给前端,然后前端在请求数据的时候需要携带token信息,后端会再次加密这些信息,将两次token进行验证,验证通过才进一步调用model层获取数据库里面的数据或者向数据库里面写入数据。
    当然更深入的,后端还可以利用像redis一样的高速缓存工具,快速验证前端传过来的token。其基本是实现原理是前端第一次登录,将生成的token存入redis,之后用户再次请求,直接查找redis是否含有先前生成好的token,有便通过验证。下面主要介绍利用JWT实现的对称加密和非对称加密。
    首先是对称加密,为了方便更加熟悉RMVC模式(router、model、view、controller),我们回顾一下前面搭建目录的过程:

//server.js导入各种模块
const express = require("express");
const app = express();
const router = require("./route/index");
app.use("/", router);
app.listen(3000, () => {
    console.log("localhost:3000.");
});
//route/index.js配置路由模块
const express = require("express");
const router = express.Router();
const loadToken = require("../controller/token");
router.get("/api/token", loadToken);
module.exports = router;
//controller/index.js配置controller控制层
const jwt = require("jsonwebtoken");
const token = (req, res, next) => {
    const loadToken = jwt.sign({ username: "张三、李四、王五牛逼!**nightowl**" },
        "nightowl"
    );
    //const result = jwt.verify(loadToken, "nightowl");
    res.send(loadToken);
};
module.exports = token;

    补充说明,这里需要导入的模块有:express、express-generator(前两者主要是为了引入express极简框架)、jsonwebtoken(引入JWT加密模块)、nodemon(项目热加载,省得每次修改代码之后需要重启)。这里使用的是对称加密,jwt.sgin()方法含有两个参数,第一个参数是被加密的数据,第二个参数是加密的私钥(补充:所谓的对称加密指的是加密和解密用的私钥和密钥是一样的)。这里介绍一个(JWT官网),可以用来解密JWT生成的数据。解密效果如下:
node.js入门笔记(六)——mongoDB数据库的基本使用、JWT、socket编程_第2张图片
    其中的蓝色部分是私钥,又被成为数字签证,可以通过右边的输入框来更改。当然jwt自身提供了解密方法jwt.verify(),这种对称加密其实不算安全,因为如果别人知道了密钥,信息就很容易泄露,加上公钥与私钥一直,增大了泄露的风险。
    接下来说一下非对称加密。非对称加密有点类似于git的环境搭建,首先需要生成私钥和公钥,然后将公钥上传到自己的git远程仓库上。在jsonwebtoken中,非对称加密的实现方法为:
(1)(下载openSSL)(用来生成签证);
(2)在cmd中执行 genrsa -out rsa_private_key.pem 2048,生成2048位私钥;
(3)执行rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem生成公钥;
生成完成之后的文件如下所示:
node.js入门笔记(六)——mongoDB数据库的基本使用、JWT、socket编程_第3张图片
    之后修改对应的加密算法:

//controller/token.js
const jwt = require("jsonwebtoken");
const fs = require("fs");
const path = require("path");
const token = (req, res, next) => {
    const privateKey = fs.readFileSync(
        path.join(__dirname, "../rsa_private_key.pem")
    );
    const publicKey = fs.readFileSync(
        path.join(__dirname, "../rsa_public_key.pem")
    );
    const loadToken = jwt.sign({ username: "张三" }, privateKey, {
        algorithm: "RS256",
    });
    const result = jwt.verify(loadToken, publicKey);
    console.log("输出解密之后的数据:", result);
    res.send(loadToken);
};
module.exports = token;

    程序运行结果如下:
node.js入门笔记(六)——mongoDB数据库的基本使用、JWT、socket编程_第4张图片
    里面展示的时间iat其实就是时间戳,对应当前密文生成的时间。在策略上,可以将公钥发送给后端,当前端上前数据时先用私钥加密,等数据到达后端之后,利用后端传入的公钥解密数据或者两次加密数据看数据是否相等。

3.socket编程

3.1原生socket编程

    socket编程更偏向于计算机网络数据传输方面的底层知识,这里可以作为了解内容,因为现在框架对于socket的封装程度已经比较高了。数据传输分为双工通信、单工通信和半双工通信,其中单工通信就类似于数码电视,只接受另外一段传输过来的数据,双工通信就类似于QQ,信息的接受和发送能够同时进行,半双工通信类似于老式手机“大哥大”,需要一段给出回应才能够继续发送。接下来简单说说如果撤掉各种框架的封装,使用最底层的socket如何实现数据通信:
socket属于node.js中内置模块Net模块,程序举例如下:

//server.js
const net = require("net");
const server = net.createServer((socket) => {//创建服务端,创建之后像连接的客户端发送信息
    socket.write("hello client!");
    socket.on("data", (chunk) => {
        console.log("来自客户端的数据:", chunk.toString());
    });
});
server.on("error", (err) => {
    console.log("服务端错误", err);
});
server.listen("8080", () => {//设置服务端对外开放的端口号
    console.log("opened server on:", server.address());
});
//client.js
const net = require("net");
const client = net.createConnection({ port: 8080 }, () => {
//建立连接,这里使用的主机一致,所以不涉及ip信息
    console.log("connect to server!");
    client.write("hello server!");
});
client.on("data", (chunk) => {
    console.log("来自服务端的数据:", chunk.toString());
});
client.on("error", (err) => {
    console.log("客户端错误", err);
});

    程序执行效果如下:
在这里插入图片描述
对于聊天室的模拟,程序实现如下:

//server.js
const net = require("net");
const server = new net.createServer();
let clients = {};
let clientName = 0;

server.on("connection", (client) => {
    client.name = ++clientName;
    console.log("客户端NO", client.name, "获得与server的连接!");
    clients[client.name] = client;
    client.on("data", (msg) => {
        broadcast(client, msg.toString());
    });
    //绑定数据监听,每有数据获取之后广播
    client.on("close", () => {
        delete clients[client.name];
        console.log("客户端NO" + client.name, "与server断开连接!");
    });
});

function broadcast(client, msg) {
    for (var key in clients) {
    	if(key!=client.name){
    		 clients[key].write("客户端NO" + client.name + "说:" + msg);
    	}
    }
}
server.listen(8080, () => {
    console.log("server 开启!");
});
//client.js
const net = require("net");
const readline = require("readline");
let socket = new net.Socket();
const port = 8080;
const host = "127.0.0.1";

socket.setEncoding = "UTF-8";
socket.connect(port, host, () => {
    socket.write("hello.");
});
socket.on("data", (msg) => {
    console.log(msg.toString());
    say();
});
const r1 = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
});

function say() {
    r1.question("请输入向server传输的数据:\n", (inputMsg) => {
        if (inputMsg != "bye") {
            socket.write(inputMsg + "\n");
        } else {
            socket.destroy();
            r1.close();
        }
    });
}
socket.on("error", (err) => {
    console.log("client error", err);
    socket.end();
});

    难点,数据之间的传输,一个传输对应一个监听处理,坑点:wirte()函数不能直接传输对象,因此不能使用,来凭借多个字符串。
实现效果如下:
node.js入门笔记(六)——mongoDB数据库的基本使用、JWT、socket编程_第5张图片

3.2WebSocket第三方依赖

    除了接近底层外,socket中我们能够发现一个特点,server端可以直接向服务端发送数据,也就可以解决消息推送等问题,实现真正意义的双工通信,http是不能够做到这一点的,http协议下只能由client请求。讲道理我之前还没深入到WebSocket,可以看看(阮老师的博文),看到WebSocket之后才发现自己前端也就学了点皮毛,WebSocket的协议标识为ws,加密之后用的是wss,无同源策略。现在还学的比较少,应该用不上这么多,但是需要知道有这个东西。需要注意的是web浏览器中由WebSocket,在node.js中如果需要用到WebSocket,需要使用第三方依赖,安装方式:npm install ws -S,程序代码如下:
    服务端代码:

//server.js
const webSocket = require("ws");
const ws = new webSocket.Server({ port: 8080 });
let clients = {};
let num = 0;

function broadcast(client, msg) {
    for (let key in clients) {
        clients[key].send("来自客户端NO" + client.name + "的数据:" + msg);
    }
}
ws.on("connection", (client) => {
    client.name = ++num;
    clients[client.name] = client;
    client.on("open", () => {
        ws.send("来自客户端的hello");
    });
    client.on("message", (msg) => {
        console.log("来自客户端NO" + client.name + "的数据:", msg.toString());
        broadcast(client, msg);
    });
    client.on("close", () => {
        console.log("客户端NO" + client.name + "已断开连接~");
        delete clients[client.name];
    });
});
ws.on("close", () => {
    console.log("服务端已经关闭!");
});

    客户端代码:


<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>websockettitle>
    <script src="./client.js">script>
head>

<body>
    <div>我是一个websocket测试客户端div>
body>

html>
//client.js
const ws = new WebSocket("ws://localhost:8080");
ws.onopen = () => {
    console.log("client open");
    ws.send("你好呀,服务端");
};
ws.onmessage = (socket) => {
    console.log("来自服务端的数据:", socket.data);
};
ws.onerror = () => {
    console.log("客户端错误");
};
ws.onclose = () => {
    console.log("客户端关闭连接");
};

    其实这个代码比较简单,很好理解,需要注意server.js里面的client不能理解成客户端,应该理解成服务端那边用来管理与客户端会话的管理者。这个只是一个框子,我自己也没有实践过由server端主动向client端发信息,后续还得通过实际项目进一步练习。

3.3socket.io的使用

    有些浏览器不支持H5,这使得有些时候WebSocket的应用收到了一定的限制,WebSocket可以理解成socket在浏览器端应用场景下的一种优化封装,而socket.io就是对socket更加系统的优化,WebSocket,可以算作是socket.io的一个子集。如果使用socket.io的话,程序代码如下:
服务端:

//server.js
var express = require("express");
var app = express();
var server = require("http").Server(app);
var io = require("socket.io")(server, { cors: true });

io.on("connection", function(socket) {
    console.log("有客户端与服务端建立连接");
    socket.on("receive", (msg) => {
        console.log("输出服务端收到的数据:", msg);
        //向客户端发送数据
        socket.emit("message", "收到 over !");
    });
});

server.listen(8080, () => {
    console.log("localhost:8080");
});

客户端:


<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>socket.iotitle>
    <script src="./node_modules/socket.io/client-dist/socket.io.js" charset="utf-8">script>
head>

<body>
    <div>
        <input type="text" id="msg" style="width: 200px;">
    div>
    <button id="submit">点击发送表单消息button>
    <script>
        var socket = io.connect('http://127.0.0.1:8080');
        const content = document.getElementById('content')
        document.querySelector('#submit')
            .addEventListener('click', () => {
                let content = document.getElementById("msg").value;
                //向server发送数据
                socket.emit('receive', content);
            })

        socket.on('message', (msg) => {
            console.log("输出客户端收到的数据:", msg);
        })
    script>
body>

html>

需要注意的是,消息的传递都需要绑定一个信息传输标识,比如上面的"message"和"receive",如果遇到了跨域问题,直接在创建server的时候允许跨域。相比于WebSocket在写法上更加简单了
node.js入门笔记(六)——mongoDB数据库的基本使用、JWT、socket编程_第6张图片

你可能感兴趣的:(#,node.js,node.js,数据库,mysql)