基于jsonwebtoken(JWT) 的web认证 (Node版实现)

welldum 2019-06-27

什么是JSON Web Token ?

官方给出的定义是:
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、独立的方式,用于安全地在当事人之间传递信息作为一个JSON对象。这些信息可以被验证和信任,因为它是数字签名的。JWTs可以使用一个secret (使用HMAC算法)或使用RSA的公钥/私钥对来签名。 文中我们使用公私钥的加密方式。

结构

由下面三个部分组成一个token字符串

  • Header

    • header通常由两个部分组成:令牌的type,即JWT,以及正在使用的散列算法,如HMAC SHA256或RSA。
  • Payload ,包含三个类型,自定义部分可以存储一些信息,如:用户信息、角色等;

    • Registered claims:已注册的,具体属性,点击传送
    • Public claims:自定义的,具体属性,点击传送
    • Private claims:这是在同意使用它们的各方之间共享信息的自定义声明,并且既没有注册也没有公开声明。
  • Signature

    • 使用header、payload和secret生成一个签名

工作流程

这里直接使用官网的图
基于jsonwebtoken(JWT) 的web认证 (Node版实现)

他的优点是什么?

  1. 使用json传输数据,因此在编码时,也会更小
  2. 更安全
  3. 无需在服务端保存相关信息,只需要验证token是否有效即可

代码实现

这里使用express来创建一个服务
  1. 创建一个入口文件,主要用于一些中间件的挂载

    //index.js
     const express = require('express')
    const app  = express()
    // 使用body-parser获取请求body
    const bodyParser = require('body-parser');
    const cookieParser = require('cookie-parser')
    
    
    // 引入路由中间件
    const routerAdmin = require('./app/router-admin')
    
    app.use(bodyParser.json()); // for parsing application/json
    app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
    app.use(cookieParser())
    
    // 加载路由模块
    app.use('/admin',routerAdmin);
    
    app.listen(3000,function(){
        console.log('port 3000 start')
    })
  2. 然后创建一个 app/router-admin.js 生成需要用到认证的路由中间件,这里我是在mongodb中取的用户信息,也可以自行模拟数据

    //router-admin.js 
    
    const express = require('express')
    const router = express.Router()
    var MongoClient = require('mongodb').MongoClient;
    
    const jwt = require('jsonwebtoken')
    const fs = require('fs')
    // const secretStr = 'sdfsjfklsjfiewjwoieow'
    
    // 根据mongodb生成的_id查询数据
    var ObjectId = require('mongodb').ObjectId
    var url = "mongodb://localhost:27017/";
    
    var payload = {
        user : 'william',
        admin : true
    }
    
    router.post('/login',function(req,res){
        let name = req.body.name,
            pwd = req.body.pwd;
        var cert = fs.readFileSync('./private.key')
        MongoClient.connect(url, function(err, db) {
            if (err) throw err;
            var dbo = db.db("blog");
            var noSqlStr = {name:name,pwd:pwd}
            dbo.collection("userlist"). find(noSqlStr).toArray(function(err, result) { // 返回集合中所有数据
                if (err) throw err;
                // console.log(result);
                // 验证通过,服务端回传token
                if(!!result.length){
                    var token = jwt.sign(payload, cert, { algorithm: 'RS256' ,expiresIn:'30s'});
                    res.send({
                        status : true,
                        msg : '',
                        token : token
                    })
                }          
                db.close();
            });
        });
    })
    
    // 验证jsonwebtoken是否过期的中间件,在login接口后面执行,除了login接口的请求外,其他接口都需要验证token
    router.use(function jwtVerify(req, res, next) {
        let token = req.get('token')
        console.log(token)
        var cert = fs.readFileSync('./public.key');  
        // 先解密
        jwt.verify(token, cert,function(err,decoded){
            if(err || !decoded) res.send({data:null,status:false,msg:err})
            
            if(decoded.user == payload.user){
                next();
            }
            
        });
    });
    
    router.get('/search',function(req,res){
        res.send({data:'查询成功',msg:'',status:true})
    })
    
    
    module.exports = router
  3. 因为这里使用的是公私钥的加密方式,需使用ssh-keygen生成公私钥

    私钥生成:ssh-keygen -t rsa -b 2048 -f private.key
        公钥生成:openssl rsa -in private.key -pubout -outform PEM -out public.key

完整项目地址

参考

  1. https://jwt.io/introduction/
  2. https://tools.ietf.org/html/r...

相关推荐