Node.js学习笔记【七】

小风车吱呀转 2021-08-08 19:04:33
学习 JS node node.js 笔记


1

Session简介

Session是一种非常重要非常流行的用户认证授权的方式。

认证:让服务器知道你是是谁

授权:让服务器知道你什么能干什么不能干

Session的优势

  • 相比JWT,最大的优势就在于可以主动清除session了(因为session是保存在服务端的,服务端可以主动清除;JWT是以Token形式保存在客户端,只要没过期,客户端就可以一直拿着Token来进行用户认证与授权)
  • session保存在服务器端,相对较为安全
  • 结合cookie使用,较为灵活,兼容性较好

session的劣势

  • cookie+session跨域场景表现并不好(cookie具有不可跨域性)
  • 如果是分布式部署,需要做多机共享session机制
  • 基于cookie的机制很容易被CSRFCSRF跨站请求伪造,一种攻击,它可以用你的cookie进行攻击)
  • 查询session信息可能会有数据库查询操作(想要拿到完整的session信息还需要拿session_id去查询数据库,查询就需要时间和计算能力,这就会带来一定的性能问题。)

Session相关的概念介绍

  • session:主要存放在服务器端,相对安全
  • cookie:主要存放在客户端,并且不是很安全
  • sessionStorage:仅在当前会话下有效,关闭页面或浏览器后被清除
  • localstorage:除非被清除,否则永久保存

JWT简介

什么是JWT?

  • JSON Web Token 是一个开放标准(RFC 7519)
  • 定义了一种紧凑且独立的方式,可以将各方之间的信息作为JSON对象进行安全传输
  • 该信息可以验证和信任,因为是经过数字签名的

JWT的构成

  • 头部(Header)
  • 有效载荷(Payload)
  • 签名(Signature)

JWT分成了三个部分,每个部分都有黑点隔开

image-20210808134734909

Header

Header本质是个JSON,这个JSON里面有2个字段

  • typ:token的类型,这里固定有JWT
  • alg:使用的hash算法,例如:HMAC SHA256或者RSA

Header编码前后

  • {“alg”:“HS256”,"typ":"JWT"}
  • 编码后就是一段Base64字符串

Payload

  • 存储需要传递的信息,如用户ID、用户名等
  • 还包含元数据,如过期时间、发布人等
  • 与Header不同,Playload可以加密

Playload编码前后

  • {“user_id”:"xiaofengche"}
  • 编码后就是一段Base64字符串

Signature

  • 对Header和Payload部分进行签名
  • 保证Token在传输的过程中没有被篡改或者损坏

Signature算法

Signature = HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

生成完签名之后依然需要进行Base64编码

JWT工作原理

客户端(浏览器)通过POST请求将用户名和密码传给服务器,服务端对用户名和密码进行核对,核对成功后将用户ID等其他信息作为JWT有效载荷,将其与头部进行base64编码后形成一个JWT,然后后端将那一段字符串作为登录成功这个请求的返回结果返回给前端,然后前端将其保存在localStorage或者sessionStorage中。

之后前端每次请求都会把JWT字符串作为Http头里面的Authorization(鉴权),然后发送给后端,后端检查其是否存在,如果存在则验证JWT字符串的有效性(例如签名是否正确,令牌是否过期等)。

验证通过后,后端则使用JWT中包含的用户信息进行其他业务逻辑并返回相应的结果。

 

image-20210808140848756

 

JWT vs. Session

  • 可扩展性

JWT可以无缝接入水平拓展,因为基于Token(令牌)的身份验证是无状态的,所以不需要在session中存储用户信息,应用程序可以轻松拓展,可以使用Token从不同的服务器中访问资源,而不用担心用户是否真的登录在某台服务器上。

  • 安全性

这两种都是会受到攻击的。

  • RESTful API

RESTful要求程序是无状态的,像session这种是有状态的认证方式,显然是不能做RESTful API的。

  • 性能

