Cookie 使用总结

1 常用属性及描述

  • name,Cookie 的名称,Cookie 一旦创建,名称便不可更改
  • value,Cookie 的值。如果值为 Unicode 字符,需要为字符编码。如果值为二进制数据,则需要使用 BASE64 编码
  • maxAge,Cookie 失效的时间,单位秒。如果为正数,则该 Cookie 在 maxAge 秒之后失效。如果为负数,该 Cookie 为临时 Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该 Cookie。如果为 0,表示删除该 Cookie。默认为 -1。
  • secure,该 Cookie 是否仅被使用安全协议传输。安全协议。安全协议有 HTTPS,SSL 等,在网络上传输数据之前先将数据加密。默认为 false。
  • path,Cookie 的使用路径。如果设置为 /sessionWeb/,则只有 contextPath 为 /sessionWeb 的程序可以访问该 Cookie。如果设置为 /,则本域名下 contextPath 都可以访问该 Cookie。

    注意:最后一个字符必须为 /

  • domain,可以访问该 Cookie 的域名。如果设置为 .google.com,则所有以 google.com 结尾的域名都可以访问该 Cookie

    注意:第一个字符必须为 .

  • comment,该 Cookie 的用处说明,浏览器显示 Cookie 信息的时候显示该说明。
  • version,Cookie 使用的版本号,0 表示遵循 Netscape 的 Cookie 规范,1 表示遵循 W3C 的 RFC 2109 规范

Cookie 的 maxAge 决定着 Cookie 的有效期,单位为秒(Second)。Cookie 中通过 getMaxAge() 方法与 setMaxAge(int maxAge) 方法来读写 maxAge 属性。

如果 maxAge 属性为正数,则表示该 Cookie 会在 maxAge 秒之后自动失效。浏览器会将 maxAge 为正数的 Cookie 持久化,即写到对应的 Cookie 文件中。无论客户关闭了浏览器还是电脑,只要还在 maxAge 秒之前,登录网站时该 Cookie 仍然有效。下面代码中的 Cookie 信息将永远有效。

如果 maxAge 为负数,则表示该 Cookie 仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该 Cookie 即失效。maxAge 为负数的 Cookie,为临时性 Cookie,不会被持久化,不会被写到 Cookie 文件中。Cookie 信息保存在浏览器内存中,因此关闭浏览器该 Cookie 就消失了。Cookie 默认的 maxAge 值为-1。

如果 maxAge 为 0,则表示删除该 Cookie。Cookie 机制没有提供删除 Cookie 的方法,因此通过设置该 Cookie 即时失效实现删除 Cookie 的效果。失效的 Cookie 会被浏览器从 Cookie 文件或者内存中删除,

response 对象提供的 Cookie 操作方法只有一个添加操作 add(Cookie cookie)

要想修改 Cookie 只能使用一个同名的 Cookie 来覆盖原来的 Cookie,达到修改的目的。删除时只需要把 maxAge 修改为 0 即可。

从客户端读取 Cookie 时,包括 maxAge 在内的其他属性都是不可读的,也不会被提交。浏览器提交 Cookie 时只会提交 name 与 value 属性。maxAge 属性只被浏览器用来判断 Cookie 是否过期。

Cookie 并不提供修改、删除操作。如果要修改某个 Cookie,只需要新建一个同名的 Cookie,添加到 response 中覆盖原来的 Cookie。

如果要删除某个 Cookie,只需要新建一个同名的 Cookie,并将 maxAge 设置为 0,并添加到 response 中覆盖原来的 Cookie。

注意:修改、删除 Cookie 时,新建的 Cookie 除 value、maxAge 之外的所有属性,例如 name、path、domain 等,都要与原 Cookie 完全一样。否则,浏览器将视为两个不同的 Cookie 不予覆盖,导致修改、删除失败。

Cookie 是不可跨域名的。域名 www.google.com 颁发的 Cookie 不会被提交到域名 www.baidu.com。这是由 Cookie 的隐私安全机制决定的。隐私安全机制能够禁止网站非法获取其他网站的 Cookie。

正常情况下,同一个一级域名下的两个二级域名如 www.baidu.comp_w_picpaths.baidu.com 也不能交互使用 Cookie,因为二者的域名并不严格相同。如果想所有 baidu.com 名下的二级域名都可以使用该 Cookie,需要设置 Cookie 的 domain 参数为 .baidu.com

