Koa 鉴权实战 - Session/Cookie

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Koa 鉴权实战 - Session/Cookie

Session/Cookie

cookie 是如何工作的

// cookie.js
const http = require('http');

http
  .createServer((req, res) => {
    if (req.url === '/favicon.ico') {
      res.end('favicon.ico');
      return;
    }
    // 获取 Cookie
    console.log(`cookies: ${req.headers.cookie}`);

    // 设置 Cookie
    res.setHeader('Set-Cookie', 'cookie1=a');
    res.end('Hello Cookie');
  })
  .listen(3000);

cookie 缺点

  • 空间太小
  • 不安全

简易 session 实现

session 会话机制是一种服务器端机制,使用类似于哈希表的结构来保存信息
实现原理:
  1. 服务器在接受客户端首次访问时在服务器端创建 session,然后保存 session (保存在内存或 redis 中),然后给这个 session 生成一个唯一的标识字符串(uuid),然后在响应头中设置该 uuid
  2. 签名,通过密钥对 sid 进行签名处理,避免客户端修改 sid (非必需步骤)
  3. 浏览器中收到请求响应的时候解析响应头,然后将 sid 保存在本地 cookie 中,浏览器下次发起 http 请求时会带上该域名下的 cookie 信息
  4. 服务器在接受客户端请求时会解析请求头 cookie 中的 sid,然后根据这个 sid 去找服务器端保存的该客户端的 session,然后判断请求是否合法
// cookie.js
const http = require('http');

const session = {};

http
  .createServer((req, res) => {
    if (req.url === '/favicon.ico') {
      res.end('favicon.ico');
      return;
    }
    // 获取 Cookie
    console.log(`cookies: ${req.headers.cookie}`);

    const sessionKey = 'sid';
    const cookie = req.headers.cookie;

    if (cookie && cookie.indexOf(sessionKey) > -1) {
      res.end('Second Request');
      // 获取 Cookie 中的信息
      const pattern = new RegExp(`${sessionKey}=([^;]+);?\s*`);
      const sid = pattern.exec(cookie)[1];
      console.log(`session: ${sid}, ${JSON.stringify(session[sid])}`);
    } else {
      const sid = (Math.random() * 999999).toFixed();
      // 设置 Cookie
      res.setHeader('Set-Cookie', `${sessionKey}=${sid}`);
      session[sid] = { name: 'cell' };
      res.end('Hello Session');
    }
  })
  .listen(3000);

在 koa 中使用 session

// app.js
const koa = require('koa');
const session = require('koa-session');

const app = new koa();

// 签名 key
app.keys = ['my secret'];

// 配置项
const SESS_CONFIG = {
  key: 'cilab:sess', // cookie 键名
  maxAge: 24 * 60 * 60 * 1000, // 1 day
  httpOnly: true, // 仅限服务器修改 
  signed: true // 对 cookie 进行签名
};

app.use(session(SESS_CONFIG, app));

app.use(ctx => {
  if (ctx.path === '/favicon.ico') return;
  // 获取
  let n = ctx.session.count || 0;

  // 设置
  ctx.session.count = ++n;
  ctx.body = `request ${n} times`;
});

app.listen(3000);

使用 redis 进行持久化处理

// app.js
const koa = require('koa');
const session = require('koa-session');
const redis = require('redis');
const redisStore = require('koa-redis');
const wrapper = require('co-redis');

const app = new koa();

const redisClient = redis.createClient(6379, 'localhost');
const client = wrapper(redisClient);

// 签名 key
app.keys = ['my secret'];

app.use(session({
  key: 'cilab:sess',
  store: redisStore({ client })
}, app));

app.use(async (ctx, next) => {
  const keys = await client.keys('*');
  keys.forEach(async key => {
    console.log(await client.get(key));
  });
  await next();
});

app.use(ctx => {
  if (ctx.path === '/favicon.ico') return;
  // 获取
  let n = ctx.session.count || 0;

  // 设置
  ctx.session.count = ++n;
  ctx.body = `request ${n} times`;
});

app.listen(3000);

session-cookie 方案在 Koa 中实践