客户端向服务端发出请求的时候,可能会有大量的用户信息在JWT中,每个Http请求都会产生大量的开销;如果用session的话只要少量的开销就可以了, 因为session_id非常小,JWT的大小可能是它的好几倍。

但是session_id也有缺点,查询完整信息需要session_id,这也是要消耗性能的;JWT字符串包含了完整信息,JWT就不需要数据库查询,性能消耗就少一点,JWT相当于用空间交换时间

  • 时效性

JWT的时效性要比session差一点。因为JWT只有等到过期时间才可以销毁,无法实时更新,session可以在服务端主动手动销毁。

在Node.js中使用JWT

  • 安装jsonwebtoken

npm i jsonwebtoken

  • 签名

在终端进入 node 命令行,引入jwt

image-20210808150150719

使用sign方法进行签名,它的第一个参数是JSON对象,第二个参数可以写密钥

> token = jwt.sign({name:"xiaofengche"},'secret');
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieGlhb2ZlbmdjaGUiLCJpYXQiOjE2Mjg0MDYzMzF9.zOCf0dzBRvuBjOCcZZ5nuLbUGd4q05SQuFod48ScML4'

拿到token之后就可以传给客户端,客户端每次请求都可以拿着这个token放在头部传回给服务端,服务端拿到token之后就可以判断当前用户是谁了,有什么权限。

  • 验证

使用解码 decode就可以判断用户是谁

> jwt.decode(token);
{ name: 'xiaofengche', iat: 1628406331 }

这里的iat是指签名时的时间,单位是秒

需要验证用户信息是否被篡改,verify第二个参数是要加密钥的

> jwt.verify(token,'secret');
{ name: 'xiaofengche', iat: 1628406331 }

证明token是合法的,签名也是合法的

实现用户注册

  • 设计用户Schema

需要重新设计Schema,添加密码这个字段。

const mongoose = require('mongoose');
//mongoose提供的Schema类生成文档Schema
const { Schema,model } = mongoose

const userSchema = new Schema({
//将没用的信息隐藏起来
__v:{type:Number,select:false},
//required表示这个属性是必选的
//default可以设置默认值
name:{type:String,required:true},
//像密码这种敏感信息不应该随便暴露,需要将其隐藏起来——select:false
password:{type:String,required:true,select:false},

});
//建立模型
//User:为文档集合名称
module.exports = model('User',userSchema);

在相关操作添加新字段的定义

//创建用户
async create(ctx){
//校验请求体的name位字符串类型并且是必选的
ctx.verifyParams({
//必选:required 删掉也是默认为true
name:{ type:'string',required:true },
password:{type:'string',required:true},
});
const user = await new User(ctx.request.body).save();
ctx.body = user;
}
//更新用户
async update(ctx){
ctx.verifyParams({
//必选:required 删掉也是默认为true
name:{ type:'string',required:false },
password:{type:'string',required:false},
});
const user = await User.findByIdAndUpdate(ctx.params.id,ctx.request.body);
if(!user){ctx.throw(404,'用户不存在');}
ctx.body = user;

}

由于修改用户属性可以部分修改,所以需要修改更改路由的请求方法

//put是整体替换,现在的用户可以更新一部分属性
router.patch('/:id',update);
  • 编写保证唯一性的逻辑(用户的唯一性)

在创建用户编写保证唯一性的逻辑,保证创建时用户名不重复

//更新用户
async update(ctx){
ctx.verifyParams({
//必选:required 删掉也是默认为true
name:{ type:'string',required:false },
password:{type:'string',required:false},
});
//获取请求体中的用户名
const {name} = ctx.request.body
// findOne返回符合条件的第一个用户
const repreatedUser = await User.findOne({name});
//如果有重复用户返回409状态码代表冲突
if(repreatedUser){
ctx.throw(409,"用户名已占用");
}
const user = await User.findByIdAndUpdate(ctx.params.id,ctx.request.body);
if(!user){ctx.throw(404,'用户不存在');}
ctx.body = user;

}

image-20210808161817705

实现登录并获取token

  • 登录接口设计

