Koa 鉴权实战 - Session/Cookie

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容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
相关文章
|
19天前
|
存储 安全 搜索推荐
理解Session和Cookie:Java Web开发中的用户状态管理
理解Session和Cookie:Java Web开发中的用户状态管理
40 4
|
22天前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
1月前
|
缓存 Java Spring
servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header
文章比较了在Servlet和Spring Boot中获取Cookie、Session和Header的方法,并提供了相应的代码实例,展示了两种方式在实际应用中的异同。
164 3
servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header
|
1月前
|
存储 安全 数据安全/隐私保护
Cookie 和 Session 的区别及使用 Session 进行身份验证的方法
【10月更文挑战第12天】总之,Cookie 和 Session 各有特点,在不同的场景中发挥着不同的作用。使用 Session 进行身份验证是常见的做法,通过合理的设计和管理,可以确保用户身份的安全和可靠验证。
21 1
|
2月前
|
存储 缓存 数据处理
php学习笔记-php会话控制,cookie,session的使用,cookie自动登录和session 图书上传信息添加和修改例子-day07
本文介绍了PHP会话控制及Web常用的预定义变量,包括`$_REQUEST`、`$_SERVER`、`$_COOKIE`和`$_SESSION`的用法和示例。涵盖了cookie的创建、使用、删除以及session的工作原理和使用,并通过图书上传的例子演示了session在实际应用中的使用。
php学习笔记-php会话控制,cookie,session的使用,cookie自动登录和session 图书上传信息添加和修改例子-day07
|
2月前
|
存储 前端开发 Java
JavaWeb基础7——会话技术Cookie&Session
会话技术、Cookie的发送和获取、存活时间、Session钝化与活化、销毁、用户登录注册“记住我”和“验证码”案例
JavaWeb基础7——会话技术Cookie&Session
|
2月前
|
存储 安全 NoSQL
Cookie、Session、Token 解析
Cookie、Session、Token 解析
62 0
|
3月前
|
存储 JavaScript 前端开发
Cookie 反制策略详解:Cookie加解密原理、Cookie和Session机制、Cookie hook、acw_sc__v2、jsl Cookie调试、重定向Cookie
Cookie 反制策略详解:Cookie加解密原理、Cookie和Session机制、Cookie hook、acw_sc__v2、jsl Cookie调试、重定向Cookie
210 1
|
3月前
|
存储 安全 搜索推荐
【JavaWeb 秘籍】Cookie vs Session:揭秘 Web 会话管理的奥秘与实战指南!
【8月更文挑战第24天】本文以问答形式深入探讨了Web开发中关键的会话管理技术——Cookie与Session。首先解释了两者的基本概念及工作原理,随后对比分析了它们在存储位置、安全性及容量上的差异。接着,通过示例代码详细介绍了如何在JavaWeb环境中实现Cookie与Session的操作,包括创建与读取过程。最后,针对不同应用场景提供了选择使用Cookie或Session的指导建议,并提出了保障二者安全性的措施。阅读本文可帮助开发者更好地理解并应用这两种技术。
61 1
|
3月前
|
C# 开发者 Windows
WPF遇上Office:一场关于Word与Excel自动化操作的技术盛宴,从环境搭建到代码实战,看WPF如何玩转文档处理的那些事儿
【8月更文挑战第31天】Windows Presentation Foundation (WPF) 是 .NET Framework 的重要组件,以其强大的图形界面和灵活的数据绑定功能著称。本文通过具体示例代码,介绍如何在 WPF 应用中实现 Word 和 Excel 文档的自动化操作,包括文档的读取、编辑和保存等。首先创建 WPF 项目并设计用户界面,然后在 `MainWindow.xaml.cs` 中编写逻辑代码,利用 `Microsoft.Office.Interop` 命名空间实现 Office 文档的自动化处理。文章还提供了注意事项,帮助开发者避免常见问题。
249 0