说出我的悲惨故事给大家乐呵乐呵:公司刚来了一个实习生,老板让他写几个接口给我,我页面还没画完呢。他就把接口给我了,我敲开心,第一次见这么高效率的后端。但我很快就笑不出来了。他似乎不知道HTTP通信是无状态的。他不能识别登录的用户,只会把数据添加进数据库。虽然网络上有很多资料,但为了省时间,我打算写了简单的demo和他探讨一下,可能不太规范,希望大家多多指教。
JSON Web Token 简介
JSON Web Token 简称JWT,在HTTP通信过程中,进行身份认证。
好了,就说这么多,先上代码,因为有些概念说不好会把人转晕,手动操作后就容易理解了:
/**
*
* 实现流程:
* 用户登录,服务器产生一个token(加密字符串)发送给前端,
* 前端将token保存(想存哪就存哪)
* 前端发起数据请求时携带token
* 服务端验证token是否合法,合法继续操作,不合法终止操作
* token的使用场景:无状态请求,保持用户的登录状态,第三方登录(token+auth2.0)
*/
async function onFinished(e){
//提交表单默认会跳转到新的页面,或者刷新整个页面
//1.阻止默认事件
e.preventDefault();
//2.定义formData对象
let formData = new FormData(e.target);
let username = formData.get('username');
let password = formData.get('password');
console.log(username, password);
try{
const result = await login(username, password);
//save token to local storage
storage.set('token', result.token)
console.log(result)
}catch(e){
console.log(e)
}
}
export function login(username,password) {
return axios.post('http://192.168.50.225:3000/login',{
username,
password
})
}
node 中 jwt的使用
var express = require('express');
const jwt = require('jsonwebtoken');//加载包
const fs = require('fs');
const { dirname } = require('path');
var router = express.Router();
/**
* login
*/
router.post('/login', function(req, res, next){
console.log(req.body)//{ username: 'eqw', password: 'qweq' }
//产生token默认算法hs256
/**
* 此方法接收两个参数
* 第一个是要加密保存的数据(一个对象,不要放隐秘性的数据,如密码),
* 第二个是要加密的私钥(一个字符串,越乱越好)
*/
let token=jwt.sign(req.body,'rieryowqerdfkjhasdfqr');
console.log(token);//返回一个加密字符串
// 服务器签发的token
//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMTIzIiwiaWF0IjoxNTcwMDc2NjU5fQ.3FT6v8zVptdWGBILD1m1CRY6sCP1I3E947krUh_E3
//我就不链接数据库了,将数据存入本地文件
//fs.appendFile()方法的作用是:将指定的内容追加到文件中。如果该文件不存在,则创建该文件:
fs.appendFile(__dirname+'/data.txt', JSON.stringify(req.body), function (err) {
if (err) throw err;
console.log('Saved!');
});
res.send({
token,
status:200,
message:'success'
})
});
module.exports = router;
前端登录:
只做演示实验,前端没有对密码加密勿怪
前端请求数据:
request.js
import axios from 'axios';
import { storage } from '../utils/localStorage';
axios.defaults.timeout = 5000
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
//在请求头里验证是否有token
axios.interceptors.request.use(function (config) {
const token=storage.get('token');
console.log(token)
if(token){
config.headers['Authorization'] = 'Bearer ' + token;
}else{
//重定向到登录界面
}
return config;
}, error => {
console.log(error)
Promise.reject(error)
})
//请求响应
axios.interceptors.response.use(function (response) {
if (response.status === 401) {
//
}
return response;
}, function (error) {
return Promise.reject(error)
})
export default axios;
user.js
import axios from '../request';
export function login(username,password) {
return axios.post('http://192.168.50.228:3000/login',{
username,
password
})
}
export function getInfo(){
return axios.get('http://192.168.50.228:3000/info');
}
后端验证token:
/**
* 请求数据
*/
router.get('/info',function(req,res,next){
//客户端请求数据的时候验证token
//客户端传递过来的token
console.log(req.headers.authorization)
let tokens=req.headers.authorization.split(' ')[1];
/**
* verify接收两个参数,
* 第一个参数是客户端传递过来的token,
* 第二个参数是加密时的私钥;
* 第三个参数是回调函数
*/
jwt.verify(tokens,'rieryowqerdfkjhasdfqr',function (err,data) {
console.log(err);//签名通过返回null,签名不通过返回err(JsonWebTokenError: invalid signature)
console.log(data);// 通过返回解密数据,失败返回unfinished
fs.readFile(__dirname+'/data.txt',function(err, datas){
if(err){
res.writeHead(505,{'Content-Type':'text/html;charset=utf-8'});
res.end({
message:'500 服务器内部错误'
})
return;
}
//console.log(data,'database');//<Buffer 7b 22 75 73 65 72 6e 61 6d 65 22 3a 22 6d 69 61 6f 22 2c 22 70 61 73 73 77 6f 72 64 22 3a 22 6d 69 61 6f 31 32 33 22 7d>
console.log(JSON.parse(datas.toString()));
//从文件中直接读取到时Buffer ,把Buffer转成datas.toString() 字符串
const user=JSON.parse(datas.toString());
console.log(user.username===data.username,'-_-');
if(user.username===data.username){
res.send({
data:[
{id:1,name:"纸崩"},
{id:2,name:"当怪兽来敲门"}
]
})
}else{
res.writeHead(404,{'Content-Type':'text/html;charset=utf-8'});
res.end({
message:'token is error'
})
}
})
});
});
前端请求:
现在在来看概念:
HTTP通信是无状态的,因此客户端的请求到了服务端处理之后是无法返回给原来的客户端的。so,需要对访问的客户进行识别。
常用的做法是session机制:客户端在服务端登陆成功之后,服务端会生成一个sessionID,返回给客户端,客户端将sessionID保存到cookie中,再次发起请求的时候,携带cookie中sessionID到服务端,服务端会缓存该session,当客户端请求到来的时候,服务端就知道是哪个用户的请求,并将处理的结果返回给客户端,完成通信。
通过上面的分析,可以知道session存在以下问题:
- session保存在服务端,当客户访问量增加时,服务端就需要存储大量的sesion会话,对服务器又很大的考验;
所以这里采用了JSON Web Token,JSON Web Token是怎么做的?
1.客户端通过用户名和密码登录服务器
2.服务端对客户端进行身份验证
3.服务端对该用户生成Token,返回给客户端
4.客户端将Token保存到本地浏览器,一半保存cookie中
5.客户端发起请求,需要携带该tooken
6.服务端收到请求后,首先验证该Token,之后返回数据。
服务端不需要存储token,只需要对Token中携带的信息进行验证即可;
无论客户端访问后台的哪台服务器,只要可以通过用户信息的验证即可。
参考资料:https://blog.csdn.net/weixin_44036436/article/details/102004739