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('服务器启动成功~')
})