简介

  • HTTP是无状态协议。当浏览器中加载页面,然后转到同一网站的另一页面时,服务器和浏览器都没有任何内在的方法可以认识到,这是同一浏览器访问同一网站。换一种说法,Web工作的方式就是在每个HTTP请求中都要包含所有必要的信息,服务器才能满足这个请求。

  • 所以需要用某种办法在HTTP上建立状态,于是便有了cookie和会话。

关于cookie

  • cookie的想法很简单:服务器发送一点信息,浏览器在一段可配置的时期内保存它。发送哪些信息确实是由服务器来决定:通常只是一个唯一ID号,标识特定浏览器,从而维持一个有状态的假象。

  • cookie对用户来说不是加密的;

  • 服务器向客户端发送的所有cookie都能被客户端查看。

  • 用户对cookie有绝对的控制权,可以删除或禁用cookie

  • 一般的cookie可以被篡改

  • 不管浏览器什么时候发起一个跟cookie关联的请求,盲目地相信cookie中的内容,都有可能会受到攻击。要确保cookie不被篡改,使用签名cookie。

cookie可以用于攻击

  • 跨站脚本攻击 (XSS)攻击方式。XSS攻击中有一种技术就涉及用恶意的JavaScript修改cookie中的内容。所以不要轻易相信返回到服务器的cookie内容。

  • 用签名cookie会有帮助(不管是用户修改的还是恶意JavaScript修改的,这些篡改都会在签名cookie中留下明显的痕迹),并且还可以设定选项指明cookie只能由服务器修改。这些cookie的用途会受限,但它们肯定更安全。**

  • 如果可以选择,会话要优于cookie

  • 大多数情况下,可以用会话维持状态; 并且会话更容易,不用担心会滥用用户的存储,而且也更安全。

  • 当服务器希望客户端保存一个cookie时,它会发送一个响应头Set-Cookie,其中包含名称/值对。当客户端向服务器发送含有cookie的请求时,它会发送多个请求头Cookie,其中包含这些cookie的值。**

凭证的外化###

  • 为了保证cookie的安全,必须有一个cookie秘钥。cookie秘钥是一个字符串,服务器知道它是什么,它会在cookie发送到客户端之前对cookie加密。这是一个不需要记住的密码,所以可以是随机字符串。

  • 推荐用一个随机密码生成器来生成cookie秘钥。**

  • 外化第三方凭证是一种常见的做法,比如cookie秘钥、数据库密码和API令牌(Twitter、Facebook等)。这不仅易于维护(容易找到和更新凭证),还可以让版本控制系统忽略这些凭证文件。这对放在GitHub或其他开源源码控制库上的开源代码库尤其重要。**

  • 因此可以准备将凭证外化在一个JavaScript文件中(用JSON或XML也行,但我觉得JavaScript最容易)。

  • 创建文件credentials.js:

module.exports = {
cookieSecret: '把你的cookie秘钥放在这里',
};
  • 现在,为了防止不慎把这个文件添加到源码库中,在.gitignore文件中加上credentials.js。
var credentials = require('./credentials.js');

Express中的Cookie###

在程序中开始设置和访问cookie之前,需要先引入中间件cookie-parser。首先npm install --save cookie-parser,然后

app.use(require('cookie-parser')(credentials.cookieSecret));
  • 完成这个之后,就可以在任何能访问到响应对象的地方设置cookie或签名cookie:
res.cookie('monster', 'nom nom');
res.cookie('signed_monster', 'nom nom', { signed: true });
  • 签名cookie的优先级高于未签名cookie。如果将签名cookie命名为signed_monster,那就不能用这个名字再命名未签名cookie(它返回时会变成undefined)。

  • 要获取客户端发送过来的cookie的值(如果有的话),只需访问请求对象的cookie或signedCookie属性:

var monster = req.cookies.monster;
var signedMonster = req.signedCookies.monster;
  • 任何字符串都可以作为cookie的名称。

  • 要删除cookie,用res.clearCookie

