node.js +express 实现token 验证

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容