Servlet-Cookie源码分析 源码环境:Tomcat8
最近在学习servlet的一些实现细节,阅读了Cookie的源码。
Cookie本质上是服务器发送给客户端(主要是浏览器)的一个会话临时数据。
其源码注释文档的说明:
Creates a cookie, a small amount of information sent by a servlet to a Web browser, saved by the browser, and later sent back to the server. A cookie's value can uniquely identify a client, so cookies are commonly used for session management.
就是说这个东西是个轻量级的信息载体,由服务器创建发送给Web浏览器,之后每一次浏览器发送Http请求访问服务器的时候再传回给服务器,让服务器知道“就是这个用户”。一个Cookie的“值”必须不同来唯一确定一个客户端,所以Cookie经常配合服务器端的Session使用。
在Tomcat8源码中的Cookie源码有接近500行,其中很多东西都是比较占空间的,比如一系列set,get方法,现在拿出其中一部分进行分析。
Cookie的声明:
public class Cookie implements Cloneable, Serializable {
//代码省略
}
说明,这个类的对象可以被复制和持久化。
Cookie的静态部分:
private static final CookieNameValidator validation;
static {
boolean strictNaming;
String prop = System.getProperty("org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING");
if (prop != null) {
strictNaming = Boolean.parseBoolean(prop);
} else {
strictNaming = Boolean.getBoolean("org.apache.catalina.STRICT_SERVLET_COMPLIANCE");
} if (strictNaming) {
validation = new RFC2109Validator();
}
else {
validation = new NetscapeValidator();
}
} private static final long serialVersionUID = 1L;
这里面有一个:CookieNameValidator对象。这个东西是拿来干嘛的呢?实际上Validator是判断一个值是否有效的检测器。也就是说这个对象用于判断Cookie的名字是否有效。那么什么名字是有效的Cookie名字呢?源码文档里面有说明:
The name must conform to RFC 2109. That means it can contain only ASCII alphanumeric characters and cannot contain commas, semicolons, or white space or begin with a $ character. The cookie's name cannot be changed after creation.
就不翻译了。总之,满足这些规则才能被作为Cookie的名字。
而静态代码块里面的strictNaming则表示了一种配置:到底是用RFC2109Validator还是NetscapeValidator进行真正的是否合法的判断。那么这几个类到底有什么区别?实际上是对合法名字判断的严格程度区分的。
源码和我的注释:
class CookieNameValidator { //有效判断器的基类,基本不用这个
private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
protected static final ResourceBundle lStrings = ResourceBundle.getBundle(LSTRING_FILE); protected final BitSet allowed; //这个东西来实现对可取字符区间的控制,其作用原理比较简单:在一个范围内用比特位表示是否有效。 protected CookieNameValidator(String separators) { //构造函数输入一个字符集,这个字符集里面的字符都被作为非法字符对待
allowed = new BitSet(128);
allowed.set(0x20, 0x7f);
for (int i = 0; i < separators.length(); i++) {
char ch = separators.charAt(i);
allowed.clear(ch);
}
} void validate(String name) { //判断一个那么是否合法
if (name == null || name.length() == 0) { //先是判断是否为null或者为空字符串
throw new IllegalArgumentException(lStrings.getString("err.cookie_name_blank"));
}
if (!isToken(name)) { //调用isToken进行判断。isToken负责判断这个name里面是否包含非法字符,如果有则抛出异常
String errMsg = lStrings.getString("err.cookie_name_is_token");
throw new IllegalArgumentException(MessageFormat.format(errMsg, name));
}
} private boolean isToken(String possibleToken) { //这个名字是否合法的判断函数
int len = possibleToken.length(); for (int i = 0; i < len; i++) {
char c = possibleToken.charAt(i);
if (!allowed.get(c)) { //把这个字符串的每一个字符拿出来和有效字符集合(上文中的BitSet)进行判断,如果发现一个无效字符则怎个字符串无效
return false;
}
}
return true;
}
} class NetscapeValidator extends CookieNameValidator {
// the Netscape specification describes NAME=VALUE as
// "a sequence of characters excluding semi-colon, comma and white space"
// we also exclude the '=' character that separates NAME from VALUE
private static final String NETSCAPE_SEPARATORS = ",; " + "="; //等于把这些字符也当做非法字符 NetscapeValidator() {
super(NETSCAPE_SEPARATORS); //通过父类的构造方法传入更多的非法字符,让判断更严格。
}
} class RFC6265Validator extends CookieNameValidator {
private static final String RFC2616_SEPARATORS = "()<>@,;:\\\"/[]?={} \t"; //更加多的非法字符,更加严格的名字检查 RFC6265Validator() {
super(RFC2616_SEPARATORS); // special treatment to allow for FWD_SLASH_IS_SEPARATOR property
boolean allowSlash;
String prop = System.getProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");
if (prop != null) {
allowSlash = !Boolean.parseBoolean(prop);
} else {
allowSlash = !Boolean.getBoolean("org.apache.catalina.STRICT_SERVLET_COMPLIANCE");
}
if (allowSlash) {
allowed.set('/');
}
}
} class RFC2109Validator extends RFC6265Validator { //更加严格,避免以$开头的String作为名字
RFC2109Validator() {
} @Override
void validate(String name) {
super.validate(name);
if (name.charAt(0) == '$') {
String errMsg = lStrings.getString("err.cookie_name_is_token");
throw new IllegalArgumentException(MessageFormat.format(errMsg, name));
}
}
}
以上源码的注释基本说明白了这个控制Cookie名字是否有效的机制。这个机制保证每一个Cookie的名字的合法性(当然具体要按照什么标准来还要再定)。
接着看Cookie的源码:
private final String name;
private String value; private int version = 0; // ;Version=1 ... means RFC 2109 style //
// Attributes encoded in the header's cookie fields.
//
private String comment; // ;Comment=VALUE ... describes cookie's use
private String domain; // ;Domain=VALUE ... domain that sees cookie
private int maxAge = -1; // ;Max-Age=VALUE ... cookies auto-expire
private String path; // ;Path=VALUE ... URLs that see the cookie
private boolean secure; // ;Secure ... e.g. use SSL
private boolean httpOnly; // Not in cookie specs, but supported by browsers
name和Value为最重要的两个量,一个是Cookie的名字,一个是它的值,名字可以重复,值必须唯一。这里有个小细节,注释上明确说了名字在创建之后不可以再更改,那么这个final起到什么效果呢?就是起到阻止更改的效果。String对象的每一次修改,都是new 一个String,其值为更改之后的值,再返回新的对象,让原来的引用指向这个新的对象,而不是真的修改了这个对象的内容。把它修饰为final之后,这个引用就不可以再变化,也就不能修改其值了。
下方的实例变量为Cookie的“属性”。Cookie的注释也明确提到,很多浏览器的支持不是很完善,使用要谨慎。
path:指明Cookie对应的页面(通常是一个目录),这个目录的子页面都可以访问这个Cookie,其他则不能访问,如果要设置为全局可以访问的,则设置为/。
domain:指定关联的WEB服务器域名。
secure:指定是否安全传输数据,或者为空或者为secure,如果是空用不安全的http,如果是secure用https或者别的加密方式。这个只是加密传输的内容而不是本地保存的Cookie。
maxAge:最大生命周期。整数,单位为秒,设置这个Cookie的过期时间。如果为负数则关闭浏览器失效,本地不保存。
comment:浏览器显示这个Cookie的时候显示出来的对这个Cookie的意义的说明。
version:Cookie使用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范
httpOnly:cookie的httponly属性。若此属性为true,则只有在http请求头中会带有此cookie的信息,而不能通过document.cookie来访问此cookie。
构造方法:
public Cookie(String name, String value) {
validation.validate(name);
this.name = name;
this.value = value;
}
比较直白:检查名字的合法性,然后注入。
后面跟了一大堆get和set方法,都是关于Cookie的那些属性的。
Servlet-Cookie源码分析 源码环境:Tomcat8的更多相关文章
- Flink源码分析 - 源码构建
原文地址:https://mp.weixin.qq.com/s?__biz=MzU2Njg5Nzk0NQ==&mid=2247483692&idx=1&sn=18cddc1ee ...
- Elasticsearch源码分析 - 源码构建
原文地址:https://mp.weixin.qq.com/s?__biz=MzU2Njg5Nzk0NQ==&mid=2247483694&idx=1&sn=bd03afe5a ...
- zxing源码分析——QR码部分
Android应用横竖屏切换 zxing源码分析——DataMatrix码部分 zxing源码分析——QR码部分 2013-07-10 17:16:03| 分类: 默认分类 | 标签: |字号大中 ...
- ffplay源码分析3-代码框架
ffplay是FFmpeg工程自带的简单播放器,使用FFmpeg提供的解码器和SDL库进行视频播放.本文基于FFmpeg工程4.1版本进行分析,其中ffplay源码清单如下: https://gith ...
- 鸿蒙内核源码分析(源码结构篇) | 内核每个文件的含义 | 百篇博客分析OpenHarmony源码 | v18.04
百篇博客系列篇.本篇为: v18.xx 鸿蒙内核源码分析(源码结构篇) | 内核每个文件的含义 | 51.c.h .o 前因后果相关篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 ...
- 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 百篇博客分析OpenHarmony源码 | v13.02
百篇博客系列篇.本篇为: v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 51.c.h .o 几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 ...
- nginx源码分析-源码结构
本文主要简单介绍nginx源码目录结构.程序编译流程.如何构建学习nginx的环境等.本文以及后续nginx源码分析文章是基于nginx当前(2009-02-27)的稳定版本0.6.35进行的分析,该 ...
- Dubbo 源码分析系列之一环境搭建
环境搭建的步骤有哪些 依赖外部的环境 使用的开发工具 源码的拉取 结构大致介绍 1 依赖的外部环境 安装JDK 安装Git 安装maven 这边我们就不介绍怎么安装这些外部环境了,大家自行从安装这些外 ...
- Spring源码分析——源码分析环境搭建
1.在Windows上安装Gradle gradle工具类似于maven,用于项目的构建,此处主要用于构建spring源码,以便我们将spring源码导入eclipse. 开发环境 Java:JDK8 ...
随机推荐
- Boost条件变量condition_variable_any
Boost条件变量可以用来实现线程同步,它必须与互斥量配合使用.使用条件变量实现生产者消费者的简单例子如下,需要注意的是cond_put.wait(lock)是在等待条件满足.如果条件不满足,则释放锁 ...
- C标准头文件<math.h>
定义域错误可以理解为超出了函数的适用范围,如果发生了定义域错误,设errno为EDOM 如果结果不能表示为double值,则发生值域错误,如果结果上溢,则函数返回HUGE_VAL的值,设errno为E ...
- 时光倒流程序设计-AlloyTicker
熵与负熵 熵遵循熵增原理,即无序非热能与热能之间的转换具有方向性.薛定谔说过:生命本质在于负熵.熵代表的是无序,负熵就是熵的对立,而负熵表示的则是有序.汲取负熵(米饭.面包.牛奶.鸡蛋),可以简单的理 ...
- 蓝牙协议中的SBC编码
一.从信息的传输说起  上图是一个典型的蓝牙耳机应用场景.手机上的音频信息经过编码以后通过蓝牙协议被蓝牙耳机接收,经过解码以后,蓝牙耳机成功获取手机上的音频信息,然后再转化为振动被人耳识别.这是一个 ...
- 【转】深入浅出JavaScript之闭包(Closure)
闭包(closure)是掌握Javascript从人门到深入一个非常重要的门槛,它是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.下面写下我的学习笔记~ 闭包-无处不 ...
- AEAI HR_v1.5.2升级说明,开源人力资源管理系统
1.升级说明 本次AEAI HR升级内容主要是针对数通畅联推出AEAI ECP企业云联平台而升级的,其中对AEAI HR的各模块进行扩展,同时增加了移动门户版功能及为AEAI ECP提供数据服务接口. ...
- 突然想到一个问题:有关 cqrs 分离
大部分的系统,都是查询多,c少,那为什么我们不把q放在内存里面,而c直接操作数据库呢? 就如enode,c在内存,而q在数据库,当然q也很容易扩展到内存缓存上.二个enode案例demo,都可以让c的 ...
- Hibernate 系列 04 - Hibernate 配置相关的类
引导目录: Hibernate 系列教程 目录 前言: 通过上一篇的增删改查小练习之后,咱们大概已经掌握了Hibernate的基本用法. 我们发现,在调用Hibernate API的过程中,虽然Hib ...
- T-SQL 去除特定字段的前导0
在工作过程中遇到一个需求,要从特定字段中删除前导零,这是一个简单的VARCHAR(10)字段. 例如,如果字段包含"00001A",则SELECT语句需要将数据返回为"1 ...
- MySQL Performance-Schema(二) 理论篇
MySQL Performance-Schema中总共包含52个表,主要分为几类:Setup表,Instance表,Wait Event表,Stage Event表Statement Event表,C ...