学习后端鉴权系列: 基于Cookie, Session认证

说起鉴权大家应该都很熟悉, 不过作为前端开发来讲, 鉴权的流程大头都在后端小哥那边, 但是作为一个有志气的开发者肯定要好好学习整个鉴权流程以及方案, 不然怎么跟后端合作:smile:。

常见的鉴权方案

  • 基于Cookie, Session认证

  • JWT认证, Token认证

  • OAuth2认证

  • SSO单点登陆

  • LDAP认证登陆

  • 扫码登陆

基于Cookie, Session认证

关于 Cookie 使用推荐阅读,HTTP cookies。

先上大家常见的一张 Cookie , Session 流程图。

例子展示

下面通过node + koa + redis来展示上述的流程。

开发前准备

  • 安装node

  • 安装 redis 并且本地启动

note: 下面代码只是供demo展示, 具体代码结构设计在生产环境可不能这么写, 后面我会总结一篇关于koa最佳实践文章

启动redis

然后通过终端查看你的redis有么有存储数据。

app.js

// app.js

const Koa = require("koa");
const Router = require("koa-router");
const bodyParser = require("koa-bodyparser");
const session = require("koa-session2");
const Store = require("./Store.js");

const app = new Koa();
const router = new Router();

app.keys = ["this is my secret key"];
app.use(bodyParser());

app.use(
  session({
    key: "jssessionId"
  })
);

// 模拟登陆
router.post("/login", async (ctx, next) => {
  const { username = "", password = "" } = ctx.request.body || {};
  // fake data
  const _username = "xyz";
  const _password = 123456;
  if (username === _username && password === _password) {
    const store = new Store();
    const sid = await store.set({
      username,
      password
    }, {
      maxAge: 1000 * 60 * 2 // 设定只有120s的有效时间
    });
    ctx.cookies.set('jssessionId', sid)
    ctx.body = {
      success: true,
      msg: "登陆成功"
    };
  } else {
    ctx.status = 401;
    ctx.body = {
      success: false,
      code: 10000,
      msg: "账号或者密码错误"
    };
  }
});

// 获取用户信息
router.get(
  "/user",
  async (ctx, next) => {
    const store = new Store();
    const jssessionId = ctx.cookies.get('jssessionId')
    const userSession = await store.get(jssessionId)
    console.log('获取到请求的cookie', jssessionId, 'session', userSession)
    if (!userSession) {
      ctx.status = 401;
      ctx.body = {
        success: false,
        msg: "oAuth Faill"
      };
    } else {
      ctx.userSession = userSession
      await next();
    }
  },
  async (ctx, next) => {
    ctx.body = {
      success: true,
      data: ctx.userSession
    };
  }
);

app.use(router.routes()).use(router.allowedMethods());
app.on("error", (err, ctx) => {
  console.error("server error", err, ctx);
});
app.listen(3000, () => {
  console.log("Server listening on port 3000");
});

Store.js

const Redis = require("ioredis");
const { Store } = require("koa-session2");

class RedisStore extends Store {
  constructor() {
    super();
    this.redis = new Redis(); // Connect to 127.0.0.1:6379
  }
  async get(sid, ctx) {
    try {
      const data = await this.redis.get(`jssessionId:${sid}`);
      return JSON.parse(data);
    } catch (err) {
      throw new Error(err);
    }
  }

  async set(session, { sid = this.getID(24), maxAge = 1000000 } = {}, ctx) {
    try {
      // EX: redis支持过了有效期自动删除
      await this.redis.set(
        `jssessionId:${sid}`,
        JSON.stringify(session),
        "EX",
        maxAge / 1000
      );
    } catch (err) {
      throw new Error(err);
    }
    return sid;
  }
}

module.exports = RedisStore;

然后通过postman我们来测试下

注意看返回的Set-Cookie, 接着我们看下redis

已经存在一条数据, 另外它的有效时间是120S,过了120S该数据会自动清除。

然后我们再通过另外接口去访问用户信息。

是可以获取到用户信息的,说明一切正常。

120S之后再次调用该接口测试是否已经失效。

redis里面也确实自动清除了该条数据。

备注

有错误的地方欢迎大家斧正, 源码地址。

最后有兴趣的关注一波公众号。