Koa implementation of login credentials cookie, session, token

One. cookie

 1.1. Understanding cookies

1.2. Common attributes of cookies

1.3. Cookie client settings

  • Set cookies via js in the browser (rarely used in development)
  • When max-age is not set, it is a memory cookie, which will disappear when the browser is closed
  • When setting max-age, it is a hard disk cookie, which can only be destroyed when the expiration time is reached
document.cookie = "name=why;max-age=30;"
document.cookie = "age=18;max-age=60;"

1.4 cookie server settings

  • The server sets the cookie

Koa supports direct manipulation of cookies by default

  • The browser accepts the cookie, and the client saves

  • Other network requests in subsequent scopes will automatically carry cookies

  • The server gets the cookie and verifies the cookie

const Koa = require('koa')
const KoaRouter = require('@koa/router')

const app = new Koa()

const userRouter = new KoaRouter({ prefix: '/users' })

/**
 * 1.服务器设置cookie
 * 2.客户端(浏览器)保存cookie
 * 3.在同一个作用域下访问服务器, 自动携带cookie
 * 4.服务器验证客户端携带的cookie
 */
userRouter.get('/login', (ctx, next) => {
  // 在服务器中为登录的客户端, 设置一个cookie
  ctx.cookies.set('slogan', 'ikun', {
    maxAge: 60 * 1000 * 5
  })

  ctx.body = '登录成功~'
})

userRouter.get('/list', (ctx, next) => {
  // 验证用户的登录凭证: 携带口号 ikun
  const value = ctx.cookies.get('slogan')
  console.log(value)
  if (value === 'ikun') {
    ctx.body = `user list data~`
  } else {
    ctx.body = `没有权限访问用户列表, 请先登录~`
  }
})

app.use(userRouter.routes())
app.use(userRouter.allowedMethods())

app.listen(8000, () => {
  console.log('服务器启动成功~')
})

two, session

 2.1 The server uses session

  • cookie-based

  • The information and content in the cookie are encrypted and signed

  • Early login credentials: cookie+session

const Koa = require('koa');
const KoaRouter = require('@koa/router');
const koaSession = require('koa-session');

const app = new Koa();

const userRouter = new KoaRouter({ prefix: '/users' });

const session = koaSession (
  {
    key: 'sessionid', //cookie的key
    maxAge: 5 * 1000, //过期时间
    httpOnly: true, //不允许通过js获取cookie
    rolling: true, // 每次响应时,刷新session的有效期
    signed: true, // 是否使用signed签名认证,防止数据被篡改
  },
  app
);

// 加盐操作
app.keys = ['aaa', 'bbb', 'why', 'kobe'];
app.use(session);

userRouter.get('/login', (ctx, next) => {
  // 在服务器中为登录的客户端, 设置一个cookie
  ctx.session.slogan = 'ikun';

  ctx.body = '登录成功~';
});

userRouter.get('/list', (ctx, next) => {
  // 验证用户的登录凭证: 携带口号 ikun
  const value = ctx.session.slogan;
  console.log(value);
  if (value === 'ikun') {
    ctx.body = `user list data~`;
  } else {
    ctx.body = `没有权限访问用户列表, 请先登录~`;
  }
});

app.use(userRouter.routes());
app.use(userRouter.allowedMethods());

app.listen(8000, () => {
  console.log('服务器启动成功~');
});

2.2 Disadvantages of cookie+session

 

3. Issuance and verification of token

3.1   jsonwebtoken generated token

const jwt = require('jsonwebtoken') 
const token = jwt.sign(payload, secretkey, {
    expiresIn: 60
  })

 ​​​​​​

The Token generated by JWT consists of three parts:

header

  • alg: the encryption algorithm used, the default is HMAC SHA256 (HS256), using the same key for encryption and decryption (symmetric encryption)
  • typ: JWT, fixed value, usually written as JWT;
  • It will be encoded by the base64Url algorithm;


payload

  • The data carried, for example, we can put the user's id and name into the payload;
  • By default, it will also carry iat (issued at), the issuance time of the token
  • We can also set the expiration time : exp (expiration time)
  • It will be encoded by base64Url algorithm


signature 

  • Set a secretKey, and perform the HMACSHA256 algorithm by combining the results of the first two;
  • HMACSHA256(base64Url(header)+.+base64Url(payload),secretKey);
  • But if the secretKey is exposed, it is a very dangerous thing, because the token can be simulated later, and the token can also be decrypted

