诶,
token是什么?
看看阮一峰怎么说:
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
hahah~分界线
我认为;token就是一把钥匙;通过用户的注册登录获取到两把token钥匙;放在浏览器里;后端给定一个协议;在获取某些信息时需要验证一下前端传过去的这把钥匙;匹配成功获取数据!
钥匙分成2把;一把是短时间token;主要用于获取数据时给给后台做验证的!而另一把长时间token的存在是为了让token更加的严谨;获取数据的钥匙会在短时间内过期,这样通过长时间的token就可以刷新短时间token;从而两者搭配,干活不累~;
so;
- 前端nodeJS写token(JWT)
- 实际案例中封装的token
一、前端nodeJS写token(JWT)
JWT(Json Web Tokens)
生成Token的解决方案有许多,常用的一种就是 Json Web Tokens .
JWT标准的Tokens由三部分组成
- header:包含token的类型和加密算法
- payload:包含token的内容
- signature:通过密钥将前两者加密得到最终的token
这三部分中间使用 " . " 分隔开,并且都会使用Base64编码方式编码,如下
eyJhbGc6IkpXVCJ9.eyJpc3MiOiJCIsImVzg5NTU0NDUiLCJuYW1lnVlfQ.SwyHTf8AqKYMAJc
客户端收到这个 Token 以后把它存储下来,下回向服务端发送请求的时候就带着这个 Token 。服务端收到这个 Token ,然后进行验证,通过以后就会返回给客户端想要的资源。
安装jsw模块
cnpm install jsonwebtoken -S
在js文件中引用jsw模块
var jwt = require(“jsonwebtoken”);
基础生成token
// 要生成token的主题信息【这里可以包含用户的一些相关信息,content需要为一个对象,否则有可能会报错】
var content = { msg: '{"username":"张三"}' };
// 这是加密的key(密钥或私钥)
var secretOrPrivateKey = "Odin"
var token = jwt.sign(content, secretOrPrivateKey, {
expiresIn: 60 * 1 // 24小时过期,以s作为单位
});
console.log("token:"+token);
var setToken=token;
基础验证token
//获取到token
var tokenVal = "获取的前端传来的token";//rq.body.token || rq.query.token || rq.headers["x-access-token"]; // 可以从body或query或者header中获取token;
tokenVal = setToken;
// 这是加密的key(密钥或私钥)
var secretOrPrivateKey = "Odin"
jwt.verify(tokenVal, secretOrPrivateKey, function (err, decode) {
if (err) { // 当token过期,或这是一个伪造的token,或这是无效的token时会触发此逻辑
console.log(err.message);
} else {
console.log(decode.msg); // {"username":"张三"}
}
})
模块化的封装;导出
//封装tokenObj
var tokenObj = {
//创建token
creataToken: function (boxVal, time) { // 内容,时间
var content = { msg: boxVal };
var secretOrPrivateKey = "Odin"
var token = jwt.sign(content, secretOrPrivateKey, {
expiresIn: time // 24小时过期,以s作为单位
});
return token;
},
//验证token
checkToken: function (tokenValue,fn) { // token值;导出函数
var secretOrPrivateKey = "Odin"; //密钥
jwt.verify(tokenValue,secretOrPrivateKey,function (err,decode) {
if (err) {
console.log(err);
} else {
console.log(decode.msg); // {"username":"张三"}
return fn(decode);//函数导出
}
})
}
}
//模块化的导出
exports.Obj={tokenObj:tokenObj};
前端调用
var token = require("./token.js");
console.log(token);
/*//token打印结果
{ Obj:
{ tokenObj:
{ creataToken: [Function: creataToken],
checkToken: [Function: checkToken] },
} }
*/
var tokenVal=token.Obj.tokenObj.creataToken("小红",60); // 创建一个token
console.log(tokenVal) // 获取到token(JWT)
token.Obj.tokenObj.checkToken(tokenVal,testOK) //验证token
function testOK(res){ // 验证成功
console.log(res);
console.log(res.msg);
}
nodejs实现token运用;
顺序是:
1、本地js发送ajax请求;nodejs向服务器发送数据;服务器响应返回结果;nodejs返回结果和token给本地js;
2、本地js储存token;需要时带上token向nodejs进行验证;nodejs验证成功后返回数据;
html.js
//创建token
$(".login").click(function(){
var userName = $(".userName").val();
var userSex = $(".userSex").val();
let userBox={
userName:userName,
userSex:userSex
}//获取到数据
Promise.all([ //发送ajax请求
$.ajax({
url: "http://127.0.0.1:8087/userBox/",
type: "post",
data:userBox,
dataType: "json",
})
]).then(
resolve => {
resGo(resolve)
},
reject => {
console.log(reject);
}
);
});
// 登陆成功后获取到token,本地存储
function resGo(res){
console.log(res);
console.log(res[0].tokenVal)
sessionStorage.setItem("key",res[0].tokenVal); //本地储存token
}
nodejs(基础设置)
//基础设置
//创建数据库链接(单独部分)
var mysql = require('mysql'); //导入mysql模块
function getcon() {
//创建数据库相关信息 mysql提供createConnection的链接方法
var connection = mysql.createConnection({
host: '127.0.0.1', //数据库地址
user: 'root', //数据库用户名
password: 'root', //数据库密码
database: 'dowork' //数据库名
});
return connection;//返回结果
}
//导出
module.exports=getcon;
-------------------------------------------------------分割线------------------------------------------------------------------------------------
//导入数据库
var getmySQL = require("./getmySQL");
//导入express模块
var getExpress = require("express");
var toTokenGo = require("../token.js")
//获取express方法
var app = getExpress();
//设置跨域访问(设置在所有的请求前面即可)
app.all("*", function (req, res, next) {
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin", "*");
//允许的header类型
res.header("Access-Control-Allow-Headers", "content-type");
//跨域允许的请求方式
res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
if (req.method == 'OPTIONS')
res.send(200); //让options尝试请求快速结束
else
next();
});
//插入post的body!!
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
nodejs(正式开始!之创建token)
检查一下是否导入sql,express,tokenjs,是否启动数据库
app.post('/userBox', function (req, res) {
var userName = req.body.userName;
var userSex = req.body.userSex;
console.log(userName, userSex)
var getmySQLCon = getmySQL();
//启动数据库
getmySQLCon.connect();
var sql = `SELECT * FROM userBox WHERE userName = '${userName}' && userSex = ${userSex}`;
getmySQLCon.query(sql, function (err, result) { //写入数据库
if (err) {
console.log("出错了:" + err.message);
res.send({
state: false,
des: "出错了:" + err.message
});
getmySQLCon.end();
return;
} else {
if (result.length == 0) {
res.send({
state: true,
des: "没有信息哦"
})
} else {
console.log(userName)
console.log(toTokenGo.Obj.tokenObj);
//导入模块调用
var tokenVal = toTokenGo.Obj.tokenObj.creataToken(userName, 60 * 10);
res.send({ //数据库返回
state: true,
des: result,
tokenVal: tokenVal //返回token咯~
})
}
}
getmySQLCon.end(); //关闭数据库
})
})
通过token验证,获取到数据
$(".getUser").click(function(){
// 获得token验证码
var tokenVal = sessionStorage.getItem("key");
// 后台交互,通过密钥提升安全性
Promise.all([
$.ajax({
url: "http://127.0.0.1:8087/userALL/",
type: "post",
data: {tokenVal:tokenVal}, //注意后台接口文档
dataType: "json",
})
]).then(
resolve => {
console.log(resolve)
},
reject => {
console.log(reject);
}
);
})
nodejs(开始啦!之验证token获取数据)
// token 验证成功后获取数据
app.post('/userALL', function (req, res) {
var tokenVal = req.body.tokenVal;
// console.log(tokenVal); 获取到token进行验证
toTokenGo.Obj.tokenObj.checkToken(tokenVal, goNext)
//token验证成功调用
function goNext(d) {
console.log(d);
var getmySQLCon = getmySQL();
//启动数据库
getmySQLCon.connect();
var sql = `SELECT * FROM userBox`;
getmySQLCon.query(sql, function (err, result) {
if (err) {
console.log("出错了:" + err.message);
res.send({
state: false,
des: "出错了:" + err.message
});
getmySQLCon.end();
return;
} else {
if (result.length == 0) {
res.send({
state: true,
des: "没有信息哦"
})
} else {
console.log(toTokenGo);
res.send({
state: true,
des: result,
})
}
}
getmySQLCon.end();
})
}
})
okε=ε=ε=( ̄▽ ̄)~
在实际中,后端已经写好了token;前端如何是好?
二、实际案例中封装的token
逻辑思路:我们通过按钮事件触发,在向后台登录注册和本地储存长时间和短时间的两种token;短时间token保质期只有5分钟;需要通过长时间的token去做刷新;刷新更新本地短时间token后在回调获取数据
var urlVal = "url基础路径";
// 保存到本地
sessionStorage.setItem("urlVal", "url基础路径");
// !!!!!!!!!!!需要token,并且是formdata格式!!!!!!!!!!!!!!!!!!!!!
//传输数据的give带token myTAjaxGive("get/post", "/url/", 传到数据库的对象, 结果函数的方法名);
function myTAjaxGive(type, url, data, fn) {
Promise.all([
$.ajax({
url: urlVal + url,
type: type,
data: data,
processData: false, //formdata格式要求
contentType: false,//formdata格式要求
headers: { "Authorization": "Bearer " + sessionStorage.getItem("tokenValSmall") }//带上头部
})
]).then(
resolve => { //成功的时候
// 判定 token 是否过期
if (resolve[0].code == 200) {
return fn(resolve);
} else if (resolve[0].code == 401) { //没有登录的时候
localStorage.href = "../page/login.html"
} else if (resolve[0].code == 601) { //5分钟过期了的时候获取新的token
dataBox = [myTAjaxGive, type, url, data, fn]; //防止异步调用导致本地来不及存上token时的回调函数
//AjaxGive(type,"url",长时间token,回调函数函数名,回调函数数据)
AjaxGive("post", "/token/refresh/", { refresh: sessionStorage.getItem("tokenValLong") }, TokenNew, dataBox)//我就自动的再去请求下新的
}
},
reject => {
rejectTime(reject);
}
);
}
//!!!!!!!!!!!!!!!!!!!需要token,普通格式!!!!!!!!!!!!!!!!!!!!!
// 传输普通数据
function TAjaxGive(type, url, data, fn) {
Promise.all([
$.ajax({
url: urlVal + url,
type: type,
data: data,
headers: { "Authorization": "Bearer " + sessionStorage.getItem("tokenValSmall") }
})
]).then(
resolve => { //成功的时候
// 判定 token 是否过期
if (resolve[0].code == 200) {
fn(resolve);
} else if (resolve[0].code == 401) { //没有登录的时候
localStorage.href = "../page/login.html"
} else if (resolve[0].code == 601) { //5分钟过期了的时候获取新的token
dataBox = [TAjaxGive, type, url, data, fn];
AjaxGive("post", "/token/refresh/", { refresh: sessionStorage.getItem("tokenValLong") }, TokenNew, dataBox);
}
},
reject => {
rejectTime(reject);
}
);
}
刷新token AjaxGive(type, url, data, fn, fn_data)
//过期刷新token
function AjaxGive(type, url, data, fn, fn_data) {
Promise.all([
$.ajax({
url: urlVal + url,
type: type,
data: data
})
]).then(
resolve => { //成功的时候
fn(resolve, fn_data);
},
reject => {
rejectTime(reject)
}
);
}
回调函数TokengoNew(res, dataBox)
function TokengoNew(res, dataBox) {
sessionStorage.setItem("tokenValSmall", res[0].msg.access);
//为了避免异步造成的数据延时
if (sessionStorage.getItem("tokenValSmall")==res[0].msg.access) {
//执行回调
dataBox[0](dataBox[1], dataBox[2], dataBox[3]);
} else {
alert("请求失败")
}
}
ola~(。・∀・)ノ
其实我在Promise 时出现了异步调用的问题;至今没有很好的解决TT~
token还可以用在7天免登陆;只要后台设置的刷新token(也就是我说的长时间token能够设置到7天;)储存在cookie里面;用户登录的时候刷新就可以了;token的储存肯定是需要加密和隐藏的;后期可以多多改进一下下