web应用中常见的几种鉴权凭证形式以及它们的区别
cookie
特点:
- 存储于客户端,分为内存客户端(请求时)和硬盘客户端(根据过期时长)
- 单一域名,可以指定domain、path
属性:
1.name=value 以键值对的形式设置cookie
js
// 设置cookie
document.cookie = 'code=20211222160606';
// 获取cookie
console.log(document.cookie);
2.expire、max-age 设置过期时间
js
// max-age 以秒为单位
document.cookie = 'code=20211222160606;max-age=3600';
// expires date-in-GMTString-format如果未定义则在会话结束时过期
var now = new Date();
var time = now.getTime();
var expireTime = time + 24*60*60*100;
now.setTime(expireTime);
document.cookie = `code=20211222160606;expires=${now.toGMTString()}`;
3.服务端设置cookie,以koa为例
js
loginRouter.post('/login', ctx => {
ctx.cookies.set('code', '20211222160606', {
maxAge: 3600,
// signed: '',
// expires: '',
// path: '/xxx',
// domain: 'xxx',
// secure: false,
// httpOnly: true,
// overwrite: true
})
// ...
})
loginRouter.post('/index', ctx => {
const code = ctx.cookies.get('code');
// ...
})
session
session使用基于cookie在服务端生成sessionId并在请求中携带的状态记录机制
js
const Session = require('koa-session');
// session配置初始化
const session = Session({
key: 'sid',
maxAge: 10 * 1000,
signed: true, // 是否使用加密签名
}, app);
app.keys = ["28b1bd94-630a-11ec-90d6-0242ac120003"];
app.use(session);
loginRouter.post('/login', ctx => {
ctx.session.info = {
label: 'xxx',
code: 'xxx'
};
// ...
})
otherRouter.post('/info', ctx => {
const { label, code } = ctx.session.info;
// ...
})
cookie、session的区别和缺点
- session因为存储在服务端相对于cookie明文传输较为安全
- cookie只支持字符串类型,session则没有
- cookie的过期时间长于session
- cookie最多支持4kb,session虽然没限制但是对于分布式大量请求则会消耗更多的资源
- 对于浏览器外的客户端则需要额外手动设置cookie、session
tokenjwt
较之cookie和session目前采用最多的是token形式,客户端鉴权成功后由服务端颁发令牌在后续请求中携带,并在路由匹配时校验
特点
- 基于复杂的加密算法使得安全性更高
- 支持移动端设备
- 支持跨程序调用
JWT组成
header
- alg:采用的加密算法,默认是 HMAC SHA256(HS256),采用同一个密钥进行 加密和解密;(可选用rsa非对称加密)
- 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;
使用jsonwebtoken库生成jwt
非对称加密
HS256单一密钥因为适用统一密钥进行颁布、验证所以采用RSA非对称加密。
- 使用私钥(private key)生成token
- 使用公钥(public key)验证token
shell
// 使用openssl生成公钥私钥对
genrsa -out private.key 1024
rsa -in private.key -pubout -out public.key
生成、验证token
js
const jwt = require('jsonwebtoken');
const PRIVATE_KEY = fs.readFileSync('your generate private key path');
const PUBLIC_KEY = fs.readFileSync('your generate public key path');
// 生成token
loginRouter.post('/login', ctx => {
const info = {
name: 'xxx',
code: 123
}
const token = jwt.sign(info, PRIVATE_KEY, {
algorithm: 'RS256', // 使用rsa非对称加密
expiresIn: 60 * 60, // 数字或字符串表示token过期时间,数字类型单位ms
// ...具体参数
// https://github.com/auth0/node-jsonwebtoken
})
ctx.body = token;
})
// 验证token
loginRouter.post('/info', ctx => {
const authorization = ctx.headers.authorization;
const token = authorization.replace("Bearer ", "");
try {
const result = jwt.verify(token, PUBLIC_KEY, {
algorithms: ["RS256"]
});
ctx.body = result;
} catch (error) {
console.log(error.message);
ctx.body = "token is invalid~";
}
})