⡖⠒⠒⠒⠤⢄⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸ ⠀⠀⠀⡼⠀⠀⠀⠀ ⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢶⣲⡴⣗⣲⡦⢤⡏⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠋⠉⠉⠓⠛⠿⢷⣶⣦⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠇⠀⠀⠀⠀⠀⠀⠘⡇⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡞⠀⠀⠀⠀⠀⠀⠀⢰⠇⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⡴⠊⠉⠳⡄⠀⢀⣀⣀⡀⠀⣸⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢸⠃⠀⠰⠆⣿⡞⠉⠀⠀⠉⠲⡏⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠈⢧⡀⣀⡴⠛⡇⠀⠈⠃⠀⠀⡗⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣱⠃⡴⠙⠢⠤⣀⠤⡾⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢀⡇⣇⡼⠁⠀⠀⠀⠀⢰⠃⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⣸⢠⣉⣀⡴⠙⠀⠀⠀⣼⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⡏⠀⠈⠁⠀⠀⠀⠀⢀⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢸⠃⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⣰⠃⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⣀⠤⠚⣶⡀⢠⠄⡰⠃⣠⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⢀⣠⠔⣋⣷⣠⡞⠀⠉⠙⠛⠋⢩⡀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀
⠀⡏⢴⠋⠁⠀⣸⠁⠀⠀⠀⠀⠀ ⠀⣹⢦⣶⡛⠳⣄⠀⠀⠀⠀⠀
⠀⠙⣌⠳⣄⠀⡇ 不能 ⡏⠀⠀ ⠈⠳⡌⣦⠀⠀⠀⠀
⠀⠀⠈⢳⣈⣻⡇ 白嫖 ⢰⣇⣀⡠⠴⢊⡡⠋⠀⠀⠀⠀
⠀⠀⠀⠀⠳⢿⡇⠀⠀⠀⠀⠀⠀⢸⣻⣶⡶⠊⠁⠀⠀
⠀⠀⠀⠀⠀⢠⠟⠙⠓⠒⠒⠒⠒⢾⡛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⣠⠏⠀⣸⠏⠉⠉⠳⣄⠀⠙⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⡰⠃⠀⡴⠃⠀⠀⠀⠀⠈⢦⡀⠈⠳⡄⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣸⠳⣤⠎⠀⠀⠀⠀⠀⠀⠀⠀⠙⢄⡤⢯⡀⠀⠀⠀⠀⠀⠀
⠀⠐⡇⠸⡅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⡆⢳⠀⠀⠀⠀⠀⠀
⠀⠀⠹⡄⠹⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⠸⡆⠀⠀⠀⠀⠀
⠀⠀⠀⠹⡄⢳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⡀⣧⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢹⡤⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣷⠚⣆⠀⠀⠀⠀
⠀⠀⠀⡠⠊⠉⠉⢹⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡎⠉⠀⠙⢦⡀⠀
⠀⠀⠾⠤⠤⠶⠒⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠒⠲⠤⠽
Node.js
是一个javascript运行环境。它让javascript可以开发后端程序
,实现几乎其他后端语言实现的所有功能,可以与```PHP、Java、Python、.NET、Ruby等后端语言平起平坐。Ryan Dahl
鉴权说明 :「HTTP 无状态」我们知道,HTTP 是无状态的。也就是说,HTTP 请求方和响应方间无法维护状态,都是一次性的,它不知道前后的请求都发生了什么。但有的场景下,我们需要维护状态。最典型的,一个用户登陆微博,发布、关注、评论,都应是在登录后的用户状态下的 「标记」
,那解决办法是什么呢?
Cookie&Session 图示 ↓
//注册session中间件
app.use(session({
name: "先生", //session名字
secret: "serverz~qwer", //服务器生成 session 的签名
cookie: {
maxAge: 1000 * 60 * 60, //过期时间
secure: false // 为 true 时候表示只有 https 协议才能访问cookie
},
rolling: true, 为 true 表示 超时前刷新,cookie 会重新计时; 为 false 表示在超时前刷新多少次,都是按照第一次刷新开始计时。
resave: true, //重新设置session后, 会自动重新计算过期时间
saveUninitialized: true, 强制将为初始化的 session 存储
//通过connect-mongo 存储数据库、过期自动销毁、创建新的sesstion储存数据库
store: MongoStore.create({
mongoUrl: 'mongodb://127.0.0.1:27017/Oyande', //新创建了一个数据库
ttl: 1000 * 60 * 60 // 过期时间
})
}))
– 登录校验接口
//登录校验接口
router.post("/login", UserController.login)
router.get("/logout", UserController.logout)
APP.js管理
//设置中间件,sesssion过期校验
app.use((req, res, next) => {
//排除login相关的路由和接口
if (req.url.includes("login")) {
next()
return
}
if (req.session.user) {
//重新设置以下sesssion
req.session.mydate = Date.now()
next()
} else {
//是接口, 返回错误码
//不是接口,就重定向
req.url.includes("api")
? res.status(401).json({ ok: 0 }) : res.redirect("/login")
}
})
– M
login: (username, password) => {
return UserModel.find({ username, password })
}
– V
Document
登录页面
用户名:
密码:
– C
login: async (req, res) => {
const { username, password } = req.body
const data = await UserService.login(username, password)
if (data.length === 0) {
res.send({
ok: 0
})
} else {
//设置session {}
req.session.user = data[0] //设置session对象,
//默认存在内存中。
res.send({
ok: 1
})
}
},
logout: (req, res) => {
req.session.destroy(() => {
res.send({ ok: 1 })
})
}
演示
缺点
session 缺点 :占内存,占空间,容易跨平台伪造 ,CSRF伪造攻击
JSON Web Token (JWT) 使用步骤说明 :session 换成 localstorage ,数据储存在localstorage,防止加密的数据(签名
)被反推出来 需要 添加 密钥。
使用Session 图示
使用JSON Web Token (JWT) 图示
当然, 如果一个人的token 被别人偷走了, 那也没办法,会认为小偷就是合法用户, 这其实和一个人的session id 被别人偷走是一样的。
这样一来, 就不保存session id , 只是生成token , 然后验证token , 用CPU计算时间获取session 存储空间 !
解除了session id这个负担, 可以说是无事一身轻, 机器集群现在可以轻松地做水平扩展, 用户访问量增大, 直接加机器就行。 这种无状态的感觉实在是太好了!
缺点
占带宽,正常情况下要比 session_id 更大,需要消耗更多流量,挤占更多带宽,假如你的网站每月有 10 万次的浏览器,就意味着要多开销几十兆的流量。听起来并不多,但日积月累也是不小一笔开销。实际上,许多人会在 JWT 中存储的信息会更多;
无法在服务端注销,那么久很难解决劫持问题;
性能问题,JWT 的卖点之一就是加密签名,由于这个特性,接收方得以验证 JWT 是否有效且被信任。对于有着严格性能要求的 Web 应用,这并不理想,尤其对于单线程环境。
注意
CSRF攻击的原因是浏览器会自动带上cookie,而不会带上token;
以CSRF攻击为例 ↓
cookie:用户点击了链接,cookie未失效,导致发起请求后后端以为是用户正常操作,于是进行扣款操作;token:用户点击链接,由于浏览器不会自动带上token,所以即使发了请求,后端的token验证不会通过,所以不会进行扣款操作;
使用库 Jsonwentoken : https://www.npmjs.com/package/jsonwebtoken
使用库 axios : https://www.npmjs.com/package/axios
–
说明:sign方法用于生成一个jwt字符串,该方法接收四个参数,依次如
0、jsonwebtoken.sign(payload, secretOrPrivateKey, [options, callback]);
参数
1、接收一个对象,用于传入用户身份信息
2、接收一个字符串,作为jwt.signature的加密密钥
3、options包括以下选项
4、callback(err,token):生成jwt结束时执行的回调函数,遵循nodejs的错误优先原则,第一个参数是生成jwt过程抛出的异常信息,第二个参数是成功生成的jwt字符串值
❗注意 : 需要注意的是,一旦指定了sign方法的第四个参数回调函数,则sign方法变为异步方法,jwt字符串只能通过回调函数获取。
–
说明:verify方法用于校验和解析jwt字符串,该方法入参四个参数,如
1、token:jwt字符串
2、secret:加密密钥
3、options:配置对象 配置如下
4、callback(err,decode):回调函数,遵循nodejs错误优先原则,第一个参数是异常信息,第二个参数是根据jwt解码出来的数据
❗注意1 : 需要注意其中complete属性,该属性为true,则根据jwt字符串解码出来完整数据,即包含header,payload,signature,否则只包含payload
❗注意2 : 一旦verify传入callback,verify方法不再同步返回解码数据,但是verify入参callback并不是异步执行的,而是同步执行的。
规范文件路径 util/JWT.js
const jwt = require("jsonwebtoken")
const secret = "guoxiansheng"
const JWT = {
//获取的token 格式 XXX.XXX.XXX
generate(value, expires) {
return jwt.sign(value, secret, { expiresIn: expires }) //内容数据、密钥、token的时长
//第四个回调函数(err,token)=>{}
},
verify(token) {
try {
return jwt.verify(token, secret,{complete:false}) //加密的token 、 密钥
//第四个回调函数(err,token)=>{}
} catch (error) {
return false
}
}
}
module.exports = JWT
- NotBeforeError 该异常发生在jwt尚未生效时使用
- TokenExpiredError 该异常发生在jwt字符串校验成功,但是已失效
- JsonWebTokenError 该异常发生在
规范文件路径 util/JWT.js
const jwt = require("jsonwebtoken")
const secret = "guoxiansheng"
const JWT = {
//获取的token 格式 XXX.XXX.XXX
generate(value, expires) {
return jwt.sign(value, secret, { expiresIn: expires,notBefore:'0.2h' }) //内容数据、密钥、token的时长
//第四个回调函数(err,token)=>{}
},
verify(token) {
try {
return jwt.verify(token, secret,{complete:false}) //加密的token 、 密钥
//第四个回调函数(err,token)=>{}
} catch (error) {
return false
}
}
}
module.exports = JWT
html
Document
登录页面
用户名:
密码:
接口
//使用到ejs
var express = require('express');
var router = express.Router();
router.get('/', function (req, res, next) {
res.render('login', { title: 'Express' });
});
module.exports = router;
登录判断
login: async (req, res) => {
const { username, password } = req.body
const data = await UserService.login(username, password)
if (data.length === 0) {
res.send({
ok: 0
})
} else {
//设置token
const token = JWT.generate({
_id: data[0]._id,
username: data[0].username
}, "1d")
//token返回在header
res.header("Authorization", token)
//默认存在内存中。
res.send({
ok: 1
})
}
},
说明:首页发送数据接口请求,且 首页设置 “阿贾克斯” 请求拦截器,请求前(获取localStorage token) 且 设置请求头(headers),app.js(设置中间件判断token,通过后返回响应头) , 请求成功前(重新设置localStorage token),出错(移除token,重定向到 /login)
前端代码
首页
//设置中间件,token过期校验
app.use((req, res, next) => {
//排除login相关的路由和接口
if (req.url.includes("login")) {
next()
return
}
const token = req.headers["authorization"]?.split(" ")[1]
if (token) {
const payload = JWT.verify(token)
if (payload) {
//重新计算token过期时间
const newToken = JWT.generate({
_id: payload._id,
username: payload.username
}, "1d")
res.header("Authorization", newToken)
next()
} else {
res.status(401).send({ errCode: -1, errInfo: "token过期" })
}
} else {
next()
}
})
以上是个人学习Node的相关知识点,一点一滴的记录了下来,有问题请评论区指正,共同进步,这才是我写文章的原因之,如果这篇文章对您有帮助请三连支持一波