wordpress全站禁止未登录用户访问

其实主要目的还是想把wp作为一个私人记录的场所。

简单查了一下,网上有个修改theme的方案:http://coolwhy1.iteye.com/blog/1897995

但这样做的话,担心哪天随手升级了theme之后这东西就失效了,然后若干秘密瞬间公之于众。

于是还是觉得插件开发比较适合这个场景。

查了一番,发现要找合适的hook点。但wp文档(https://codex.wordpress.org/Plugin_API/Action_Reference)实在是有些难懂,反正原理也明白了,本地搭建了一套环境,把值得怀疑的hook点挨个试了一下……

于是发现get_header有效。

大致代码:

function liveme_if_login() {
if (!is_user_logged_in()) {
auth_redirect();
}
}
function do_filt() {
liveme_if_login();
}
// Now we set that function up to execute when the admin_notices action is called
add_action( ‘get_header’, ‘do_filt’ );

半小时掌握世界上最好的语言的感觉真好。

Oracle tnslistener无法启动的问题

被oracle折腾得头疼,就在自己的虚拟机上配了一个,专门运行oracle,其它的什么都不干,一直运行得挺好。

换了新路由器之后,没有找到固定ip的功能,于是每天过来改一遍listener。今天突发奇想把所有临近域的ip都给设置进去了,然后windows突发更新,一遍一遍的重启,重启都结束之后listener就启动不起来了……

骂了半天微软,上网查了一下,原因其实是:listener的配置必须要跟本机网络的ip一致。因此,localhost可以,vm的虚拟网卡地址可以,映射到局域网的ip也可以,其它的就不行了。

Java Web开发教程——基本思路

思路:

将需求拆解为基本技术的简单组合,再分别实现。

  • 基本技能必须是足够简单且容易理解的。
  • 对基本技能的组合,必须是严格相等于需求域的,不能有缺漏。

一、基本技能

基础知识

B/S(Browser/Server),就是以浏览器作为用户界面与用户进行交互,逻辑在服务器中进行处理的项目架构

所以B/S项目的基本形式就是:

  • 用户通过浏览器向服务器请求资源或请求操作
  • 服务器操作后,将用户想看到的信息交给浏览器,浏览器将这些信息显示出来,并提供用户下一步操作的入口

Http基本流程

与C/S开发不同,B/S项目开发严格遵守“请求-》响应”的循环,任何一个需求的实现,都要拆分成若干个“请求-》响应”的流程组合。

每次“请求-》响应”的基本流程如下:

  • 前端请求-》Http传输-》后端路由-》后端处理-》响应-》前端显示

前端请求怎样发送

Http的请求,在简单项目应用中,可视作包含以下几部分:

  • url:用于服务器定位由哪个逻辑单元处理这个请求
  • 方法:GET、POST等。在简单的Java Web开发技术中,这二者没有特别严格的区分,但考虑到HTTP的语义,可大致区分为:对服务器无影响的操作,用GET;有影响的(如添加或修改了数据)用POST
  • 参数:包括参数名和参数值。每个参数名称可以对应多个值

先将需求的请求提交部分抽象为以上几部分,即url是什么、用什么方法提交、包括什么参数。再用浏览器标准支持的形式实现。

浏览器提交请求的方式可包括以下几种:

  • 在地址栏输入url并回车提交。此时提交的是GET请求。
  • 通过提交表单(form)的形式提交。此时,根据form的method属性决定提交方法,默认为GET。提交表单可以通过点击表单内type=submit的按钮或input,也可以通过js控制提交。
  • 点击超链接,做页面跳转,相当于在浏览器中输入地址。因此提交的也是GET请求。
  • 通过js控制做页面跳转,有各种各样的方式。

GET请求的参数将包含在url中,格式为

xxx.xx?param1=value1&param1=value2&param3=value3

表示将请求提交到xxx.xx,包含名称为param1的两个参数值和名称为param3的一个参数值。

Http传输

Http传输是通常俗称的前端和后端的分界线。

浏览器负责提交Http请求,通过网络设备,将该请求提交给服务器。此步骤一般不需要自行写程序实现。

可通过浏览器调试工具,查看Http传输的内容。如Chrome点击F12打开的开发工具,或Firefox下的firebugs。

对Http程序的调试,应优先关注传输部分。如果程序运行有误,先通过浏览器调试工具确定提交的请求是否与你预期的相符。如果相符,则基本确定问题出现在后端,如果不相符,则基本确定问题出现在前端。

后端路由

在项目采用的技术范畴下,可将服务器接收到Http请求开始,直到将此请求传递给为了完成该请求而写的逻辑处理单元(如Servlet、Struts的Action、SpringMvc的Controller等)为止,这其中的部分视作“路由”部分。

这部分主要解决的问题是,怎样根据Http请求的内容,将该请求传递给正确的逻辑处理单元。

以Java Web项目为例:

请求发送到服务器后,根据请求url的端口决定交由哪个程序处理,一般会由tomcat或者netty等Java Web服务器监听该端口。根据请求内容,Java Web服务器会根据web.xml的配置,决定由哪些Servlet、Filter等参与处理,一般会将请求配置分发给Struts、Springmvc等框架。

框架会根据请求url中的内容,以及符合该框架要求的配置信息,将请求分发给某个具体实现的Java类。此类为项目开发人员编写的,一般从这个步骤开始,程序的控制权开始由项目开发人员的代码控制。配置方式要解决的问题,就是http请求与Java类的映射逻辑,比如“以.action结尾的并且.前面的部分是login的请求,与LoginAction匹配”。

后端处理——数据绑定部分

在控制权转移到项目开发人员自行编写的特定代码后,仍需要解决如何将http请求中的数据包装为这些代码可以利用的形式的问题。

例如通过Java Web进行开发,必须解决前端传入的param1=value1这些参数,如何在Java程序中使用的问题。

不同的框架有不同的绑定方式,如Struts2的方式是在Action类中找到与参数名称对应的get函数,如请求中有param1=value1,则框架会在该Action程序执行之前,执行setParam1(value1),那么我们可以在这个函数中将value1赋值给某个我们之后可以利用的变量,就可以在我们自己的代码中使用了。

后端处理——逻辑部分

将前端的参数接收到之后,就可以使用当前语言提供的方式,去做特定的逻辑处理。只要参数接收正确,那么这部分的处理与一般的简单Java程序没有区别。

存取数据库

业务类系统的重要部分就是对数据的持久化操作,大部分情况下都利用数据库实现这一需求。

一般存取数据库的基本方法都会在框架层面解决,在开发之前需要了解这些基本方法,需要时直接调用即可。

Java开发一般采用比较严格的Object-Relation-Mapping模式,即每张数据库表都会有一个Java类对应。大部分情况下,此Java类就是标准的Java Bean。因此,这个步骤需要具备的技术基础,就是要解决“给定一个Java Bean,怎样保存到数据库中,怎样更新到数据库中……”这一类的问题

后端处理——视图部分

后端的逻辑处理完成后,大部分情况会生成一个用户界面,反馈给用户。具体到B/S项目开发,就是要生成Html页面以及配套的js、css等内容。

js用于控制前端的逻辑,css用于控制前端样式,这两者一般都是静态的内容,大部分情况下也不会包含在需要逻辑处理的请求中,而是通过html中的script、link等标签,让浏览器另外发送一个资源请求去获取这些文件。

页面显示的内容,通过html表示。而在逻辑处理完成后,通常情况下都会根据逻辑处理的结果,去动态生成html内容。用Java代码生成html内容的方式,将类似于:

[java]

print("&lt;html&gt;");<br data-mce-bogus="1">

print("&lt;body&gt;");<br data-mce-bogus="1">

print("&lt;h1&gt;" + title + "&lt;/h1&gt;");<br data-mce-bogus="1">

[/java]

 

非常繁琐。

因此,采用jsp的形式完成以上内容。

jsp是一种语法类似于html,但支持动态内容生成语法的语言。servlet容器将会把jsp处理成html响应给前端。

在这部分我们需要解决的问题包括:

  1. 上一步的逻辑处理单元处理完成后,将控制流程转移给哪个jsp。例如struts2中,将会通过配置view的方式,将Action函数的返回值与某个jsp文件对应。在springmvc中,可以直接返回jsp路径,也可以通过其它方式配置更复杂的对应关系。
  2. 怎样控制jsp生成html。jsp中类似于html的部分,将会直接生成同样的html。包含动态语法的部分,将会根据规则,计算出应该生成的html。这个计算过程,一般会用到上一步逻辑处理单元的逻辑处理结果。例如

    逻辑处理单元中,根据前台传入的参数,去数据库中读取了一个表示人员信息的对象,在jsp中就有对应的方法去获取这个对象的各项值,并使用。使用的方式最常见的是el表达式,另外也包含jsp标签等内容。

生成html后,容器将负责将此内容响应给客户端浏览器。

另外,也会有一些不生成html内容的流程,如最近比较流行的restful api方案,就会返回一些特定格式的json。但其原理和基本流程是不变的,都是后端通过各种方式,生成各种格式的“字符串”。这些字符串都符合前端使用者(如浏览器)的某些规范,因此可以控制前端使用者的行为。

响应

容器负责将处理结果以http相应的方式发送给前端。此相应一定是和之前的请求对应的。此部分一般也不需要自己写代码负责。

前端显示

如果是浏览器负责前端的显示和交互,则一般会运行html、js、css等几类的文件。而这些文件,是后端生成后响应给前端的。因此,后端通过上面提到的各种方式,控制这几类文件生成后的内容,再将这些内容交给前端,从而控制前端的行为。

前面提到过,在实际开发中,js、css一般都是静态文件,动态生成的部分是html。动态生成的技术主要是jsp。在编写jsp的过程中,要能分析出各种情况下生成的html是什么样子的,从而指导jsp文件的编写。

在了解以上流程后,此部分需要解决的问题就是,什么样的html、js、css,会在浏览器上显示出什么样的内容。

Html为“标记性语言”,基本语法为“标签+属性”。因此,编写html的基本思路为:什么样的需求对应什么样的标签,然后用什么样的属性去控制细节。一定不要背诵所有标签的含义,而是要理解这个思路,并且在用的时候查询即可。

js是一门程序设计语言,与java类似,初期可以互相参考理解。但js的程序设计范式与java不同,因此在后期深入时,要注意与类java语言的区别。另外,js初期的主要应用目的是改变html的内容,这部分主要对应的知识是“js的dom操作”,即“通过什么样的js可以改变某个标签的某项内容”。

前端开发的复杂之处在于,任何一项需求都有很多方式与之对应。而选择的方案如果不当,会在很久以后才会显出弊端,增加了调整的成本。我认为比较重点的原则之一是“语义化前端开发”,即语言要与含义对应,例如想显示一个表格,用table标签也可,用若干个div控制样式也可,但从语义角度讲,我们要一个表格,就一定要用table标签。

 

案例分析:完成登录功能(待补充)

一、了解需求

能做到完全表述需求的所有流程。是否做到“完全表述”,取决于表述内的所有词汇,是否都属于某个在工程范畴内已经被良好定义的“技能表”或者“词汇表”。如果不能做到,则要不断精化。

例,第一版需求:

输入用户名和密码登录

需要考虑,输入、用户名、密码、登录,这几个词汇,是否都是已经被良好定义的。之所以要强调在“工程”范畴内,是因为我们并不是在做汉语研究,没有必要追溯每个词语的最终源头,而只是在当前的工程知识范围内解释解释即可。如果是良好合作的团队,则以上几个词在要求不太严谨的情况下都不用解释。但如果是新人新团队,则需要在初期建立一定的共识。

输入:这是一个用户的动作。由于做的是web项目,所有的用户界面都由浏览器端完成,其主要实现方式是html+js+css。由于html是标准化很完善的技术,”输入“对应的是哪个标签,应该是技术上的共识,因此不必过多解释。既然是用户界面,那么一定涉及到用户体验设计。由于项目类型是”后台系统“,这一项要求也不是十分严格,采用项目成型的样式库即可。

用户名、密码:具体含义用常识理解即可。这两项

二、形成设计方案

附录:参考知识

  • Http原理
  • Html基本使用方法

Web开发教程——命令行入门

初学Java的人一般都会被如何设置path环境变量折腾一顿,到了学JavaWeb的时候,又会被CATALINA_HOME之类的环境变量再折腾一顿。了解了命令行运行程序的原理之后,就可以脱离见招拆招乱试的困境。

基础知识

  •  计算机世界中,我们写的、我们用的,都是“程序”。程序需要一定的方式启动,启动的时候,可以传递参数。
  • 双击桌面上的程序图标、点击开始菜单中的程序图标、双击某个类型的文件(如.doc)后操作系统自动用某个程序(如Word)打开,这些都是图形化Windows提供给我们的,运行程序的方法

命令行概念

操作系统会根据自身特点,提供人机交互工具,Windows的图形化界面就是一种。大部分的操作系统都会提供命令行形式的人机交互工具。

命令提示符(运行-》cmd)就是Windows下的命令行工具。大部分Linux,进去之后就直接是个shell环境了,这就是Linux下的命令行工具。

命令行,顾名思义,输入命令、换行,系统按照你的命令执行。这些所谓的命令,其实都属于上面说的程序。

图形化界面易用性好,但由于操作方式基于二维平面上的鼠标点击,想把某个操作录制成脚本,自动执行,很困难。而命令行,由于是纯文本输入输出,利于制作脚本,随便打开个纯文本编辑器,每条命令输入进去,保存,就可以了。因此,命令行是自动化执行任务的主要途径

命令行基本语法

输入命令或程序,补充一些参数,回车执行。

命令和程序的区别,暂时没有找到太严谨的定义。我的理解是,操作系统提供命令行环境本身的一些工具,如cd,属于命令。对于这些命令,并没有一个单独的程序叫cd的与之对应。而程序,应该是符合操作系统规则的独立可执行文件。

例:dir /p
  • 第一个单词,是命令或者程序名称,如上面的dir
  • 第一个单词以后的部分,是参数,如上面的/p。某些参数用于配置程序的运行方式,如dir /p用于配置分页显示。某些参数是作为路径,指定了一个文件给该程序使用。具体的参数含义,取决于该程序怎样使用,在操作系统和命令行工具层面,并没有特殊的含义。
  • >用于组合一个命令(以及参数)和一个文件,会将前一个命令在控制台输出的内容 输入到文件中
  • |用于组合两个命令(以及参数),会将前一个命令在控制台输出的内容输入给第二个命令

Path问题

输入什么能被正常执行?

命令的处理方式是特殊的,这里只说程序。

将输入的内容作为文件路径,找文件,找得到的话,如果该文件是可执行的,则执行,这样,程序就运行起来了。

所以,我们需要解决的是,输入什么样的内容,可以让我们想要的那个可执行文件被找到。

另外,在输入表示文件路径的参数的时候,也要保证该文件可以被找到,从而程序才能使用这个文件。一般来讲,程序使用文件的方式也遵循操作系统找文件的一般习惯,因此,与找可执行文件的方式相同。

那么,什么样的路径能够被找到?

路径分为绝对路径和相对路径两种。绝对路径以/开头,表示从硬盘的根目录开始找起(Windows下,如果不在一个分区内,则通过盘符区分,如c:/abc……)。相对路径不以“/”开头,其“相对”指的是当前目录,即从当前目录开始,根据相对路径的内容找起。

任何时刻,命令行都有“当前目录”的概念。windows命令行工具中,会在行首提示当前目录,如:

D:\var>

可以通过cd命令切换当前目录。linux环境下的显示和操作方式也类似。

如果输入的是绝对路径,那么与当前目录无关,直接按照路径内容找就可以了。

如果输入的是相对路径,则以当前目录为基础,加上相对路径,进行查找。如当前的目录为d:/abc,输入的内容为bcd/ef.exe,则最终找到的内容为d:/abc/bcd/ef.exe。

相对路径中有两个特殊符号,一个点(.)表示当前目录,两个点(..)表示上级目录。如当前目录为d:/abc,输入的内容为../ef.exe,则找到的内容为d:/ef.exe。

环境变量

环境变量是指,在操作系统环境下命令行可以读取的,统一预定义的变量内容。在命令行下可以通过set命令查看和配置。

Window下有个特殊的环境变量,叫path,配置了多个目录,用“;”分隔。如果在命令行运行程序时输入的是不包含路径的程序名称,那么查找过程也会包含path中的所有路径。在查找顺序上,Windows貌似是先当前目录后path,而linux相反。

案例分析:永远找不到的javac

我刚学习java的时候,经历过一下午的各种找不到。貌似现在的jdk已经能在安装后让你直接运行了,如果是这样的话,那就换成永远都找不到的ant,永远都找不到的mvn,都可以。

当你输入javac并回车时,操作系统要怎样找这个程序?在当前路径中找,找不到的话就去path中找。如果都没有,那当然会提示找不到。

网上一定给出了很多教你如何设置path的方法,根据上面说的内容,应该可以知道为什么这样设置就能成功执行了。那再加个题目,能不能找到除了设置path外的第二种方法?用绝对路径就可以了。

那再加个一个题目,装完了oracle之后,eclipse就启动不起来了,是什么原因?

答案:eclipse要javac才能启动-》找javac的时候,依赖了path变量中的内容-》安装oracle时,oracle附带装了一个低版本的jdk,并且放到了path中的最靠前位置-》eclipse不能用这个低版本的jdk启动,于是报错了。

其它

本文中的不严谨

各种定义,措辞,基本上都是我随口说的,并没有经过严格考证。比如所有的内容,都是在我的当前操作系统,当前程序设计环境,当前的业务需求下想到,换成类linux环境,/和\的区别就能让这里的例子基本失效。

不要背诵本文中的任何结论,我想表达的是在我得出这些结论时的思考过程。

 

 

项目开发进度管理方法论——功能点

功能点

功能点作为需求调研的主要成果产物,用于说明项目的所有功能性要求

功能点的整理形式为逐级拆分形式,采用恰当的文档形式,对应项目需求。最高层的结点即为项目名称,对应项目的全部需求。其余结点逐层拆分,每一个结点的所有子节点的内容相加,应等价于该结点

根据以上形式,功能点的形式应为一棵,其叶子节点是任务安排的最小单位。一般来讲,每个叶子节点的开发工作量预期不应超过4人时。同时,应同构任务的拆分,逐步明确技术方案。至叶子节点处,应做到实现方案明确,即通过项目架构下的成熟方案的简单组合,可实现该节点对应的功能。

在文档形式选择上,应利于形成以上的树结构,并且利于修改。在实践中,我们采用xmind软件,用思维导图的形式体现功能点。

功能点估算模型

优先级

  • 未定(?):是否实现该功能点,由于某些原因尚未确定。
  • 核心级(零级):该产品之所以为该产品的核心特性对应的功能点。例如学生管理系统中的添加学生功能。
  • 重要级(一级):保证该产品能够成功运行的功能点,换言之,该功能点没有完成时,该产品整体处于不能使用状态。例如Web项目中的大部分框架功能(如用户登录),学生管理系统中的学生列表显示。
    以上两级,都属于产品发布时必须实现的功能。区别在于,核心级的需要更多考虑优化问题。
  • 亮点级(二级):完成后,可以使产品增加亮点,从而增加客户的满意程度。
  • 期望级(三级):用户期望拥有的功能。
    以上两级应争取实现。如果项目发布周期紧而放弃,应评价该项目完成情况较差。
  • 非重要级(四级):实现后有好处,但不实现,也不存在太大的坏处。
    项目周期紧时,优先放弃此级。
  • 反向级(九级):功能点的实现目标,与其它功能点(特别是前四级的功能点)有冲突。开发此类功能点的任务,必须在通盘考虑所有功能点,并且有对冲突有明确结论的情况下才能开展。

复杂度

  • 0:未定义,还未能做到有明确用例。此级的功能点倾向于暂不实现,必须先确定用例后划定复杂度,再进行实现。
  • 1:简单级,包含2个以下正常用例和3个以下异常用例。或用常识即可取得一致理解。
  • 2:复杂级,包含3~5个正常用例或4~8个异常用例。
  • 3~5:根据用例数类推。一般不超过5级。
  • 9:待定级,没有明确用例。此类功能点归入9级而不是0级的原因是,由于某些原因,无法明确用例,因此需要特殊的方案处理,例如先随意实现一版做思维引导。此级别的功能点的开发方案,需要特殊处理。

技术风险

  • 0:无任何技术风险,由1个项目架构中的成熟方案即可实现,或者由不超过3个项目架构中的成熟方案组合实现,且组合方式也有明确方案或丰富经验。项目组任何一个人对其进行独立实现时,选择的技术方案不会与项目技术负责人的想法有差别,且最终完成时间与项目负责人的预估不会有明显偏差。
  • 1:一般,由项目架构中的成熟方案即可实现,接近于无风险。
  • 2:有风险:由项目架构中的成熟方案+高度内聚的特定技术方案可实现,例如某流程中包含微信支付功能。定义为此级别的功能点,需明确指出其技术风险点,如“微信支付”。实现此级别功能点时,需要有特定技术方案的详细参考资料和解决方案。
  • 3:有较大风险:对现有架构方案中的通用性约定作调整,如之前的架构方案是简单页面,而新功能点中需要大量使用iframe。
  • 3:有较大风险:需对现有架构方案中的内容做结构性调整,如之前的架构方案是直连数据库,某功能点则要求提供缓存。
  • 9:极大风险:与现有架构方案完全不兼容,如之前的架构方案是Java Web,但此功能点要求开发iOs app。

 

 

Java Web开发教程——技能表(待续)

程序设计基础

设计基本流程

需要掌握思路。所有独立程序都要能转化为以下几项内容的组合

  • 顺序
  • 循环
  • 判断
  • 函数调用:区分“调用”和“实现”。在当前层面不需要考虑的内容,可先定义函数接口。如考虑新建人员信息功能时,对“保存到数据库”的需求,可视作一个函数调用。在设计数据库底层代码时再考虑如何实现。

java基本环境及IDE使用

  • Jdk的安装、环境变量设置
  • 命令行运行Java HelloWorld
  • eclipse的下载及安装运行

Java Core

基本语法

  • 所有关键字的含义
  • 程序设计基本流程的实现方式

核心类库

  • java.lang下的大部分包
  • String类的常用方法,及StringBuilder等相关类
  • java.util.concurrent并发程序的编写

subversion

  • 安装tortoiesSvn,并配置使用命令行svn
  • 安装eclipse插件 subversion
  • 从服务器下载文件夹到本地
  • 分辨哪些是本地待提交的数据
  • 更新
  • 提交及编写提交注释
  • 查看资源历史记录
  • 理解资源冲突,解决冲突
  • 在服务端复制或移动资源(要求保留历史记录)

除了在服务端的操作外,要求掌握命令行操作、tortoiseSvn及eclipse三种方式

maven

  • maven基本概念
  • eclipse中建立maven简单项目,编写hello world,在eclipse中运行
  • 在命令行中将maven项目打成jar包,并运行

http basic

  • http基本流程。能把所有流程抽象为请求(url+参数(名称/值))+响应,并解释清楚。
  • 采用任何一种基于http的技术开发,都要能说明每一行代码是在前端运行还是在后端运行

java web

  • tomcat的安装和运行
  • 改变tomcat端口
  • eclipse中建立maven web项目,编写简单的jsp页面,在eclipse中使用tomcat插件运行
  • 在eclipse中、及在命令行中用maven将web项目打成war包,放到tomcat中运行
  • servlet基本概念
  • jsp基本概念
  • 理解:jsp是后端程序而不是前端程序
  • 理解:jsp本质上是servlet。并说明jsp如何转化为servlet
  • jstl常见标签的使用,if、when、foreach等
  • mvc框架的基本使用:对于需要使用的mvc框架,能说明其数据绑定方式。要求至少包含数字、字符串、日期、数组、对象几种类型。

Db

Mysql

  • 安装
  • 通过命令行的MySQL程序访问数据库
  • 创建数据库
  • 创建表
  • 插入数据
  • 创建数据库用户、修改密码、分配权限

Jdbc&Mybatis

  • Java 控制台项目,任意一张表,使用Mybatis进行增删改查操作

其它类库

slf4j及logback

  • 理解常见日志的级别及含义
  • 理解为什么要有slf4j和logback两个东西,在程序中应当用哪个api
  • 理解现行程序中创建Logger的方式、目的
  • 配置在控制台中显示log
  • 配置在文件中显示log
  • 配置任一类的log、任一包下的所有log以任意级别显示
  • 配置日志显示格式,如显示几层堆栈、是否显示线程id等

Linux basics

  • ssh登录linux
  • 理解linux权限模型
  • 理解linux目录结构
  • 基本文件操作:切换当前目录、创建目录、创建文件、删除文件、删除整个目录
  • 运行程序
  • chmod修改权限

servlet在同一域下jsession冲突问题现象记录之二

重新想了想 http://nonesuccess.me/?p=363 中的现象,梳理一下思路,解决一些上次没解释请的问题。

我最初的想法类似于朴素的字符串前缀规则,端口是比域次一级的概念,但是比path高一级,因为排在前面……根据经验,同端口是存在cookie共享的,所以path也一定存在。

经过上次的测试,这个结论是错误的。同域下的同个webapp共享cookie,涉及到不同端口时,就是同名webapp共享cookie。但是不同名的webapp不会共享。

但是仔细分析一下,道理上还是讲不通。webapp是服务端servlet下的概念,浏览器并不知道服务端的架构,因此用webapp是否是一个或者是否同名,显然不是这个现象的根本原因。

查了查servlet设置cookie的apihttps://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/Cookie.html,又查了查cookie的rfchttps://tools.ietf.org/html/rfc6265,发现cookie大致有这么几个属性:

  • domain:域
  • path:cookie适用的路径
  • maxAge:有效期
  • ……

浏览器能知道的只有domain和path,所以servlet是通过设置不同的path的手段,去控制在不同webapp之间,是否共享cookie。至于上面那些现象,只是这些手段推断出来的必然结果。

==假装分割线==

人总是不能避免将精力放在把“同名webapp共享,不同名的webapp不共享”当作结论背下来上。找到root cause不是一句简单的口号,在实践中更多的问题在于无法合理的划分做到哪一步才算是root cause,从而有了“反正也找不到根本原因,所以先背下来一个”的心态。

知止而后有定。

servlet在同一域下jsession冲突问题现象记录

之前知道同一个ip或者域名下,会有session相互覆盖的问题,原因是cookie中的jsession会互相覆盖,可以通过在tomcat中修改jsessionid的cookie名称的方式解决。

今天在研究单点登录的时候突然想到,tomcat的webapps文件夹下可以随便扔war包,也就是说,tomcat是支持在同一个host下部署多个webapp程序的,这显然是在一个域中。根据经验,这样是完全可行的,因此与之前的结论有矛盾。

新建了个javaweb项目,名字叫webapp,放了一个session listener,在session create的时候打印一句日志。然后把这个项目复制了一份,名字改成webapp2。两个项目同时启动。

通过浏览器访问webapp,控制台中输出了开始session的信息。访问webapp2,控制台中也输出了开始session2的信息。按照我的预计,由于是同域,此时webapp的jsessionid cookie应该已经被覆盖了,再访问webapp应该开始一个新session。在浏览器中试了一下,发现控制台中没有打印信息。两个webapp随意交错刷新页面,都没有导致新建session。说明这个流程下,没有发生cookie互相覆盖的问题。

通过chrome查看cookie内容也印证了这一点,确实有两个不同的jsessionid,而且分别保持不变。跟之前得出的结论不符。

再仔细看了一下,发现chrome中的cookie显示界面中还有path这一项,说不行cookie不仅仅是跟域相关,还跟path相关。

于是把两个项目分别放在监听不同端口的两个tomcat中运行,但是项目的文件夹名字都改成webapp,再刷新,就开始发生cookie了。

具体现象是:

访问webapp页面,webapp启动一个session,cookie中增加一个jsessionid。

访问webapp2页面,webapp2启动一个session,cookie中增加jsessionid,由于在同一个域下,将webapp中的相应内容覆盖了。

访问webapp页面,此时浏览器提交的是webapp2的jsessionid,服务端找不到对应的session,于是又创建了session,重新在cookie中存放了jsessionid。

访问webapp2页面,类似于上一步,又创建了新的session。

大致结论:

同域的相同名称的webapp会共享cookie,不论是否在同一个端口下。(当然如果是两个不同的webapp,一定不在同一个端口下)

同域的不同名称的webapp不会共享cookie,不论是否在同一个端口下。

即:

192.168.0.100:8080/webapp1与192.168.0.100:8080/webapp2不共享jsessionid。

192.168.0.100:8080/webapp与192.168.0.100:8080/webapp共享jsessionid。