token
思想
在服务端不需要存储用户的登录记录,全部发给客户端有客户端自己存(cookie,local)
1、客户端使用用户名跟密码请求登录
2、服务端收到请求,去验证用户名与密码
3、验证成功后,服务端会签发一个 Token(加了密的字符串),再把这个 Token 发送给客户端
4、客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
5、客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
6、服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
实现
jsonwebtoken的安装引入
let jwt = require('jsonwebtoken')
生成签名
let token = jwt.sign(payload, secretOrPrivateKey, [options, callback])
校验token
jwt.verify(token, secretOrPublicKey, [options, callback])
token删除
有客户端,负责删除
session vs token
session | token | |
---|---|---|
服务端保存用户信息 | √ | × |
避免CSRF攻击 | × | √ |
安装性 | 一般 | 高 |
多服务器粘性问题 | 存在 | 不存在 |
多服务器粘性问题
当在应用中进行 session的读,写或者删除操作时,会有一个文件操作发生在操作系统的temp 文件夹下,至少在第一次时。假设有多台服务器并且 session 在第一台服务上创建。当你再次发送请求并且这个请求落在另一台服务器上,session 信息并不存在并且会获得一个“未认证”的响应。我知道,你可以通过一个粘性 session 解决这个问题。然而,在基于 token 的认证中,这个问题很自然就被解决了。没有粘性 session 的问题,因为在每个发送到服务器的请求中这个请求的 token 都会被拦截
实例:
let express= require("express")
let app = express()
//引入jsonwebtoken模块
let jwt =require("jsonwebtoken")
app.listen(3000,()=>console.log("端口监听中"))
app.get("/api/login",(req,res)=>{
//获取username,password 进行数据库验证
//生产token
let token= jwt.sign({
username:req.query.username
},"MD5",{expiresIn:60})//加密方式 过期时间
console.log(token)
//token和库信息返回给前端
res.send({
err:0,
msg:"用户登录了",
data:"库数据",
token
})
})
app.get("/api/user",(req,res)=>{
//获取客户端传递过来的token
let token = req.query.token || req.body.token || req.headers.token;
//验证token
jwt.verify(token,"MD5",(err,decode)=>{
// console.log("err",err)
console.log("decode",decode)
if(err){
//验证失败
res.send({
err:1,
msg:"登录失败"
})
}else{
//验证成功 返回数据
res.send({
err:0,
msg:"登录成功",
data:"库数据"
})
}
})
})
文件上传
思想
前端表单->后端接收到文件本身->保存到服务器上->给数据库记录文件一些信息->库返回给nodejs相关信息->nodejs返回给前端
前端: <input type=file enctype=“multipart/form-data” name=“fieldname”
实现
multer->文件名会随机->fs模块改名->path系统模块解析磁盘路径
后端:multer 接受 form-data编码数据
path系统模块
操作系统磁盘路径
编码
windows: c:\\user\\admin\\a.jpg
mac: ~/desktop/1901
UI呈现
windows: c:\user\admin
mac: ~/desktop/1901
API
磁盘路径解析 parse
path.parse('c:\\wamp\\xx.png') // string -> object
//返回
{
root: 'c:\\', 盘符
dir: 'c:\\wamp', 目录
base: 'xx.png', 文件名
ext: '.png', 扩展名
name: 'xx' 文件,不含扩展名
}
片段合并join
path.join('磁盘路径1','磁盘路径2','磁盘路径n')
__dirname 魔术变量 返回当前文件所在的磁盘路径
片段合并 resolve
path.resolve('磁盘路径1','磁盘路径n')
合并磁盘片段,右到左找根,左到右拼接,没有找到根,以当前文件路径为根
实例:
const path = require("path")
// let str = "F:\\path\\idCard2.jpg"
let str ="F:/path/idCard2.jpg"
//path解析 路径
console.log(path.parse(str))
// {
// root: 'F:/',
// dir: 'F:/path',
// base: 'idCard2.jpg',
// ext: '.jpg',
// name: 'idCard2'
// }
let str1 ="F:\\path"
let str2 ="idCard2.jpg"
console.log(path.join(str1,str2))
//F:\path\idCard2.jpg
//node.js全局变量 __dirname 获取当前文件路径
// console.log(__dirname)
// console.log(path.join(__dirname,str2))
//片段合并 resolve
// console.log(path.resolve(str2)) //相当于path.join(__dirname,str2)
console.log(path.resolve("f:\\",str2)) //f:\idCard2.jpg
console.log(path.resolve("a","f:\\","b","c.jpg"))//f:\b\c.jpg 从后往前去找根目录 找到了之后从前往后排列
multer中间件
multer 接受 form-data编码数据,所有要求前端携带时注意一下,如:<input type=file enctype="multipart/form-data" name="fieldname"
,
使用
//1 引入
let multer = require('multer');
//2 实例化
let objMulter = multer({ dest: './upload' }); //dest: 指定 保存位置(存到服务器)
//安装中间件,
app.use(objMulter.any()); //允许上传什么类型文件,any 代表任何类型
中间件扩展了req请求体 req.files
app.get('/reg',(req,res)=>{
req.files
})
fieldname: 表单name名
originalname: 上传的文件名
encoding: 编码方式
mimetype: 文件类型
buffer: 文件本身
size:尺寸
destination: 保存路径
filename: 保存后的文件名 不含后缀
path: 保存磁盘路径+保存后的文件名 不含后缀
实例:
const express = require("express")
const fs = require("fs")
const path = require("path")
const app = express()
app.listen(3000,()=>console.log("3000端口在监听中哦...."))
//静态资源托管 http://localhost:3000/upload/XXXXXXXXXXXXXX.webp
//如果用户想要在浏览器上面直接访问图片或者html页面,需要做一个静态服务器。
app.use(express.static("./public"))
//文件上传 前端例如上传一张图片给后端,后端需要将图片存入upload文件夹里面去。
//1.引入multer中间件
const multer = require("multer")
//2.实例化multer
let objMulter = multer({ dest: './public/upload' }); //dest: 指定 保存位置(存到服务器)
//3.安装中间件
//app.use(objeMulter.image()) //仅允许上传图片类型
app.use(objMulter.any()) //运行上传什么类型的文件,any就代表任意类型都可以
app.post("/api/reg",(req,res)=>{
// console.log(req)
// console.log(req.files[0].originalname)
//4.需要进行文件的改名操作了
// fs.renameSync('改前','改后');
let oldFile = req.files[0].path;
let newFile = req.files[0].path + path.parse(req.files[0].originalname).ext;
fs.renameSync(oldFile,newFile)
//5.把磁盘路径转成网络路径入库操作
//6.可以将地址返回给客户端
res.send({
err:0,
url:"http://localhost:3000/upload/"+req.files[0].filename+path.parse(req.files[0].originalname).ext
})
})
后端渲染
通常根据后端返回的json数据,然后来生成html被称为前端渲染,而后端渲染是后端把json与html结合渲染好后返回到浏览器,没前端什么事了
模板引擎
无论前后谁来渲染页面,都会用到模板引擎,前端渲染页面实际上是操作dom,后端渲染页面是把数据和html字符拼接后丢给浏览器
引擎 | 前端 | 后端 |
---|---|---|
angularJs | √ | × |
vue/mustach | √ | √ |
react | √ | √ |
angularTs/mustach | √ | √ |
jade/pug | × | √ |
ejs | × | √ |
jquery + art-template | √ | × |
handlerbars | √ | × |
jade
原理:fs抓取前端静态页面 + jade + 数据 -> 返回send(data) -> 浏览器
特点:侵入式,强依赖
使用
let jade = require('jade')
let html = jade.renderFile('jade模板文件',{数据},{pretty:true}); //返回字符
jade模板文件语法
父子要缩进
属性: 标签(key=value,key2=value)
内容: 标签 内容
实例:
const express = require("express")
//引入jade
const jade = require("jade")
const app = express()
app.listen(3000)
app.get("/",(req,res)=>{
//通过jade模板引擎渲染文件
//let html = jade.renderFile('jade模板文件',{pretty:true,key:value}); //返回字符
let html = jade.renderFile("./views/jade/index.jade",{pretty:true,data:"你好呀,jade!"})
console.log(html)
res.send(html)
})
index.jade
html
head
body
div(class="list")
ul
li(class="active")
a(href="http://www.baidu.com") a连接....
div 我是div哦...
h1 #{data}
ejs
原理:fs抓取前端静态页面 + ejs + 数据 -> 返回send(data) -> 浏览器
特点:非侵入式,温和,弱依赖
使用
let ejs = require('ejs')
ejs.renderFile('ejs模板文件',{要合并到html数据},回调(err,data))
err:错误,null代表没有错误
data: 渲染后的字符|流
ejs模板 : 后缀名为ejs的html文件
ejs模板文件语法
- ejs 结构就是html
- 输出: <%= 数据名|属性名|变量名 + 表达式 %>
- 语句: <% 语句 %> 需要被<% %> 包裹
- 非转义输出: <%- 数据名|变量名 + 表达式 %>
- 载入公共:<%- include(’./hd.ejs’,{数据}) %>
其他扩展
实例:
const express = require("express")
//引入ejs模板引擎
let ejs = require("ejs")
const app = express()
app.listen(3000)
app.use(express.static("./public"))
let data = {
title:"hello!!ejs模板...",
age:18,
bool:false,
list:[
{id:1,des:"123,123",href:"href1"},
{id:2,des:"456,456",href:"href2"},S
{id:3,des:"789,789",href:"href3"}
],
strong:"<strong>Strong</strong>"
}
app.get("/",(req,res)=>{
ejs.renderFile("./views/ejs/index.ejs",data,(err,data)=>{
console.log("data=>",data)
if(!err){
res.send(data)
}
})
})
index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%-strong %></title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<%- include('./hd.ejs',{title:"你好"}) %>
<h1><%=title %></h1>
<h3><%=age+1 %></h3>
<%
var a = 3
var b = 4
var c = a+b
%>
<% var d = 10 %>
<% var e = 20 %>
<% f = d+e %>
<% if(bool){%>
<nav>渲染nav1这个导航....</nav>
<% }else{ %>
<nav>渲染nav2这个导航....</nav>
<%}%>
<div>c====> <%=c %></div>
<div>c====> <%=f %></div>
<ul>
<% for(var i=0;i<list.length;i++){ %>
<li><a href="<%= list[i].href%>"><%=list[i].des %></a></li>
<% } %>
</ul>
<div>
<% for(var i=0;i<list.length;i++){ %>
<p><a href="<%= list[i].href%>"><%=list[i].des %></a></p>
<% } %>
</div>
</body>
</html>