web鉴权cookie session token(jwt)的区别

/post/web-authorization article cover image

web应用中常见的几种鉴权凭证形式以及它们的区别

特点:

  1. 存储于客户端,分为内存客户端(请求时)和硬盘客户端(根据过期时长)
  2. 单一域名,可以指定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的区别和缺点

  1. session因为存储在服务端相对于cookie明文传输较为安全
  2. cookie只支持字符串类型,session则没有
  3. cookie的过期时间长于session
  4. cookie最多支持4kb,session虽然没限制但是对于分布式大量请求则会消耗更多的资源
  5. 对于浏览器外的客户端则需要额外手动设置cookie、session

tokenjwt

较之cookie和session目前采用最多的是token形式,客户端鉴权成功后由服务端颁发令牌在后续请求中携带,并在路由匹配时校验

特点

  1. 基于复杂的加密算法使得安全性更高
  2. 支持移动端设备
  3. 支持跨程序调用

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~";
  }
})