在这里聊聊关于我对token的浅显认知,由于本人学习前端不久,如有错误,请指正。
1、token的本质就是一串字符串,是一段从服务端生成的字符串。在你进行登录时,前端会保存你登录的数据,比如用户名,密码,邮箱,手机号等等。。。。然后通过请求发送到后端,后端对你的前端传来的数据进行加密(根据安全性,可以使用不同的加密算法进行不同程度的加密),生成字符串。
2、token主要是用来干啥的,很多人说token是个令牌,对,确实没错,token就是令牌作用。当用户登录后进入到其他的功能页面,之后发的每个请求都会带有‘令牌’token,如果‘令牌’正确看,则可以正常请求,完成相应的功能,否则,则退出,需要重新进行登录。
1、我们先了解下使用token的简单流程
首先,我们在首次进入一个网站时,会进行注册,之后到登录页面。在登录页面,我们会进行登录操作,即为首次登录,在首次登录后,登录页面传入的信息通过请求发送到后端,后端会先验证该用户是否存在,并且密码是否正确,若用户存在,且密码正确,则根据安全性,对该用户的登录信息进行不同程度的加密,保存到token中;并将该token返回至前端。
其次,前端接收到后台返回的token后,会将token保存到本地存储中,如cookies等,等待调用,并设置过期时间,若过期则自动删除。
之后,在除了登录、注册等其他的功能页面中进行请求,我们会将cooKies中保存的token放到请求头里,发送到后端。
再之后,后端取出请求头里的token,进行解密,并于在数据库中保存的用户信息进行比对(或者也许不用与数据库中的数据进行比对,直接与之前第一次在后台生成的token进行比对),若比对相等,则正常进行请求,否则,则返回登录页面,重新登录。
最后,还有一种情况,当设置cookies的时间过期了,cookies之前保存的内容也就没了,当然也就返回登录页面,重新登录。
结合二,我们来进行一步步的演示,在这里我用的技术有react-hooks、node、express、MySQL、react-cookies
1、在二的步骤之前,我们要加入一个操作,即设置一个自定的请求头set-cookie,用来在除登录、注册的其它的功能页面发送的请求头中保存token,并发送到后端。代码如下:
res.header("Access-Control-Allow-Headers", "Content-Type,XFILENAME,XFILECATEGORY,XFILESIZE,set-cookie");//一定要在这里面加上自定义的请求头
最后的set-cookies,即为我们添加的自定义请求头属性,之后我们打开控制台,查看请求头会发现,set-cookie 已经添加到请求头中
2、将前端登录信息返回到后端,在后端进行验证,并加密保存到token中;进行加密保存操作,我们需要下载一个依赖,jsonwebtoken,并进行引入
npm install jsonwebtoken
const jwt = require('jsonwebtoken')
if (result.length) { //如果在数据库中查到该用户名
console.log('------------start----------------');
var string = JSON.stringify(result);
var json = JSON.parse(string)[0];
console.log(string)
if (json.password == LoginData.password) {//进行验证密码
console.log('密码校验正确');
console.log(result);
// res.send('登录成功!')
// 用JWT 将token 将用户信息加密
const token = jwt.sign(LoginData,'sxy3469998737')
res.send({//将token返回至前端
a:'登录成功!',
b:{token}
})
}
else {
console.log('密码校验错误');
res.send('密码错误!')
}
}
3、前端接收到后台返回的token后,我们会将他保存到cookies中(这里我们引入一个依赖react-cookies来对cookies进行相关操作)
npm install --save react-cookie
import cookie from 'react-cookies'
//将token保存到cookies 并且 定时删除
const deleteTimeCookie = (a) => {
//定义cookies在一小时后自动失效
let deleteTime = new Date(new Date().getTime() +1000*60*60)
//userInfo为保存的cookie的名称,a为后台返回的token,
//{ expires: deleteTime }则记时,到期自动删除,
//{path:'/'}设置在该域名的所有路径下都可使用该cookie
cookie.save('userInfo',a,{ expires: deleteTime },{path:'/'})
}
axios.post('http://localhost:8000/Signin',
{data1:JSON.stringify(data1)},
{headers:{'Content-Type' : 'application/json'}})
.then((res) => {Loginin(res.data.a,res.data);
// cookie.save('userInfo',res.data.b.token,{path:'/'});
deleteTimeCookie(res.data.b.token);//这里的res.data.b.token即为后台返回的token
})
.catch((err) => {console.log(err);})
}
4、在除登录、注册等其他功能页面发送请求时(我这里为一个会议预订的请求),将cookie中保存的token放在自定义的请求头中,发送到后端
var a= cookie.load('userInfo');//取出cookie中userinfo中保存的内容
axios.post('http://localhost:8000/Index/Meeting',
{data4:JSON.stringify(data4)},
{headers:{'Content-Type' : 'application/json;charset=UTF-8',
'set-cookie' : a //将其给之前自定义的请求头
}
})
.then((res) => {Loginexpired(res.data);})
.catch((err) => {console.log(err);})
5、后端取出请求头里的token,进行解密,并于数据库中存入的用户信息进行比对
app.post('/Index/Meeting',
function(req,res) {
const MeetData = JSON.parse(req.body.data4);
const token = req.headers['set-cookie'].pop()//取出请求头中的token,这里pop()的作用是将token值变成字符串,若不是字符串,则解析时会报错
console.log(token);
//这里是用token的长度来判断token是否存在
//因为当cookie因为过期而被删除时,打印token会显示undefined,但是用undefined判断却不正确,null也不正确,所以用长度
if(token.length > 10) {
const a = jwt.verify(token,'sxy3469998737')//解析token,若token不为字符串,则会报错
var find2 = "SELECT * FROM useinfo WHERE username = '"+a.username+"' and password = '"+a.password+"'"
connection.query(find2,(err,result) => {
if (err) { //链接失败 直接return;
console.log('[错误]' + err);
return;
};
if(result.length) {
if(MeetData !== '删除记录!') {
//编写查询语句
var find1 = "SELECT * FROM meetbook WHERE meetRoom = '"+MeetData.meetRoom+"' and meetWeek = '"+MeetData.meetWeek+"'"
//插入语 句
var insert = 'INSERT INTO meetbook (id,meetRoom,meetMen,meetWeek,meetStartTime,meetEndTime) VALUES (0,?,?,?,?,?)';
var inserInfo = [MeetData.meetRoom,MeetData.meetMen,MeetData.meetWeek,MeetData.meetStartTime,MeetData.meetEndTime]; //定义插入数据
//判断会议时间是否冲突
var timeConflict = "SELECT * FROM meetbook WHERE (meetStartTime between '"+MeetData.meetStartTime+"' and '"+MeetData.meetEndTime+"') or (meetEndTime between '"+MeetData.meetStartTime+"' and '"+MeetData.meetEndTime+"') or (meetStartTime > '"+MeetData.meetStartTime+"' and meetEndTime < '"+MeetData.meetEndTime+"') or (meetStartTime < '"+MeetData.meetStartTime+"' and meetEndTime > '"+MeetData.meetEndTime+"') "
//查询
connection.query(find1,(err,result) => {
if (err) { //链接失败 直接return;
console.log('[错误]' + err);
return;
};
if(result.length) {
connection.query(timeConflict,(err,result) => {
if (err) { //链接失败 直接return;
console.log('[判断会议室时间冲突错误错误]' + err);
return;
};
if(result.length) {
res.send("改时间段已经被预定!")
}
else {
connection.query(insert,inserInfo,(err,result) => {
if (err) { //链接失败 直接return;
console.log('[插入数据库错误]' + err);
return;
};
console.log('------------start----------------');
console.log('预订成功');
console.log(result);
console.log('--------------end-----------------');
res.send('预订成功!')
})
}
})
}
else {
connection.query(insert,inserInfo,(err,result) => {
if (err) { //链接失败 直接return;
console.log('[插入数据库错误]' + err);
return;
};
console.log('------------start----------------');
console.log('预订成功');
console.log(result);
console.log('--------------end-----------------');
res.send('预订成功!')
})
}
})
}
else {
//console.log(MeetData);
//删除最后一条数据
var delete1 = "DELETE FROM meetbook WHERE 1 ORDER BY id DESC LIMIT 1"
connection.query(delete1,(err,result) => {
if (err) { //链接失败 直接return;
console.log('[错误]' + err);
return;
};
})
}
}
else {
res.send("验证出现错误!")
}
})
}
else {
res.send("登录已过期!")//向前端发送请求已过期的请求
}
6、前端接收到后端返回到的数据,若为‘登录已过期!’,则直接返回到登录页面,否则正常请求
//登录时间过期后 自动跳转到登录页面
const Loginexpired = (s) => {
if (s === "验证出现错误!" || s === "登录已过期!") {
window.location.href="http://localhost:3000"
}
else {
alert(s);
}
}
axios.post('http://localhost:8000/Index/Meeting',
{data4:JSON.stringify(data4)},
{headers:{'Content-Type' : 'application/json;charset=UTF-8',
'set-cookie' : a
}
})
.then((res) => {Loginexpired(res.data);})//接收到返回的数据,并传入到函数中
.catch((err) => {console.log(err);})
}