res.clearCookie('monster');

设置cookie时可以使用如下这些选项:

  • domain

控制跟cookie关联的域名。这样可以将cookie分配给特定的子域名。注意,不能给cookie设置跟服务器所用域名不同的域名,因为那样它什么也不会做。

  • path

控制应用这个cookie的路径。注意,路径会隐含地通配其后的路径。如果用的路径是/ (默认值),它会应用到网站的所有页面上。如果用的路径是/foo,它会应用到/foo、/foo/bar等路径上。

  • maxAge

指定客户端应该保存cookie多长时间,单位是毫秒。如果你省略了这一选项,浏览器关闭时cookie就会被删掉。(也可以用expiration指定cookie过期的日期,但语法很麻烦。建议用maxAge。)

  • secure

指定该cookie只通过安全(HTTPS)连接发送。

  • httpOnly

将这个选项设为true表明这个cookie只能由服务器修改。也就是说客户端JavaScript不能修改它。这有助于防范XSS攻击。

  • signed

设为true会对这个cookie签名,这样就需要用res.signedCookies而不是res.cookies访问它。被篡改的签名cookie会被服务器拒绝,并且cookie值会重置为它的原始值。

会话###

  • 会话实际上只是更方便的状态维护方法。

  • 要实现会话,必须在客户端存些东西,否则服务器无法从一个请求到下一个请求中识别客户端。

  • 通常的做法是用一个包含唯一标识的cookie,然后服务器用这个标识获取相应的会话信息; cookie不是实现这个目的的唯一手段,比如在URL中添加会话信息等;但这些技术混乱、困难且效率低下,所以最好别用。

  • HTML5为会话提供了另一种选择,那就是本地存储,但现在还没有令人叹服的理由去采用这种技术而放弃经过验证有效的cookie。

  • 从广义上来说,有两种实现会话的方法:

    • 把所有东西都存在cookie里,被称为“基于cookie的会话”,并且仅仅表示比使用cookie便利;然而,它还意味着要把添加到cookie中的所有东西都存在客户端浏览器中,所以不推荐
    • 只在cookie里存一个唯一标识,其他东西都存在服务器上。

内存存储###

  • 把会话信息存在服务器上,那么必须找个地方存储它。入门级的选择是内存会话。

  • 它们非常容易设置,但也有个巨大的缺陷:重启服务器后会话信息就消失了。更糟的是,如果扩展了多台服务器,那么每次请求可能是由不同的服务器处理的,所以会话数据有时在那里,有时不在。这明显是不可接受的用户体验。

  • 然而出于开发和测试的需要,有它就足够了。

  • 首先安装express-session(npm install --save express-session)

  • 然后,在链入cookie-parser之后链入express-session:

app.use(require('cookie-parser')(credentials.cookieSecret));
app.use(require('express-session')());

中间件express-session接受带有如下选项的配置对象:

  • key

存放唯一会话标识的cookie名称。默认为connect.sid

  • store

会话存储的实例。默认为一个MemoryStore的实例,可以满足我们当前的要求。

  • cookie

会话cookie的cookie设置 (pathdomainsecure等)。适用于常规的cookie默认值。

使用会话###

  • 会话设置好以后,使用起来就再简单不过了,只是使用请求对象的session变量的属性:
req.session.userName = 'Anonymous';
var colorScheme = req.session.colorScheme || 'dark';
  • 注意,对于会话而言,我们不是用请求对象获取值,用响应对象设置值,它全都是在请求对象上操作的。(响应对象没有session属性。)要删除会话,可以用JavaScript的delete操作符:
req.session.userName = null;        // 这会将'userName'设为null;但不会移除它
delete req.session.colorScheme; // 这会移除'colorScheme'

用会话实现即显消息###

  • “即显”消息(不要跟Adobe Flash搞混了)只是在不破坏用户导航的前提下向用户提供反馈的一种办法。
  • 用会话实现即显消息是最简单的方式(也可以用查询字符串,但那样除了URL会更丑外,还会把即显消息放到书签里)