登录这个动作不属于用户增删改查的任何一种,可以模仿github采用POST+动词形式

  • 用jsonwebtoken生成token

首先在config.js配置密钥

secret:'jwt-secret',

在users.js引入jsonwebtoken和密钥,接着实现登录接口

const jsonwebtoken = require('jsonwebtoken');
const {secret} = require('../config');

//登录
async login(ctx){
ctx.verifyParams({
name:{type:'string',required:true},
password:{type:'string',required:true},
});
//登录有两种情况:用户名不存在或密码错误,登录失败;登录成功
//查找符合条件的第一个用户
const user = await User.findOne(ctx.request.body);
if(!user){ctx.throw(401,'用户名或密码不正确');}
//获取id和name
const {_id,name} = user;
//登录成功生成token,参数分别为用户不敏感的信息,签名密钥,过期时间
//1d:一天
const token = jsonwebtoken.sign({_id,name},secret,{expiresIn:'1d'});
ctx.body = {token};
}

最后别忘了在routes->users.js注册接口

//delete是关键字,取别名
const {find,findById,create,update,delete:del,login} = require('../controllers/users');
router.post('/login',login)

效果演示:

jwt登录接口

 

自己编写Koa中间件实现用户认证与授权

  • 认证:验证token,并获取用户信息

在routes->users.js编写认证中间件。

假设客户端是通过Authorization字段 加上Bearer 空格+token这种形式把token传进来的,我们就知道怎么获取token了

const jsonwebtoken = require('jsonwebtoken');

const {secret} = require('../config');

const auth = async(ctx,next) => {
//当不设置authorization的时候把它设置为空字符串
const {authorization = ''} = ctx.request.header;
//去掉'Bearer '才是我们真正想要的token
const token = authorization.replace('Bearer ','');
//验证用户信息
try{
const user = jsonwebtoken.verify(token,secret);
ctx.state.user = user;
}catch(err){
//所有的验证失败手动抛成401错误,也就是未认证
ctx.throw(401,err.message);
}
await next();
}

最后把中间件放在需要认证的接口上

router.patch('/:id', auth,update);

router.delete('/:id',auth,del);

  • 授权:使用中间件保护接口

在users.js控制器中编写鉴权中间件(也可以像上面一样在routes->users.js里面)

 async checkOwner(ctx,next){
//判断当前修改或删除的用户id是不是当前登录用户的id
if(ctx.params.id !== ctx.status._id){
//操作的对象不是自己就抛出错误
ctx.throw(403,'没有权限')
}
await next();
}

最后把中间件添加到需要鉴权的接口上

const {find,findById,create,update,delete:del,login,checkOwner} = require('../controllers/users');

router.patch('/:id', auth,checkOwner,update);

router.delete('/:id',auth,checkOwner,del);

用koa-jwt中间件实现用户认证与授权

  • 安装koa-jwt:npm i koa-jwt --save

这是一个第三方中间件,功能强大。有了这个中间件,我们就不需要自己编写中间件了。

  • 使用中间件保护接口

引入中间件,只需一行代码就可以替换掉自己编写的认证中间件。

const jwt = require('koa-jwt');
const auth = jwt({ secret });
  • 使用中间件获取用户信息

koa-jwt同样将用户信息存放在ctx.state.user上,自定义授权中间件依然能正常使用。

 

