首先我们要了解什么是身份认证(Authentication)?
身份认证也叫身份验证或者鉴权,指通过一定的手段来完成对用户身份的验证
我们会使用session和JWT认证机制进行开发,那么我们在什么情况下使用这俩个认证机制呢?
推荐在服务端渲染开发情况下使用session认证机制
在前后端分离的Web开发环境下使用JWT认证机制
目录
一.session认证机制
我们学习session认证机制需要从HTTP协议这里开始出发:
1.HTTP协议的无状态性
HTTP协议无状态性指的是客户端的每一次HTTP请求都是独立的,连续多次的请求之间没有直接的关系,服务器也不会主动的保留每一次HTTP请求的状态
什么意思呢?
比如现在我们在上大学,出入校园是需要门卡的,一个学校几万人甚至更多,那么一个保安叔叔可以记住我们这么多人吗?当然是记不住的,我们出入校园需要使用门卡这个认证方式或者认证工具来证明我们就是这个学校的学生,我们可以进去
而HTTP协议的无状态性,就是相当于你每次出入校园都没有门卡,并且保安叔叔还记不住你,如果想出入,那么需要找学校认证一下,每一次出入都认证,那真的很麻烦
2.Cookie突破HTTP无状态的限制
所以我们现在需要使用"门卡"来突破HTTP无状态的限制
Cookie就是我们的门卡,就是证明我们是学生的认证方式
我们回到我们的编程世界中---------Cookie是什么?
Cookie是存储在用户浏览器中的一段不超过4kb的字符串,它由一个名称、一个值以及其他控制Cookie有效期、安全性、使用范围的可选属性组成
不同域名下的Cookie是各自独立的:比如我们在百度注册了一个会员,我们可以在淘宝里面使用百度的VIP吗?是不能的;再者我们拿着清华的校园卡,可以进北大的校园吗?我不在清华我没试过,哈哈
每当客户端发起请求的时候,会自动的把当前域名下的所有的没有过期的Cookie一同发送到服务器进行身份的验证
总结Cookie:自动发送-->域名独立-->过期时限(过期不能使用)-->4kb存储空间
3.Cookie的缺点
Cookie是不具有安全性的,为什么说Cookie不具有安全性呢?
因为Cookie是存储在浏览器中的,而且浏览器也提供了读写Cookie的API,因此Cookie是很容易被伪造的,不具有安全性
所以用户的隐私数据(身份信息以及密码)不要使用Cookie来存储
4.提高身份认证的安全性
Cookie虽然不具安全性,但是我们使用一些手段对Cookie进行认证的话,就会大大提高用户信息的安全性
使用什么手段?
没错,就是我们之前所提到的session认证机制
我们稍后来谈session的使用方法,我们先来了解一下session的工作原理:
用户的信息不再存在于浏览器中,而是存储在服务器中,发送给浏览器的只是一个简单的Cookie,客户端下次请求的时候,会将Cookie再次发送到服务器,服务器会根据发送来的Cookie查找对应的用户信息
5.使用session
首先我们先配置一个express-session中间件,当用户请求时,必须先经过这个session中间件的处理,才能由路由执行这次请求,返回给客户端相对于信息的内容
下载express-session中间件
npm i express-session
导入express-session中间件
const session = require('express-session')
配置session中间件(需配置对象)
服务器实例.use(session({
secret:'keyboard test',
resave:false,
saveUninitialized:true
}))
//resave与saveUninitialized为固定写法
//secret我们可以填写任意字符串
6.向session中存储数据
当express-session中间件配置成功后,我们就可以通过req.session来访问和使用这个session对象了,从而存储用户的关键信息
//用户登录
服务器实例.post('api/login',(req,res)=> {
if(req.body.username !== 'admin123' || req.body.password !== '000000'){
return res.send({
status:1,
msg:"登陆失败-因为用户名或者密码输入错误"
})
}else{
//如果成功,将用户的信息存储在session中
//自定义一个user与login登录状态属性
req.session.user = req.body
req.session.islogin = true
req.send({
status:0,
msg:"登陆成功"
})
}
})
7.向session中获取数据
我们可以直接的从req.session对象上获取到之前所存储的数据
//获取用户姓名的接口
服务器实例.get('api/username',(req,res)=> {
//首先判断用户是否登录
if(!req.session.islogin){
return res.send({status:1,msg:'fail'})
}else{
res.send({
status:0,
msg:'success',
username:req.session.user.username
})
}
})
8.清空session
调用req.session.destroy()函数清空session服务器所保存的当前用户的session信息
//退出登录的接口
服务器实例.post('api/logout',(req,res)=> {
//清空当前客户端对应session信息
req.session.destory()
res.send({
status:0,
msg:"退出登录成功"
})
})
二.JWT认证机制
我们使用JWT,那么就一定由JWT强于session的地方,那么session哪里不足呢?
session认证机制需要配合Cookie才能实现,但是我们知道因为同源策略,Cookie是不支持跨域访问的,所以涉及到前端跨域请求后端接口的时候,需要做很多很多额外的配置才可以实现跨域session认证,这样是很麻烦的
所以当前端请求后端接口不存在跨域问题的时候,我们session认证机制是比较好的(也就是当我们选择服务端渲染的Web开发模式时,使用session认证方式)
但是当前后端分离开发Web时,也就是前端需要跨域请求后端接口的时候,使用JWT认证机制
JWT全称JSON Web Token,他把用户信息通过Token加密字符串的形式保存到客户端浏览器中,而session是保存在服务器端的
那么等等...........
我们不是说存储在浏览器中的数据不安全吗?为什么JWT保存到了客户端浏览器中?
其实保存到客户端浏览器的是一长串的加密字符(也叫密钥),浏览器是无法解析的,并且有效的防止了JWT字符串在网络传输过程中被别人破解,需要返回给服务器端解析,所以它是安全的,我们之前说的是单单使用Cookie是不安全的
JWT是由Header头部、Payload有效荷载、Signature签名三部分组成的(三者之间使用.来分隔)
ZUdTY3hic3pMRGxlOHNXcnZSM08zS21DQmJXbUtmWUtjaHhpN3lYZ3RZWnNsU0.pFNCtINnlmU1BZZVdXUUxnZy0tUnVBV2NHdnNoVGZyMVdMRTFzTXJhZz09.9f871e86b9bf50b484e614b2c7342afb86cab3c4
像这样,Payload存储的是用户的信息,它是一个用户信息经过加密之后生成的字符串
Header和Signature是安全性相关的部分,保证了Token安全性
当客户端收到了服务器返回的JWT之后,通常会将它存储在localStorage或者sessionStrage中(我们并不陌生,之前提到过的本地存储,是一个浏览器给我们提供的一个5M或者20M左右的空间)
在以后客户端每次与服务器通信的时候,都会带上这个JWT的加密字符串来进行身份的验证
我们最好可以把JWT放在HTTP请求头的Authorization字段中
Authorization: Bearer <token>
1.定义secret密钥
首先我们先安装配置好JWT
npm i jsonwebtoken express-jwt
这里我们为什么要下载俩个包呢?因为是需要加密解密的,加密是一种功能,解密又是一种功能
jsonwentoken用于生成加密字符串;express-jwt用来将JWT加密字符串解析还原为JSON对象
然后我们导入这俩个模块
const token = require('jsonwebtoken')
const jwt = require('express-jwt')
接下来我们就可以进行定义secret密钥了:
token:jwt.sign(
{username:req.body.username},
secretkey,
{expiresIn:'30s'}
)
//30s代表这个token有效期为30s,过了30s就失效了,更加的安全
2.将JWT字符串还原为JSON对象
当客户端每次去访问那些有权限的接口时,都需要主动通过请求头中的Authorization字段将Token字符串发送到服务器进行身份验证
首先我们需要使用服务器实例的use()来注册一个中间件:
服务器实例.use()
然后使用expressJWT({secret:secretKey})来解析Token中间件,使用unless({path:[/^\/api\//]})来指定哪些接口时不需要访问权限的(注意正则的转义)
服务器实例.use(expressJWT({secret:secretKey}).unless({path:[/^\/api\//]}))
配置成功express-jwt中间件之后,我们就可以把解析出来的用户信息挂载到req.user属性上,然后通过req.user对象访问从JWT加密字符串中解析出来的用户信息了
tips:不要把密码加密到Token字符串上
3.捕获解析JWT字符串失败-怎么解决项目崩溃问题
当我们使用express-jwt解析Token字符串时,如果客户端发送过来的Token字符串过期或者不合法,那么会产生一个解析失败的错误,影响项目的正常运行或使项目崩溃
这个问题很简单就可以解决了,我们可以通过Express的一个错误中间件来捕获到这个错误并处理
//注册错误中间件
服务器实例.use((err,req,res,next)=> {
if(err.name === 'UnauthorizedError'){
return res.send({ststus:401,message:'无效的Token'})
}else{
//如果是其他的错误
res.send({status:500,message:'未知错误'})
}
})