session

什么是session

http是无状态的

session - 图1

但是服务器可以通过session来保存状态

为什么需要session

一般是在web应用的背景之下,我们知道web应用是基于HTTP协议的,而HTTP协议恰恰是一种无状态协议。也就是说,用户从A页面跳转到B页面会重新发送一次HTTP请求,而服务端在返回响应的时候是无法获知该用户在请求B页面之前做了什么的。

对于HTTP的无状态性的原因,相关RFC里并没有解释,但联系到HTTP的历史以及应用场景,我们可以推测出一些理由:

  1. 设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。那个时候没有动态页面技术,只有纯粹的静态HTML页面,因此根本不需要协议能保持状态;
  2. 用户在收到响应时,往往要花一些时间来阅读页面,因此如果保持客户端和服务端之间的连接,那么这个连接在大多数的时间里都将是空闲的,这是一种资源的无端浪费。所以HTTP原始的设计是默认短连接,即客户端和服务端完成一次请求和响应之后就断开TCP连接,服务器因此无法预知客户端的下一个动作,它甚至都不知道这个用户会不会再次访问,因此让HTTP协议来维护用户的访问状态也全然没有必要;
  3. 将一部分复杂性转嫁到以HTTP协议为基础的技术之上可以使得HTTP在协议这个层面上显得相对简单,而这种简单也赋予了HTTP更强的扩展能力。事实上,session技术从本质上来讲也是对HTTP协议的一种扩展。

总而言之,HTTP的无状态是由其历史使命而决定的。但随着网络技术的蓬勃发展,人们再也不满足于死板乏味的静态HTML,他们希望web应用能动起来,于是客户端出现了脚本和DOM技术,HTML里增加了表单,而服务端出现了CGI等等动态技术。

而正是这种web动态化的需求,给HTTP协议提出了一个难题:一个无状态的协议怎样才能关联两次连续的请求呢?也就是说无状态的协议怎样才能满足有状态的需求呢?

此时有状态是必然趋势而协议的无状态性也是木已成舟,因此我们需要一些方案来解决这个矛盾,来保持HTTP连接状态,于是出现了cookie和session。

功能

  • 注册
  • 登录
  • 首页
    • 如果已登录
    • 如果未登录

koa实现

https://github.com/koajs/generic-session

  • 定义sessionStore
  • 各种数据库的实现

需要区分

准备

  1. npm i -S mongoose bluebird
  2. npm i -S koa-generic-session koa-generic-session-mongo

提示,当发现bcrypt无法安装的时候

  1. > bcrypt@0.8.7 install ~/workspace/github/mongoose-base-user-plugin/node_modules/bcrypt
  2. > node-gyp rebuild
  3. gyp: ~/.node-gyp/4.4.5/common.gypi not found (cwd: ~/workspace/github/mongoose-base-user-plugin/node_modules/bcrypt) while reading includes of binding.gyp while trying to load binding.gyp
  4. gyp ERR! configure error
  5. gyp ERR! stack Error: `gyp` failed with exit code: 1
  6. gyp ERR! stack at ChildProcess.onCpExit (~/.nvm/versions/node/v4.4.5/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:343:16)
  7. gyp ERR! stack at emitTwo (events.js:87:13)
  8. gyp ERR! stack at ChildProcess.emit (events.js:172:7)
  9. gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:200:12)
  10. gyp ERR! System Darwin 14.5.0
  11. gyp ERR! command "~/.nvm/versions/node/v4.4.5/bin/node" "~/.nvm/versions/node/v4.4.5/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
  12. gyp ERR! cwd ~/workspace/github/mongoose-base-user-plugin/node_modules/bcrypt
  13. gyp ERR! node -v v4.4.5
  14. gyp ERR! node-gyp -v v1.0.3
  15. gyp ERR! not ok
  16. npm ERR! Darwin 14.5.0
  17. npm ERR! argv "~/.nvm/versions/node/v4.4.5/bin/node" "~/.nvm/versions/node/v4.4.5/bin/npm" "i"
  18. npm ERR! node v4.4.5
  19. npm ERR! npm v2.10.1
  20. npm ERR! code ELIFECYCLE
  21. npm ERR! bcrypt@0.8.7 install: `node-gyp rebuild`
  22. npm ERR! Exit status 1
  23. npm ERR!
  24. npm ERR! Failed at the bcrypt@0.8.7 install script 'node-gyp rebuild'.
  25. npm ERR! This is most likely a problem with the bcrypt package,
  26. npm ERR! not with npm itself.
  27. npm ERR! Tell the author that this fails on your system:
  28. npm ERR! node-gyp rebuild
  29. npm ERR! You can get their info via:
  30. npm ERR! npm owner ls bcrypt
  31. npm ERR! There is likely additional logging output above.

解决方案

  1. rm -rf ~/.node_gyp

核心代码

引用

  1. const session = require('koa-generic-session')
  2. const MongoStore = require('koa-generic-session-mongo')

核心代码

  1. app.keys = ['keys', 'keykeys'];
  2. app.use(session({
  3. store: new MongoStore({
  4. url: require('./mongodb').url(),
  5. ttl: 6000
  6. })
  7. }));

这样是全局的使用session,在后面的中间件里可以使用ctx.session,如果想定制高效,可以在具体路由里使用该session中间件,而非全局。

用法

比如注册

  1. // 注册信息保存
  2. router.post('/register', ctx => {
  3. console.log(ctx.request.body)
  4. var session = ctx.session
  5. let body = ctx.request.body
  6. let user = new User({
  7. username: body.name,
  8. password: body.password
  9. })
  10. return user.save(function(err, doc){
  11. if (err) {
  12. return ctx.redirect('/404')
  13. }
  14. session.current_user = {
  15. username: body.name,
  16. password: body.password
  17. }
  18. return ctx.redirect('/')
  19. })
  20. })
  • ctx.session是之前加了session中间件的原因才有的
  • ctx.session是一个对象,只要你赋值了,它就会存在
  • 这里给session增加了一个current_user对象
  • 最后重定向到根路径

然后在其他中间件里即可,获取通过session来共享current_user对象,达到了状态保存的目的。

但session是有时效的,根据ttl设置自动销毁,比如tomcat的默认时间是30分钟,这个可以酌情设置。当session失效,就变成游客模式。

既然session是一个对象,可以挂无数东西,那么是不是什么都可以挂呢?session如果使用cookie存,是内存模式,如果东西太多,会内存爆掉,如果使用db存储(redis或mongodb),就没有这样的问题,但仍然会占用较多,所以。。。除非必要,尽量少在session上放东西。

以前的购物车是放到session里,但真正的电商购物车早已经不用session里,而是持久化。所以目前来看除了和用户登录相关的,很少会用到session。