版权声明
本文为[小风车吱呀转]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/Small-Windmill/p/15115783.html

  1. HTML + CSS + JavaScript to achieve cool Fireworks (cloud like particle text 3D opening)
  2. HTML + CSS + JavaScript realizes 520 advertising love tree (including music), which is necessary for programmers to express themselves
  3. Solve the problem of Web front-end deployment server (it can be deployed online without a server)
  4. HTML + CSS + JS make wedding countdown web page template (520 / Tanabata Valentine's Day / programmer advertisement)
  5. What else can driverless minibus do besides "Park connection"?
  6. Cloud native leads the era of all cloud development
  7. NRM mirror source management tool
  8. Bring it to you, flex Jiugong
  9. Lolstyle UI component development practice (II) -- button group component
  10. Deconstruction assignment in ES6
  11. Luo 2 peerless Tang clan was officially launched. The official gave a key point, and the broadcast time was implied
  12. 20初识前端HTML(1)
  13. 当新零售遇上 Serverless
  14. 20 initial knowledge of front-end HTML (1)
  15. When new retail meets serverless
  16. [golang] - go into go language lesson 5 type conversion
  17. [golang] - go into go language lesson 6 conditional expression
  18. HTML5(八)——SVG 之 path 详解
  19. HTML5 (8) -- detailed explanation of SVG path
  20. 需要开通VIP以后页面内容才能复制怎么办?控制台禁用javascript即可
  21. Web前端|CSS入门教程(超详细的CSS使用讲解,适合前端初学者)
  22. 实践积累 —— 用Vue3简单写一个单行横向滚动组件
  23. Serverless 全能选手,再下一城
  24. What if you need to open a VIP to copy the page content? Just disable JavaScript on the console
  25. Web front end | CSS introductory tutorial (super detailed CSS explanation, suitable for front-end beginners)
  26. Practice accumulation - write a single line horizontal scroll component simply with vue3
  27. Dili Reba is thin again. She looks elegant and high in a strapless hollow skirt, and her "palm waist" is beautiful to a new height
  28. Serverless all-round player, next city
  29. The difference between MySQL semi synchronous replication and lossless semi synchronous replication
  30. Vue表单设计器的终极解决方案
  31. The ultimate solution for Vue form designer
  32. Nginx从理论到实践超详细笔记
  33. Yu Shuxin's red backless swimsuit is split to the waist and tail, with a concave convex figure and excessive color matching, and his face is white to dazzling
  34. Nginx ultra detailed notes from theory to practice
  35. 【动画消消乐|CSS】086.炫酷水波浪Loading过渡动画
  36. typecho全站启用https
  37. CCTV has another popular employee. The off-site interpretation is very professional, and the appearance ability is no less than that of Wang Bingbing
  38. [animation Xiaole | CSS] 086. Cool water wave loading transition animation
  39. Enable HTTPS in Typecho
  40. 50天用JavaScript完成50个web项目,我学到了什么?
  41. 根据JavaScript中原生的XMLHttpRequest实现jQuery的Ajax
  42. What have I learned from completing 50 web projects with JavaScript in 50 days?
  43. "My neighbor doesn't grow up" has hit the whole network. There are countless horse music circles, and actor Zhou Xiaochuan has successfully made a circle
  44. 根据JavaScript中原生的XMLHttpRequest实现jQuery的Ajax
  45. Implement the Ajax of jQuery according to the native XMLHttpRequest in JavaScript
  46. Implement the Ajax of jQuery according to the native XMLHttpRequest in JavaScript
  47. 30 + women still wear less T-shirts and jeans. If they wear them like stars, they will lose weight
  48. 数栈技术分享前端篇:TS,看你哪里逃~
  49. Several stack technology sharing front end: TS, see where you escape~
  50. 舍弃Kong和Nginx,Apache APISIX 在趣链科技 BaaS 平台的落地实践
  51. Abandon the landing practice of Kong and nginx, Apache apisik on the baas platform of fun chain technology
  52. 浪迹天涯king教你用elementui做复杂的表格,去处理报表数据(合并表头,合并表体行和列)
  53. 前端HTML两万字图文大总结,快来看看你会多少!【️熬夜整理&建议收藏️】
  54. Wandering around the world king teaches you to use elementui to make complex tables and process report data (merge header, merge table body rows and columns)
  55. 路由刷新数据丢失 - vuex数据读取的问题
  56. Front end HTML 20000 word graphic summary, come and see how much you can【 Stay up late to sort out & suggestions]
  57. Route refresh data loss - vuex data reading problem
  58. Systemctl系统启动Nginx服务脚本
  59. Systemctl system startup nginx service script
  60. sleepless