3.2 Get the token carried by the client

  const authorization = ctx.headers.authorization
  const token = authorization.replace('Bearer ', '')
  console.log(token)

  Postman simulates the client carrying token

 The actual token is in the headers.authorization field

 

3.3 Verify token and get user information

const result = jwt.verify(token, secretkey)

3.4 Disadvantages: Symmetric encryption 

The HS256 encryption algorithm used by default is a symmetric encryption algorithm. Encryption and decryption are the same key. Once the key is exposed, it is very dangerous:

  • For example, in a distributed system, each subsystem needs to obtain a key;
  • Then after getting the key, the subsystem can either issue or verify the token;
  • But for some resource servers, they only need to have the ability to verify tokens;

  Complete implementation code

const Koa = require('koa')
const KoaRouter = require('@koa/router')
const jwt = require('jsonwebtoken')

const app = new Koa()

const userRouter = new KoaRouter({ prefix: '/users' })

const secretkey = 'aaabbbccxxxx'

userRouter.get('/login', (ctx, next) => {
  // 1.颁发token
  const payload = { id: 111, name: 'why' }
  const token = jwt.sign(payload, secretkey, {
    expiresIn: 60   //过期时间
  })

  ctx.body = {
    code: 0,
    token,
    message: '登录成功, 可以进行其他的操作'
  }
})

userRouter.get('/list', (ctx, next) => {
  // 1.获取客户端携带过来的token
  const authorization = ctx.headers.authorization
  const token = authorization.replace('Bearer ', '')
  console.log(token)

  // 2.验证token
  try {
    const result = jwt.verify(token, secretkey)
    
    ctx.body = {
      code: 0,
      data: [
        { id: 111, name: 'why' },
        { id: 111, name: 'why' },
        { id: 111, name: 'why' },
      ]
    }
  } catch (error) {
    ctx.body = {
      code: -1010,
      message: 'token过期或者无效的token~'
    }
  }
})

app.use(userRouter.routes())
app.use(userRouter.allowedMethods())

app.listen(8000, () => {
  console.log('服务器启动成功~')
})

3.5. Asymmetric Encryption Token Issuance

Can use asymmetric encryption, RS256

  • Private key ( private key ): used to issue tokens
  • Public key ( public key ): used to verify the token

We can use openssl to generate a pair of private key and public key:
Mac can use the terminal terminal directly;
the default cmd terminal of Windows cannot be used directly, it is recommended to use the git bash terminal directly;

openssl
>
genrsa -out private.key 1024
>
rsa -in private.key -pubout -out public.key
  • Read private and public keys
const fs = require('fs')
const privateKey = fs.readFileSync('./keys/private.key')
const publicKey = fs.readFileSync('./keys/public.key')
  •  Issue token and encrypt with private key privateKey
const token = jwt.sign(payload, privateKey, {
    expiresIn: 60,   //token有效时间
    algorithm: 'RS256'  //加密算法
  })
  • Verify the token and decrypt it with the public key publickey
const result = jwt.verify(token, publicKey, {
      algorithms: ['RS256']
    })

 full code

const fs = require('fs')
const Koa = require('koa')
const KoaRouter = require('@koa/router')
const jwt = require('jsonwebtoken')

const app = new Koa()

const userRouter = new KoaRouter({ prefix: '/users' })

const privateKey = fs.readFileSync('./keys/private.key')
const publicKey = fs.readFileSync('./keys/public.key')

userRouter.get('/login', (ctx, next) => {
  // 1.颁发token
  const payload = { id: 111, name: 'why' }
  const token = jwt.sign(payload, privateKey, {
    expiresIn: 60,
    algorithm: 'RS256'
  })

  ctx.body = {
    code: 0,
    token,
    message: '登录成功, 可以进行其他的操作'
  }
})

userRouter.get('/list', (ctx, next) => {
  // 1.获取客户端携带过来的token
  const authorization = ctx.headers.authorization
  const token = authorization.replace('Bearer ', '')
  console.log(token)

  // 2.验证token
  try {
    const result = jwt.verify(token, publicKey, {
      algorithms: ['RS256']
    })
    
    ctx.body = {
      code: 0,
      data: [
        { id: 111, name: 'why' },
        { id: 111, name: 'why' },
        { id: 111, name: 'why' },
      ]
    }
  } catch (error) {
    console.log(error)
    ctx.body = {
      code: -1010,
      message: 'token过期或者无效的token~'
    }
  }
})

app.use(userRouter.routes())
app.use(userRouter.allowedMethods())

app.listen(8000, () => {
  console.log('服务器启动成功~')
})

Guess you like

Origin blog.csdn.net/m0_50789696/article/details/129862487