Token验证的基本流程
1.服务端收到请求,去验证用户名与密码
2.验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
3.客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
4.客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
5.服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
Node.js 实现token,需要的插件
//生成token与验证
1.jsonwebtoken
npm install jsonwebtoken --save
* 生成token的方法 sign
* 验证token的方法 verify
2.express-jwt
npm install express-jwt --save
* 验证token是否过期并规定那些路由不需要验证 express-jwt({})
JWT标准的Token有如下三个部分
- header (头部)
- payload (数据)
- signature (签名)
形式如下
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInVzZXJfaWQiOjEsImlhdCI6MTU5NDI2MjQ5NSwiZXhwIjoxNTk0MzQ4ODk1fQ.1MJ_MAFgpBjOjpggj69Xz8F_evBcMAenRK_7a8fdVrc
具体实现过程
-
目录格式
目录格式.png - 在目录下新建token文件夹,并创建token.js文件
var jwt = require('jsonwebtoken');
var jwtScrect = 'zgs_first_token'; //签名
//登录接口 生成token的方法
var setToken = function (user_name, user_id) {
return new Promise((resolve, reject) => {
//expiresln 设置token过期的时间
//{ user_name: user_name, user_id: user_id } 传入需要解析的值( 一般为用户名,用户id 等)
const token = jwt.sign({ user_name: user_name, user_id: user_id }, jwtScrect, { expiresIn: '24h' });
resolve(token)
})
}
//各个接口需要验证token的方法
var getToken = function (token) {
return new Promise((resolve, reject) => {
if (!token) {
reject({
error: 'token空'
})
}
else {
//第二种 改版后的
var info = jwt.verify(token.split(' ')[1], jwtScrect);
resolve(info); //解析返回的值(sign 传入的值)
}
})
}
module.exports = {
setToken,
getToken
}
!!!在app.js中自定义方法 验证token是否过期,如果没过期,则解析出用户信息返回req.data, 通过req.data判断token
//引入插件
var vertoken=require('./token/token')
var expressJwt=require('express-jwt')
//解析token获取用户信息
app.use(function(req, res, next) {
//注意此处token;authorization设置要与设置一致否则可能出现token为空
var token = req.headers['authorization'];
if(token == undefined){
return next();
}else{
vertoken.getToken(token).then((data)=> {
req.data = data; //解析成功后返回设置基本信息(通过req.data判断是否过期)
return next();
}).catch((error)=>{
return next();
})
}
});
//验证token是否过期并规定那些路由不需要验证
app.use(expressJwt({
secret:'zgs_first_token',
algorithms:['HS256']
}).unless({
path:['/login','/register'] //不需要验证的接口名称
}))
//设置托管静态目录; 项目根目录+ public.可直接访问public文件下的文件eg:http://localhost:3000/images/url.jpg
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/', usersRouter);
//token失效返回信息
app.use(function(err,req,res,next){
if(err.status==401){
return res.status(401).send('token失效')
//可以设置返回json 形式 res.json({message:'token失效'})
}
})
- 在登陆时生成token
//引入token
var vertoken=require('../token/token')
/* user login. */
router.post('/login', function (req, res) {
//获取参数
const params = [];
params[0] = req.body.user_name
params[1] = req.body.password
console.log(params)
if (params[0] === '' || params[1] === '') {
return res.json({
code: 1,
message: '账户或密码不能为空'
})
} else {
// 查询数据是否存在数据库中(user表没有设置索引)
connection.query(sqlobj.sqls.login, params, function (err, result) {
if (err) {
throw err;
} else {
if (result.length != 0) { //else判断哪里出错
console.log(result)
//调用生成token的方法
vertoken.setToken(result[0].user_name,result[0].user_id).then(token=>{
return res.json({
code: 200,
message: '登录成功',
token:token
//前端获取token后存储在localStroage中,
//**调用接口时 设置axios(ajax)请求头Authorization的格式为`Bearer ` +token
})
})
}
}
}
});
- 验证token 及调用
//sql语句
var sqls={
login: "select * from users where user_name = ? and password = ? ",
users:'select user_name,password from users where user_name = ?',
}
//封装需要token验证的方法,
var conn_query = function (req, res, sql, params) {
connection.query(sql, params, function (err, result) {
if (err) {
//遇到过的错误(可直接throw err)
let messageError = '';
if (err.errno == 1048) {
messageError = '参数不能为空'
} else if (err.errno == 1062) {
messageError = '数据已存在'
} else if (err.errno == 1265) {
messageError = '格式错误'
}
else {
messageError = '请求失败'
}
return res.json({
code: err.errno,
message: messageError
})
} else {
//第二种 改版后req.data 是验证后的返回值 (生成token传的数据)
if (req.data) {
return res.json({
code: 200,
message: 'success',
data: result
})
} else {
return res.json({
code: 1,
status: 401,
message: "error"
})
}
}
})
};
//导出
module.exports = {
sqls,
conn_query
}
/#在其他文件接口中调用#/
// 先引入该文件
var sqlobj = require('../db/sql'); //目录结构在上方
router.post('/api/addWebsite', function (req, res, next) {
const params = [req.body.name, req.body.url, req.body.alexa, req.body.country];
sqlobj.conn_query(req, res, sqlobj.sqls.addWebsite, params)
})
写在最后
在token失效时返回status:401,前端清除token 根据status状态码返回登录页面
完整代码 Github