可以修改本机 C:\WINDOWS\system32\drivers\etc 下的 hosts 文件来配置多个临时域名来验证 domain 属性。

注意:domain 参数必须以点(.)开始。另外,name 相同但 domain 不同的两个 Cookie 是两个不同的 Cookie。如果想要两个域名完全不同的网站共有 Cookie,可以生成两个 Cookie,domain 属性分别为两个域名,输出到客户端。

domain 属性决定运行访问 Cookie 的域名,而 path 属性决定允许访问 Cookie 的路径(ContextPath)。例如,如果只允许 /sessionWeb/ 下的程序使用 Cookie,可以这么写 cookie.setPath("/session/");

设置为 / 时允许所有路径使用 Cookie。path 属性需要使用符号 / 结尾。name 相同但 domain 相同的两个 Cookie 也是两个不同的 Cookie。

页面只能获取它属于的 Path 的 Cookie。例如 /session/test/a.jsp 不能获取到路径为 /session/abc/ 的 Cookie。

HTTP 协议不仅是无状态的,而且是不安全的。使用 HTTP 协议的数据不经过任何加密就直接在网络上传播,有被截获的可能。使用 HTTP 协议传输很机密的内容是一种隐患。如果不希望 Cookie 在 HTTP 等非安全协议中传输,可以设置 Cookie 的 secure 属性为 true。浏览器只会在 HTTPS 和 SSL 等安全协议中传输此类 Cookie。设置 secure 属性为 true 的代码是 cookie.setSecure(true);

secure 属性并不能对 Cookie 内容加密,因而不能保证绝对的安全性。如果需要高安全性,需要在程序中对 Cookie 内容加密、解密,以防泄密。

Cookie 是保存在浏览器端的,因此浏览器具有操作 Cookie 的先决条件。浏览器可以使用脚本程序如 JavaScript 或者 VBScript 等操作 Cookie。这里以 JavaScript 为例介绍常用的 Cookie 操作。例如下面的代码会输出本页面所有的 Cookie。

1
2
3
<script>
document.write(document.cookie);
</script>

由于 JavaScript 能够任意地读写 Cookie,给网站带来安全隐患,W3C 标准的浏览器会阻止 JavaScript 读写任何不属于自己网站的 Cookie。换句话说,A 网站的 JavaScript 程序读写 B 网站的 Cookie 不会有任何结果。

部分浏览器支持设置 HttpOnly 属性,如果在 cookie 中设置了 HttpOnly 属性,那么通过 js 脚本将无法读取到 cookie 信息,这样能有效的防止 XSS

8 在 Java 服务端使用注意

不能直接使用逗号这种特殊符号(对 cookie 0 版本标准而言,新版本 cookie 1 没问题)作为 cookie 的内容。而新版本的 Cookie(参见 RFC 2109)目前还不被 Javax.servlet.http.Cookie 包所支持。Cookie Version 0 中,某些特殊的字符,例如:空格,方括号,圆括号,等于号,逗号,双引号,斜杠,问号,@符号,冒号,分号都不能作为 Cookie 的内容。

9 在前端使用注意

前端请求如果携带 cookie 信息,那么后端 Access-Control-Allow-Origin 不能为 *,否则该请求会失败,在 Options 预请求时会被拦截下来。可参见 MDN 文档 :https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS, 在 springboot 中,可以增加 .allowCredentials(true) 设置,如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("*")
.maxAge(1800)
.allowedOrigins("*")
// 允许携带cookie
.allowCredentials(true);
}
}

fetch 发送请求默认是不发送 cookie 的,不管是同域还是跨域;对于那些需要权限验证的请求就可能无法正常获取数据,这时需要设置 fetch 的第二个参数,即 credentials:

1
fetch(url, { credentials: "include" });

credentials 有 3 个可选值:

  • omit: 忽略 cookie 的发送
  • same-origin: 表示 cookie 只能同域发送,不能跨域发送
  • include: cookie 既可以同域发送,也可以跨域发送

fetch 默认对服务端通过 Set-Cookie 头设置的 cookie 也会忽略,若想选择接受来自服务端的 cookie 信息,也必须要配置 credentials 选项。