传统基于服务器的验证方式
传统的验证方式是基于服务器的,就是把登陆信息存在服务端,每次登陆需要去辨别存储的登陆信息,一般都是通过session来实现, 这样会有一些问题,比如每次认证用户发起请求时,服务器都需要创建一个用记录来存储信息,当越来越多的用户发起请求时,内存的开销也会不断的增加
基于token的验证原理
基于token的身份验证是无状态的,我们不用将信息存储在服务器或者session中。token通过请求头传输,而不是把认证信息存储在服务器或者session中,就意味着可以从任意一种可以发送HTTP请求的终端向服务器发送请求
实现步骤
- 登陆时,客户端发送用户名密码
- 服务端验证用户名密码是否正确,校验通过就会生成一个有时效的token串,发送给客户端
- 客户端储存token,一般都会存储在localStorage或者cookie里面
- 客户端每次请求时都带有token,可以将其放在请求头里,每次请求都携带token
- 服务端验证token,所有需要校验身份的接口都会被校验token,若token解析后的数据包含用户身份信息,则身份验证通过,返回数据
node + jwt(jsonwebtoken) 搭建token身份验证
npm i jsonwebtoken --save // 安装jsonwebtoken模块
// 引入模块依赖
const fs = require('fs');
const path = require('path');
const jwt = require('jsonwebtoken');
// 创建 token 类
class Jwt {
constructor(data) {
this.data = data;
}
//生成token
generateToken() {
let data = this.data;
let created = Math.floor(Date.now() / 1000);
let cert = fs.readFileSync(path.join(__dirname, '../pem/private_key.pem'));//私钥 可以自己生成
let token = jwt.sign({
data,
exp: created + 60 * 30,
}, cert, {algorithm: 'RS256'});
return token;
}
// 校验token
verifyToken() {
let token = this.data;
let cert = fs.readFileSync(path.join(__dirname, '../pem/public_key.pem'));//公钥 可以自己生成
let res;
try {
let result = jwt.verify(token, cert, {algorithms: ['RS256']}) || {};
let {exp = 0} = result, current = Math.floor(Date.now() / 1000);
if (current <= exp) {
res = result.data || {};
}
} catch (e) {
res = 'err';
}
return res;
}
}
module.exports = Jwt;
使用jwt token工具
/ 引入jwt token工具
const JwtUtil = require('../public/utils/jwt');
// 我这里的是aes加密密码的可以去掉
const AesUtil = require('../public/utils/aes');
// 登录
router.post('/login',(req,res) => {
var userName = req.body.user;
var pass = req.body.pass;
new Promise((resolve, reject) => {
// 根据用户名查询用户
users.findOne({'username':userName}).exec((err,result) => {
if(err){
reject(err);
}else{
resolve(result);
}
});
}).then((result) => {
console.log(result);
if(result){
// 密码解密 利用aes
var aes = new AesUtil(result.password);
var password = aes.deCryto();
if(pass == password){
// 登陆成功,添加token验证
let _id = result._id.toString();
// 将用户id传入并生成token
let jwt = new JwtUtil(_id);
let token = jwt.generateToken();
// 将 token 返回给客户端
res.send({status:200,msg:'登陆成功',token:token});
}else{
res.send({status:400,msg:'账号密码错误'});
}
}else{
res.send({status:404,msg:'账号不存在'})
}
}).catch((err) => {
console.log(err);
res.send({status:500,msg:'账号密码错误'});
})
});
app.use(function (req, res, next) {
// 我这里知识把登陆和注册请求去掉了,其他的多有请求都需要进行token校验
if (req.url != '/user/login' && req.url != '/user/register') {
let token = req.headers.token;
let jwt = new JwtUtil(token);
let result = jwt.verifyToken();
// 如果考验通过就next,否则就返回登陆信息不正确
if (result == 'err') {
console.log(result);
res.send({status: 403, msg: '登录已过期,请重新登录'});
// res.render('login.html');
} else {
next();
}
} else {
next();
}
});
前端请求封装
export default class FetchAsync {
// get
static getFatch(url) {
let geturl = url;
return new Promise((resolve, reject) => {
var url = 'http://127.0.0.1:3001/' + geturl;
fetch(url, {
method: 'GET',
header: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Token': localStorage.getItem('token')
},
}).then((response) => {
if (response.ok) {
return response.json();
} else {
reject({status: response.status})
}
}).then((res) => {
resolve(res)
}).catch((err) => {
reject(err)
})
}
)
}
// post
static postFatch(url, params) {
console.log(params);
var url = 'http://127.0.0.1:3001/' + url;
return new Promise((resolve, reject) => {
fetch(url, {
method: 'POST',
headers: {
"Content-Type": "application/json;charset=utf-8",
'Token': localStorage.getItem('token')
},
body: JSON.stringify(params)
}).then(response => response.json()).then((res) => {
resolve(res);
}).catch((err) => {
reject(err)
});
}
)
}
}
公钥可以使用openssl通过命令来生成
- window下生成命令
公钥生成 genrsa -out rsa_private_key.pem 1024
私钥生成 rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
- mac下生成命令
私钥生成 openssl genrsa -out rsa_private_key.pem 1024
公钥生成 openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
使用node自带的加密模块crypto
const crypto = require('crypto')
function md5(content) {
crypto.createHash('md5').update(content).digest('hex')
}
网友评论