前端页面 index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
  <div id="app">
    <div>
      <input v-model="username">
      <input v-model="password">
    </div>
    <div>
      <button @click="login">Login</button>
      <button @click="logout">Logout</button>
      <button @click="getUser">Get User</button>
    </div>
    <div>
      <button onclick="document.getElementById('log').innerHTML = ''">Clear Log</button>
    </div>
    <h6 id="log"></h6>
  </div>
  <script>
    axios.defaults.widthCredentials = true;
    axios.interceptors.response.use(res => {
      document.getElementById('log').append(JSON.stringify(res.data));
      return res;
    });
    const app = new Vue({
      el: '#app',
      data: {
        username: 'cell',
        password: 'pwd',
      },
      methods: {
        async login() {
          await axios.post('/users/login', {
            username: this.username,
            password: this.password
          });
        },
        async logout() {
          await axios.post('/users/logout');
        },
        async getUser() {
          await axios.get('/users/getUser');
        }
      }
    });
  </script>
</body>
</html>

服务端 index.js

const Koa = require('koa');
const router = require('koa-router')();
const session = require('koa-session');
const cors = require('koa2-cors');
const bodyParser = require('koa-bodyparser');
const static = require('koa-static');

const app = new Koa();

app.use(cors({
  credentials: true,
}));
app.keys = ['my secret'];

app.use(static(__dirname + '/'));
app.use(bodyParser());
app.use(session(app));

app.use((ctx, next) => {
  if (ctx.url.indexOf('login') > -1) {
    next();
  } else {
    console.log('session', ctx.session.userinfo);
    if (!ctx.session.userinfo) {
      ctx.body = {
        message: 'Login Failed',
      };
    } else {
      next();
    }
  }
});

router.post('/users/login', async ctx => {
  const { body } = ctx.request;
  console.log('body', body);
  // 设置 session
  ctx.session.userinfo = body.username;
  ctx.body = {
    message: 'Login Successful'
  };
});

router.post('/users/logout', async ctx => {
  // 设置 session
  delete ctx.session.userinfo;
  ctx.body = {
    message: 'Logout Successful'
  };
});

router.get('/users/getUser', async ctx => {
  ctx.body = {
    message: 'Get Data Successful',
    userinfo: ctx.session.userinfo,
  };
});

app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);

过程描述:

  1. 用户登录时,服务端生成一个唯一的会话标识,并以该标识作为 key 存储相关数据
  2. 会话标识在客户端和服务端之间通过 cookie 进行传输
  3. 服务端通过会话标识可以获取到会话相关信息,然后对客户端的请求进行响应;如果找不到有效的会话标识,就判定用户是未登录状态
  4. 会话有过期时间,也可以通过一些操作(如退出登录)主动删除
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
存储 自然语言处理 API
Session、cookie、token有什么区别?
Session、cookie、token有什么区别?
24 1
|
2天前
|
存储 缓存 安全
【PHP开发专栏】PHP Cookie与Session管理
【4月更文挑战第30天】本文介绍了PHP中的Cookie和Session管理。Cookie是服务器发送至客户端的数据,用于维持会话状态,可使用`setcookie()`设置和`$_COOKIE`访问。Session数据存于服务器,更安全且能存储更多数据,通过`session_start()`启动,`$_SESSION`数组操作。根据需求选择Cookie(跨会话共享)或Session(单会话存储)。实战中常组合使用,如Cookie记住登录状态,Session处理购物车。理解两者原理和应用场景能提升Web开发技能。
|
5天前
|
存储 安全 前端开发
禁用Cookie后Session还能用吗?
禁用Cookie后Session还能用吗?
15 1
|
5天前
|
Java
Cookie和Session
Cookie和Session
13 0
|
17天前
|
存储 JSON 安全
|
19天前
|
存储 前端开发 数据安全/隐私保护
网站开发--Cookie 和 Session 的工作流程
网站开发--Cookie 和 Session 的工作流程
17 0
|
23天前
|
存储
cookie与Session
cookie与Session
|
29天前
|
存储 安全 Java
理解Session和Cookie:Java Web开发中的用户状态管理
【4月更文挑战第3天】本文探讨了Web应用中用户状态管理的两种主要机制——Session和Cookie。Session在服务器端存储数据,更安全,适合大量数据,而Cookie存储在客户端,可能影响性能但支持持久化。在Java Web开发中,使用Servlet API操作Session和Cookie,注意敏感信息安全、Session管理及Cookie安全设置。理解两者差异并恰当使用是优化应用性能和用户体验的关键。
|
2月前
|
数据采集 存储 安全
登录态数据抓取:Python爬虫携带Cookie与Session的应用技巧
登录态数据抓取:Python爬虫携带Cookie与Session的应用技巧
|
2月前
|
存储 安全 搜索推荐
Django中Cookie和Session的使用
Django中Cookie和Session的使用
21 0