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的声明:

1 public class Cookie implements Cloneable, Serializable {
2     //代码省略
3 }

说明,这个类的对象可以被复制和持久化。

Cookie的静态部分:

 1     private static final CookieNameValidator validation;
 2     static {
 3         boolean strictNaming;
 4         String prop = System.getProperty("org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING");
 5         if (prop != null) {
 6             strictNaming = Boolean.parseBoolean(prop);
 7         } else {
 8             strictNaming = Boolean.getBoolean("org.apache.catalina.STRICT_SERVLET_COMPLIANCE");
 9         }
10
11         if (strictNaming) {
12             validation = new RFC2109Validator();
13         }
14         else {
15             validation = new NetscapeValidator();
16         }
17     }
18
19     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进行真正的是否合法的判断。那么这几个类到底有什么区别?实际上是对合法名字判断的严格程度区分的。

源码和我的注释:

 1 class CookieNameValidator { //有效判断器的基类,基本不用这个
 2     private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
 3     protected static final ResourceBundle lStrings = ResourceBundle.getBundle(LSTRING_FILE);
 4
 5     protected final BitSet allowed;  //这个东西来实现对可取字符区间的控制,其作用原理比较简单:在一个范围内用比特位表示是否有效。
 6
 7     protected CookieNameValidator(String separators) { //构造函数输入一个字符集,这个字符集里面的字符都被作为非法字符对待
 8         allowed = new BitSet(128);
 9         allowed.set(0x20, 0x7f);
10         for (int i = 0; i < separators.length(); i++) {
11             char ch = separators.charAt(i);
12             allowed.clear(ch);
13         }
14     }
15
16     void validate(String name) {  //判断一个那么是否合法
17         if (name == null || name.length() == 0) {  //先是判断是否为null或者为空字符串
18             throw new IllegalArgumentException(lStrings.getString("err.cookie_name_blank"));
19         }
20         if (!isToken(name)) {  //调用isToken进行判断。isToken负责判断这个name里面是否包含非法字符,如果有则抛出异常
21             String errMsg = lStrings.getString("err.cookie_name_is_token");
22             throw new IllegalArgumentException(MessageFormat.format(errMsg, name));
23         }
24     }
25
26     private boolean isToken(String possibleToken) { //这个名字是否合法的判断函数
27         int len = possibleToken.length();
28
29         for (int i = 0; i < len; i++) {
30             char c = possibleToken.charAt(i);
31             if (!allowed.get(c)) {  //把这个字符串的每一个字符拿出来和有效字符集合(上文中的BitSet)进行判断,如果发现一个无效字符则怎个字符串无效
32                 return false;
33             }
34         }
35         return true;
36     }
37 }
38
39 class NetscapeValidator extends CookieNameValidator {
40     // the Netscape specification describes NAME=VALUE as
41     // "a sequence of characters excluding semi-colon, comma and white space"
42     // we also exclude the ‘=‘ character that separates NAME from VALUE
43     private static final String NETSCAPE_SEPARATORS = ",; " + "=";  //等于把这些字符也当做非法字符
44
45     NetscapeValidator() {
46         super(NETSCAPE_SEPARATORS); //通过父类的构造方法传入更多的非法字符,让判断更严格。
47     }
48 }
49
50 class RFC6265Validator extends CookieNameValidator {
51     private static final String RFC2616_SEPARATORS = "()<>@,;:\\\"/[]?={} \t";  //更加多的非法字符,更加严格的名字检查
52
53     RFC6265Validator() {
54         super(RFC2616_SEPARATORS);
55
56         // special treatment to allow for FWD_SLASH_IS_SEPARATOR property
57         boolean allowSlash;
58         String prop = System.getProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");
59         if (prop != null) {
60             allowSlash = !Boolean.parseBoolean(prop);
61         } else {
62             allowSlash = !Boolean.getBoolean("org.apache.catalina.STRICT_SERVLET_COMPLIANCE");
63         }
64         if (allowSlash) {
65             allowed.set(‘/‘);
66         }
67     }
68 }
69
70 class RFC2109Validator extends RFC6265Validator {  //更加严格,避免以$开头的String作为名字
71     RFC2109Validator() {
72     }
73
74     @Override
75     void validate(String name) {
76         super.validate(name);
77         if (name.charAt(0) == ‘$‘) {
78             String errMsg = lStrings.getString("err.cookie_name_is_token");
79             throw new IllegalArgumentException(MessageFormat.format(errMsg, name));
80         }
81     }
82 }

以上源码的注释基本说明白了这个控制Cookie名字是否有效的机制。这个机制保证每一个Cookie的名字的合法性(当然具体要按照什么标准来还要再定)。