//使用了bootstrap
{{#if flash}}
<div class="alert alert-dismissible alert-{{flash.type}}">
<button type="button" class="close"
data-dismiss="alert" aria-hidden="true">&times;</button>
<strong>{{flash.intro}}</strong> {{{flash.message}}}
</div>
{{/if}}
  • 注意,在flash.message外面用了3个大括号,这样我们就可以在消息中使用简单的HTML(可能是要加重单词或包含超链接)。
  • 接下来添加一些中间件,如果会话中有flash对象,将它添加到上下文中。即显消息显示过一次之后,我们就要从会话中去掉它,以免它在下一次请求时再次显示。在路由之前添加下面这段代码:**
app.use(function(req, res, next){
// 如果有即显消息,把它传到上下文中,然后清除它
res.locals.flash = req.session.flash;
delete req.session.flash;
next();
});

接下来我们看一下如何使用即显消息。假设我们的用户订阅了简报,并且我们想在用户订阅后把他们重定向到简报归档页面去。我们的表单处理器可能是这样的:

app.post('/newsletter', function(req, res){
var name = req.body.name || '', email = req.body.email || '';
// 输入验证
if(!email.match(VALID_EMAIL_REGEX)) {
if(req.xhr) return res.json({ error: 'Invalid name email address.' });
req.session.flash = {
type: 'danger',
intro: 'Validation error!',
message: 'The email address you entered was not valid.',
};
return res.redirect(303, '/newsletter/archive');
}
new NewsletterSignup({ name: name, email: email }).save(function(err){
if(err) {
if(req.xhr) return res.json({ error: 'Database error.' });
req.session.flash = {
type: 'danger',
intro: 'Database error!',
message: 'There was a database error; please try again later.',
}
return res.redirect(303, '/newsletter/archive');
}
if(req.xhr) return res.json({ success: true });
req.session.flash = {
type: 'success',
intro: 'Thank you!',
message: 'You have now been signed up for the newsletter.',
};
return res.redirect(303, '/newsletter/archive');
});
});

看如何用同一个处理器处理AJAX提交(因为我们检查了req.xhr),并且区分开了输入验证错误和数据库错误。记住,即便在前端做了输入验证,在后台也应该再做一次。

即显消息是网站中一种很棒的机制,即便在某些特定区域其他方法更合适一些(比如,即显消息在多表单“向导”或购物车结账流程中就不太合适)。

因为在中间件里把即显消息从会话中传给了res.locals.flash,所以必须执行重定向以便显示即显消息。如果不想通过重定向显示即显消息,直接设定res.locals.flash,而不是req.session.flash

会话的用途###

  • 当想跨页保存用户的偏好时,可以用会话。会话最常见的用法是提供用户验证信息,在登录后就会创建一个会话。之后就不用在每次重新加载页面时再登录一次。
  • 即便没有用户账号,会话也有用。网站一般都要记住你喜欢如何排列东西,或者你喜欢哪种日期格式,这些都不需要登录。
  • 尽管我建议优先选择会话而不是cookie,但理解cookie的工作机制也很重要(特别是因为有cookie才能用会话)。它对于你在应用中诊断问题、理解安全性及隐私问题都有帮助。 

express-12 Cookie与会话的更多相关文章

  1. 如何利用服务器下发的Cookie实现基于此Cookie的会话保持

    Cookie是一种在客户端保持HTTP状态信息的常用技术,基于Cookie的会话保持常常出现在很多AX的部署案例中,尤其是涉及电子交易的系统部署中.此类系统往往要求负载均衡设备按照服务器下发的Cook ...

  2. SpringMVC:学习笔记(12)——ThreadLocal实现会话共享

    SpringMVC:学习笔记(12)——ThreadLocal实现会话共享 ThreadLocal ThreadLocal,被称为线程局部变量.在并发编程的情况下,使用ThreadLocal创建的变量 ...

  3. Java Web之会话管理一: 使用Cookie进行会话管理

    一.Cookie的概念 Cookie(会话)可以简单的理解为:用户开一个浏览器,点击多个链接,访问服务器多个web资源,然后关闭浏览器,整个过程称为一个会话. 二.会话过程中解决的问题 用户在使用浏览 ...

  4. 浏览器禁用Cookie,基于Cookie的会话跟踪机制失效的解决的方法

    当浏览器禁用Cookies时.基于Cookie的会话跟踪机制就会失效.解决的方法是利用URL重写机制跟踪用户会话. 在使用URL重写机制的时候须要注意.为了保证会话跟踪的正确性,全部的链接和重定向语句 ...

  5. nginx第三方模块---nginx-sticky-module的使用(基于cookie的会话保持)

    目前的项目网站架构中使用了F5和nginx,F5用来做负载均衡,nginx只用作反向代理服务器.最近应客户的要求准备去掉F5,使用软负载.大家都知道nginx抗并发能力强,又可以做负载均衡,而且使用n ...

  6. 基于hi-nginx的web开发(python篇)——cookie和会话管理

    hi-nginx通过redis管理会话. 要开启管理,需要做三件事. 第一件开启userid: userid on; userid_name SESSIONID; userid_domain loca ...

  7. Cookie&Seesion会话 共享数据 工作流程 持久化 Servlet三个作用域 会话机制

    Day37 Cookie&Seesion会话 1.1.1 什么是cookie 当用户通过浏览器访问Web服务器时,服务器会给客户端发送一些信息,这些信息都保存在Cookie中.这样,当该浏览器 ...

  8. 使用Cookie进行会话管理

    javaweb学习总结(十一)——使用Cookie进行会话管理 一.会话的概念 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话. ...

  9. express中cookie的使用和cookie-parser的解读

    https://segmentfault.com/a/1190000004139342?_ea=504710 最近在研究express,学着使用cookie,开始不会用,就百度了一下,没有百度到特别完 ...

随机推荐

  1. vector在C++中的基本用法

    在写BlackJackGame的时候,考虑到要用到容器,所以就对容器的相关知识强化了一下: 因为我想的是有card类,最后要实现发牌,洗牌等等一系列的操作的时候,使用指向card类的对象的指针,将ca ...

  2. 【leetcode】Maximal Rectangle (hard)★

    Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and ...

  3. java导出excel(解决导出几万条数据内存溢出的问题)

    import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.File; import ja ...

  4. 数据库的日志数据库(_log.ldf)文件太大,如何压缩

    DUMP TRANSACTION TCB WITH NO_LOGBACKUP LOG TCB WITH NO_LOGDBCC SHRINKDATABASE(TCB) 执行这三条语句就可以了,这里的TC ...

  5. September 19th 2016 Week 39th Monday

    We come nearest to the great when we are great in humility. 我们最为谦逊的时候越接近伟大. When you are powerful en ...

  6. PHP之MVC学习

    代码架构进货过程 one,混编 嵌入式脚本语言PHP html与php混编的编码方式 two,显示和逻辑相分离 最后,需要将显示和逻辑的结果放在一起! 需要在 php页面,将html代码 载入才可以! ...

  7. EventBus学习入门

    EventBus Features What makes greenrobot's EventBus unique, are its features: Simple yet powerful: Ev ...

  8. 虚拟机Class文件结构笔记

    >>Java语言的平台无关性 Java是与平台无关的语言,“一次编写,到处运行”,这一方面依赖于Java源代码编译后生成的存储字节码的文件,即Class文件是语言和平台无关的: 另一方面依 ...

  9. Delphi开发中各种文件扩展名代表什么文件

    暂时就遇到了以下这几种,以后遇到再进行补充 .DPR Delphi Project文件,打开这个文件,就会打开所有的编程的代码文件.包含了Pascal代码 .PAS Pascal文件,Pascal单元 ...

  10. 【stut 逆置正整数】

    C语言实验——逆置正整数 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 输入一个三位正整数,将它反向输出. 输入 3位正整数. ...