一.cookie
1.1. 认识cookie
1.2. cookie常见属性
1.3. cookie客户端设置
- 在浏览器中通过js设置cookie(在开发中很少使用)
- 没有设置max-age时, 是一个内存cookie, 浏览器关闭时会消失
- 设置max-age时, 是一个硬盘cookie, 只能等到过期时间到达的时候, 才会销毁
document.cookie = "name=why;max-age=30;"
document.cookie = "age=18;max-age=60;"
1.4 cookie服务器设置
-
服务器设置cookie
Koa 中默认支持直接操作 cookie
-
浏览器接受cookie, 并且客户端保存
-
在之后的作用域其他网络请求, 会自动携带cookie
-
服务器获取cookie, 并且验证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('服务器启动成功~')
})
二、session
2.1 服务器使用session
-
基于cookie
-
cookie中信息和内容进行加密处理, 加密签名
-
早期登录凭证: 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 cookie+session缺点
三、 token的颁发和验证
3.1 jsonwebtoken 生成token
const jwt = require('jsonwebtoken')
const token = jwt.sign(payload, secretkey, {
expiresIn: 60
})
JWT生成的 Token 由三部分组成:
header
- alg:采用的加密算法,默认是 HMAC SHA256 (HS256 ),采用同一个密钥进行加密和解密(对称加密)
- typ:JWT ,固定值,通常都写成 JWT 即可;
- 会通过base64Url 算法进行编码;
payload
- 携带的数据,比如我们可以将用户的id 和 name 放到 payload 中;
- 默认也会携带 iat (issued at ),令牌的签发时间
- 我们也可以设置过期时间:exp (expiration time)
- 会通过base64Url 算法进行编码
signature
- 设置一个secretKey ,通过将前两个的结果合并后进行 HMACSHA256 的算法;
- HMACSHA256(base64Url(header)+.+base64Url(payload),secretKey);
- 但是如果secretKey 暴露是一件非常危险的事情,因为之后就可以模拟颁发 token ,也可以解密 token
3.2 获取客户端携带的token
const authorization = ctx.headers.authorization
const token = authorization.replace('Bearer ', '')
console.log(token)
postman 模拟客户端携带token
实际 token 在 headers.authorization 字段中
3.3 验证 token,获取用户信息
const result = jwt.verify(token, secretkey)
3.4 缺点: 对称加密
默认使用的HS256 加密算法是对称加密算法,加密和解密都是同一个密钥,一旦密钥暴露就是非常危险的事情:
- 比如在分布式系统中,每一个子系统都需要获取到密钥;
- 那么拿到这个密钥后这个子系统既可以发布另外,也可以验证令牌;
- 但是对于一些资源服务器来说,它们只需要有验证令牌的能力就可以了;
完整实现代码
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. 非对称加密颁发令牌
可以使用非对称加密,RS256
- 私钥( private key ):用于发布令牌
- 公钥( public key ):用于验证令牌
我们可以使用 openssl 来生成一对私钥和公钥:
Mac 直接使用 terminal 终端即可;
Windows默认的 cmd 终端是不能直接使用的,建议直接使用 git bash 终端;openssl > genrsa -out private.key 1024 > rsa -in private.key -pubout -out public.key
- 读取私钥和公钥
const fs = require('fs')
const privateKey = fs.readFileSync('./keys/private.key')
const publicKey = fs.readFileSync('./keys/public.key')
- 颁发 token ,用私钥 privateKey 进行加密
const token = jwt.sign(payload, privateKey, {
expiresIn: 60, //token有效时间
algorithm: 'RS256' //加密算法
})
- 验证 token 用公钥 publickey 进行解密
const result = jwt.verify(token, publicKey, {
algorithms: ['RS256']
})
完整代码
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('服务器启动成功~')
})