接着看Cookie的源码:

 1     private final String name;
 2     private String value;
 3
 4     private int version = 0; // ;Version=1 ... means RFC 2109 style
 5
 6     //
 7     // Attributes encoded in the header‘s cookie fields.
 8     //
 9     private String comment; // ;Comment=VALUE ... describes cookie‘s use
10     private String domain; // ;Domain=VALUE ... domain that sees cookie
11     private int maxAge = -1; // ;Max-Age=VALUE ... cookies auto-expire
12     private String path; // ;Path=VALUE ... URLs that see the cookie
13     private boolean secure; // ;Secure ... e.g. use SSL
14     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。

构造方法:

1     public Cookie(String name, String value) {
2         validation.validate(name);
3         this.name = name;
4         this.value = value;
5     }

比较直白:检查名字的合法性,然后注入。

后面跟了一大堆get和set方法,都是关于Cookie的那些属性的。

时间: 2024-11-29 03:37:04

Servlet-Cookie源码分析 源码环境:Tomcat8的相关文章

DroidPlugin源码分析插件运行环境初始化

从DroidPlugin的官方文档中我们知道. 2 在AndroidManifest.xml中使用插件的com.morgoo.droidplugin.PluginApplication: 或者在自定义的Application的onCreate()函数中,调用PluginHelper.getInstance().applicationOnCreate(getBaseContext()); 在Application的attachBaseContext()函数中,调用 PluginHelper.get

webpack源码分析——配置调试环境

无论是阅读webpack源码,还是编写webpack的plugin和loader,配置调试环境都是很有必要的.weabpack的运行环境是nodejs,调试webpack就是调试nodejs程序.我们平时使用的IDE如eclipse.webstorm都支持nodejs的调试.本文以eclipse(Version: Oxygen.1a Release (4.7.1a))为例,进行讲解. 在这个例子里面,我们使用webpack <entry> [<entry>] <output&

Spring源码分析——源码分析环境搭建

1.在Windows上安装Gradle gradle工具类似于maven,用于项目的构建,此处主要用于构建spring源码,以便我们将spring源码导入eclipse. 开发环境 Java:JDK8(必须是JDK或JRE7以上,使用java -version查看当前电脑java版本) 操作系统:Windows 安装步骤 下载最新的Gradle压缩包:Gradle官网:https://gradle.org/,当前最新版本下载地址:https://gradle.org/releases/,下载bi

mybatis源码分析之01环境搭建

直接使用maven搭建一个mybatis的运行环境 1. pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="

ncnn源码分析-004-代码流程总结

0.调用实例 先看一个调用实例,顺着调用流程探寻ncnn内部具体实现细节. #include "net.h" int main(int argc, char **argv) { ncnn::Mat in; ncnn::Mat out; ncnn::Net net; net.load_param("model.param"); net.load_model("model.bin"); ncnn::Extractor ex = net.create_

DroidPlugin源码分析插件进程管理以及预注册Activity,Service,ContentProvide的选择

在360对DroidPlugin的特点介绍中有云: 插件的四大组件完全不需要在Host程序中注册,支持Service.Activity.BroadcastReceiver.ContentProvider四大组件. 实现了进程管理,插件的空进程会被及时回收,占用内存低. 之所以支持Service,Activity,ContentProvider三大组件,是因为DroidPlugin在AndroidManifest文件中预先注册了8个运行插件的进程,每个进程预注册Service一个, Content

知识链-源码分析

源码分析 HashSet源码分析 HashMap源码分析 源码解析Servlet和HttpServlet

structs2源码分析

一.到网上下载struts2的源代码: http://mirrors.cnnic.cn/apache//struts/source/struts-2.3.16.3-src.zip 我把项目的源码路径定向到下载到的源代码: 这份是webwork的核心源码,读取配置文件的核心代码就在于此: 二.下面是struts本尊,基本实现的就是请求的分发: 二.从项目本身去做分析: 好多大牛大师傅都不断强调,去看源码分析源码,但是却从来很少人,告诉你怎么去看,经过多年的摸索我总算找到一个分析源代码的套路,特别对

Spring Core Container 源码分析三:Spring Beans 初始化流程分析

前言 本文是笔者所著的 Spring Core Container 源码分析系列之一: 本篇文章主要试图梳理出 Spring Beans 的初始化主流程和相关核心代码逻辑: 本文转载自本人的私人博客,伤神的博客: http://www.shangyang.me/2017/04/01/spring-core-container-sourcecode-analysis-beans-instantiating-process/ 本文为作者的原创作品,转载需注明出处: 源码分析环境搭建 参考 Sprin