Cookie、Session、JWT

一.课程主题

权限校验

二.课程规划

  • cookie+session 在koa中的应用
  • jwt 和 cookie实战
  • 实现简易express

三.知识点

cookie+session实现登录权限

yarn add koa koa-router koa-views koa-bodyparser koa-session
1
let Koa = require('koa');
let Router = require('koa-router');
let bodyparser = require('koa-bodyparser');
let views = require('koa-views');
let session = require('koa-session');
let router = new Router()
let app = new Koa();
app.use(bodyparser());
app.use(session({},app));
app.keys = ['zf'];
app.use(views(__dirname+'/views',{
    map:{
        'html':'ejs'
    }
}));
router.get('/',async ctx=>{ // 默认访问首页
    await ctx.render('home.html');
});
router.post('/list',async ctx=>{
    let {username,password} = ctx.request.body;
    if(username === password){ // 用户名和密码相等就认为用户登录过了
        ctx.session.user = {username};
        ctx.redirect('/list')
    }else{
        ctx.redirect('/');
    }
});
router.get('/list',async ctx=>{ // 登陆后才能访问 获取列表页面
    let {username} = ctx.session.user || {};
    if(username){
        let r = await ctx.render('list.html',{username});
        console.log(r)
    }else{
        ctx.redirect('/');
    }
})
app.use(router.routes());
app.listen(4000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

1.什么是jwt?

  • JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案

    解决问题:session不支持分布式架构,无法支持横向扩展,只能通过数据库来保存会话数据实现共享。如果持久层失败会出现认证失败。

    优点:服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展。

JWT包含了使用.分隔的三部分

  • Header 头部

    { "alg": "HS256", "typ": "JWT"}   
    // algorithm => HMAC SHA256
    // type => JWT
    
    1
    2
    3
  • Payload 负载、载荷

    JWT 规定了7个官方字段
    iss (issuer):签发人
    exp (expiration time):过期时间
    sub (subject):主题
    aud (audience):受众
    nbf (Not Before):生效时间
    iat (Issued At):签发时间
    jti (JWT ID):编号
    
    1
    2
    3
    4
    5
    6
    7
    8
  • Signature 签名

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

    HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret)
    
    1
    2
    3
    4

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+/=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-/替换成_ 。这就是 Base64URL 算法。

2.使用方式

HTTP 请求的头信息Authorization字段里面

Authorization: Bearer <token>
1

通过url传输

http://www.xxx.com/pwa?token=xxxxx
1

如果是post请求也可以放在请求体中

3.实际应用

let Koa = require('koa');
let Router = require('koa-router');
let bodyparser = require('koa-bodyparser');
let jwt = require('jwt-simple');
let router = new Router()
let app = new Koa();
app.use(bodyparser())
let secret = 'zfpx';
// 验证是否登陆
router.post('/login',async(ctx)=>{ 
    let {username,password} = ctx.request.body;
    if(username === 'admin' && password === 'admin'){
       let token =  jwt.encode(username,secret);
       ctx.body = {
            code:200,
            username,
            token,
       }
    }
});
// 验证是否有权限
router.get('/validate',async(ctx)=>{ 
    let Authorization = ctx.get('authorization')
    let [,token] = Authorization.split(' ');
    if(token){
        try{
            let r = jwt.decode(token,secret);
            ctx.body = {
                code:200,
                username:r,
                token
            }
        }catch(e){
            ctx.body = {
                code:401,
                data:'没有登陆'
            }
        }
    }else{
        ctx.body = {
            code:401,
            data:'没有登陆'
        }
    }
  
});
app.use(router.routes());
app.listen(4000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

4.原理实现

let myJwt = {
    sign(content,secret){
        let r = crypto.createHmac('sha256',secret).update(content).digest('base64');
        return this.base64urlEscape(r)
    },
    base64urlEscape(str){
        return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
    },
    toBase64(content){
        return this.base64urlEscape(Buffer.from(JSON.stringify(content)).toString('base64'))
    },
    encode(username,secret){
        let header = this.toBase64({ typ: 'JWT', alg: 'HS256' });
        let content = this.toBase64(username);
        let sign = this.sign([header,content].join('.'),secret);
        return  [header,content,sign].join('.')
    },
    base64urlUnescape(str) {
        str += new Array(5 - str.length % 4).join('=');
        return str.replace(/\-/g, '+').replace(/_/g, '/');
    },
    decode(token,secret){
        let [header,content,sign] = token.split('.');
        let newSign = this.sign([header,content].join('.'),secret);
        if(sign === newSign){
            return Buffer.from(this.base64urlUnescape(content),'base64').toString();
        }else{
            throw new Error('被篡改')
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

jsonwebtoken https://www.npmjs.com/package/jsonwebtoken