jwt

一.前沿

jwt是json web token缩写。它将用户信息加密到token里,服务器不保存任何用户信息,服务器通过保存的密钥验证token的正确性,只要正确即通过验证。
优点是在分布式系统中,很好地解决了单点登录问题,很容易解决了session共享的问题。比如一个公司有很多相关联的网站,比如A,B,C。通常希望用户在登陆A网站以后,再访问B,C网站能够自动登录(也就是所谓的单点登陆)。
缺点是无法作废已颁布的令牌/不易应对数据过期。比如说,我登陆了A网站,再登录B网站,在token过期之前,token始终是有效的,无法废除token的有效性。

二.jwt原理

JWT的原理是服务器认证以后,生成一个下面所示的对象,发送给用户。

{
 name:hello,
 roles: 管理员,
 expires:2018年11月
}

以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。当然,出于安全的考虑,服务器不能发送明文的数据给用户,通常需要对这个对象进行加密。

三.jwt加密生成token

这里使用了jsonwebtoken来生成token
jwt.sign(payload, secretOrPrivateKey, [options, callback])
1.Payload 部分是一个 JSON 对象,用来存放实际需要传递的数据.比如:

{
  id:user.id,
  name:user.name
}

2.secretOrPrivateKey:是加密的名字,可以自己定义
3.第三个参数是可选的,主要包括加密方式,过期时间等,一般只需要设置过期时间expiresIn,加密方式默认使用HS256

                  const rule = {id:user.id,name:user.name};
                  jwt.sign(rule,keys.secretOrKey,{expiresIn:3600},(err,token) => {
                    if(err) throw err;
                    res.json({
                      success:true,
                      token:"Bearer " + token
                    });
                  })

token组成

最终生成的token如下图所示:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJuYW1lIjoiaGVsbG8iLCJpYXQiOjE1NDMzOTUyMDgsImV4cCI6MTU0MzM5ODgwOH0.
zFiGRNTST8RSY3e1JqWx0SA0S4BCOgNCVsUC4u9JHbY

可以观察到生成的token被 .划分开来,由三部分组成。这三部分分别是Header,
Payload,Signature
。其中:
Header是一个对象,描述 JWT 的元数据,通常是下面的样子。

{
  "alg": "HS256",
  "typ": "JWT"
}

上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。最后将上面的 JSON 对象使用 Base64URL 算法转成字符串,也就是token的第一部分

Payload也是一个对象,就是我们上面提到的需要传递的数据组成的对象

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)  

四.使用passport验证token(passport-jwt验证jwt类型的token)

passport通常是用来实现登陆验证。这里我们使用passport-jwt来进行验证jwt类型的token。
1.使用passport
app.js

const passport = require('passport');
app.use(passport.initialize());

const JwtStrategy = require('passport-jwt').Strategy,
  ExtractJwt = require('passport-jwt').ExtractJwt;
const opts = {}

//得到token
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
//设置token时使用的加密名字
opts.secretOrKey = 'secret';

passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
  console.log(jwt_payload);
}));

1. JwtStrategy用来定义策列,是在路由执行之前执行。
2. ExtractJwt里面包含很多函数,这里需要注意的是fromAuthHeaderAsBearerToken**是获取到请求时以Bearer +token得到的token。
注意:他只能获取到以Bearer+一个空格开头的token。因此我们这里的token必须以Bearer+ 一个空格开头。如下所示:

res.json(
{
   success:true,
   token:"Bearer "+token
})

3.opts对象里面定义的各个参数是我们之前创建token设置的参数
opts.jwtFromRequest:用于从request中获取token
opts.secretOrKey:是之前设置的加密名字

4.passport.use(new JwtStrategy(opts, function(jwt_payload, done) {}))使用passport.use()定义策略。
回调函数中的jwt_payload时一个包含id,name等的对象。

{ name: 'hello', id: 'hello', iat: 1543401227, exp: 1543404827 }

根据payload中的id,我们可以查询是否有这个对象。
回调函数的第二个参数done是一个函数(相当于next),这个函数第一个参数是err,第二个参数是user。我们可以通过done将user进行返回,这样的话在请求中就把user对象添加进去了。如下所示:

 passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
    // console.log(jwt_payload);
    User.findById(jwt_payload.id)
      .then((user) => {
        if(user){
          return done(null,user)
        }else{
          return done(null,false);
        }
      })
  }));

user.js
这样的话,在其他任何进行路由的地方都可以使用passport.authenticate进行验证.

router.get('/current',passport.authenticate('jwt',{ session: false }),(req,res) => {
  console.log(req.user);  //{ name: 'hello', id: 'hello', iat: 1543401227, exp: 1543404827 }
});

注意:非常重要的一点就是我们如果使用了passport进行了验证。那么就可以从请求中获取到user,当然前提是调用了done函数,返回了user。

你可能感兴趣的:(jwt)