精华内容
下载资源
问答
  • session cookie

    千次阅读 2010-12-22 08:53:00
    摘要:虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session...

    摘要:虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。
    目录:
    一、术语session
    二、HTTP协议与状态保持
    三、理解cookie机制
    四、理解session机制
    五、理解javax.servlet.http.HttpSession
    六、HttpSession常见问题
    七、跨应用程序的session共享
    八、总结
    参考文档一、术语session

    在我的经验里,session这个词被滥用的程度大概仅次于transaction,更加有趣的是transactionsession在某些语境下的含义是相同的。
    session
    ,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 session。有时候我们可以看到这样的话在一个浏览器会话期间,...”,这里的会话一词用的就是其本义,是指从一个浏览器窗口打开到关闭这个期间 。最混乱的是用户(客户端)在一次会话期间这样一句话,它可能指用户的一系列动作(一般情况下是同某个具体目的相关的一系列动作,比如从登录到选购商品到结账登出这样一个网上购物的过程,有时候也被称为一个transaction),然而有时候也可能仅仅是指一次连接,也有可能是指含义,其中的差别只能靠上下文来推断
    然而当session一词与网络协议相关联时,它又往往隐含了面向连接/保持状态这样两个含义,面向连接指的是在通信双方在通信之前要先建立一个通信的渠道,比如打电话,直到对方接了电话通信才能开始,与此相对的是写信,在你把信发出去的时候你并不能确认对方的地址是否正确,通信渠道不一定能建立,但对发信人来说,通信已经开始了。保持状态则是指通信的一方能够把一系列的消息关联起来,使得消息之间可以互相依赖,比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有一个TCP session”或者一个POP3 session”
    而到了web服务器蓬勃发展的时代,sessionweb开发语境下的语义又有了新的扩展,它的含义是指一类用来在客户端与服务器之间保持状态的解决方案。有时候session也用来指这种解决方案的存储结构,如xxx保存在session 。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持,所以在某种特定语言的语境下,session也被用来指代该语言的解决方案,比如经常把Java里提供的javax.servlet.http.HttpSession简称为session
    鉴于这种混乱已不可改变,本文中session一词的运用也会根据上下文有不同的含义,请大家注意分辨。
    在本文中,使用中文浏览器会话期间来表达含义,使用“session机制来表达含义,使用“session”表达含义,使用具体的“HttpSession”来表达含义

    二、HTTP协议与状态保持
    HTTP
    协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的,好比一个顾客和一个自动售货机或者一个普通的(非会员制)大卖场之间的关系一样。

    然而聪明(或者贪心?)的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、 cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。

    让我们用几个例子来描述一下cookiesession机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠,然而一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:

    1
    、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。

    2
    、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。

    3
    、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。

    由于HTTP协议是无状态的,而出于种种考虑也不希望使之成为有状态的,因此,后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。

    三、理解cookie机制

    cookie
    机制的基本原理就如上面的例子一样简单,但是还有几个问题需要解决:会员卡如何分发;会员卡的内容;以及客户如何使用会员卡

    正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie

    cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示,如果某家分店还发行了自己的会员卡,那么进这家店的时候除了要出示麦当劳的会员卡,还要出示这家店的会员卡。

    cookie
    的内容主要包括:名字,值,过期时间,路径和域。

    其中域可以指定某一个域比如.google.com,相当于总店招牌,比如宝洁公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com,可以用飘柔来做比。

    路径就是跟在域名后面的URL路径,比如/或者/foo等等,可以用某飘柔专柜做比。

    路径与域合在一起就构成了cookie的作用范围。

    如果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。

    存储在硬盘上的cookie 可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。对于IE,在一个打开的窗口上按 Ctrl-N(或者从文件菜单)打开的窗口可以与原窗口共享,而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie;对于 Mozilla Firefox0.8,所有的进程和标签页都可以共享同样的cookie。一般来说是用javascriptwindow.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。

    下面就是一个goolge设置cookie的响应头的例子

    HTTP/1.1 302 Found

    Location: http://www.google.com/intl/zh-CN/

    Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:

    S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com

    Content-Type: text/html


    这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分


    浏览器在再次访问goolge的资源时自动向外发送cookie

    使用Firefox可以很容易的观察现有的cookie的值

    使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。


    IE
    也可以设置在接受cookie前询问


    这是一个询问接受cookie的对话框。



    四、理解session机制

    session
    机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。



    当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个 session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session idsession id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。

    保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID,而。比如weblogic对于web应用程序生成的cookieJSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是 JSESSIONID

    由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid=

    ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764

    另一种是作为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764

    这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。

    为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id

    另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如下面的表单

    <form name="testform" action="/xxx">

    <input type="text">

    </form>

    在被传递给客户端之前将被改写成

    <form name="testform" action="/xxx">

    <input type="hidden" name="jsessionid"

    value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">

    <input type="text">

    </form>

    这种技术现在已较少应用,笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。

    实际上这种技术可以简单的用对action应用URL重写来代替。



    在谈论session机制的时候,常常听到这样一种误解只要关闭浏览器,session就消失了。其实可以想象一下会员卡的例子,除非顾客主动对店家提出销卡,否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的,除非程序通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个 session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够找到原来的session

    恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

    五、理解javax.servlet.http.HttpSession

    HttpSession
    Java平台对session机制的实现规范,因为它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异。这里我们以BEAWeblogic Server8.1作为例子来演示。

    首先,Weblogic Server提供了一系列的参数来控制它的HttpSession的实现,包括使用cookie的开关选项,使用URL重写的开关选项,session持久化的设置,session失效时间的设置,以及针对cookie的各种设置,比如设置cookie的名字、路径、域, cookie的生存时间等。

    一般情况下,session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的session也会被清空,如果设置了session的持久化特性,服务器就会把session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用, Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。

    复制严格说来不算持久化保存,因为session实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进程中,这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session

    cookie
    生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。

    cookie
    的路径对于web应用程序来说是一个非常重要的选项,Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。

    关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869


    六、HttpSession常见问题

    (在本小节中session的含义为的混合)

    1
    session在何时被创建

    一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <% @page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。

    由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。

    2
    session何时被删除

    综合前面的讨论,session在下列情况下被删除a.程序调用HttpSession.invalidate();b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;c.服务器进程被停止(非持久session

    3
    、如何做到在浏览器关闭时删除session

    严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。

    4
    、有个HttpSessionListener是怎么回事

    你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListenerHttpSessionActivationListener HttpSessionAttributeListener

    5
    、存放在session中的对象必须是可序列化的吗

    不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在 Weblogic Serversession中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果 session中有不可序列化的对象,在session销毁时会有一个Exception,很奇怪。

    6
    、如何才能正确的应付客户端禁止cookie的可能性

    对所有的URL使用URL重写,包括超链接,formaction,和重定向的URL,具体做法参见[6]

    http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

    7
    、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session

    参见第三小节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。

    8
    、如何防止用户打开两个浏览器窗口操作导致的session混乱

    这个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在session里,客户端提交表单时必须把这个id也返回服务器,程序首先比较返回的id与保存在session里的值是否一致,如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id,以防主窗口无法操作,建议不要再window.open打开的窗口里做修改操作,这样就可以不用设置。

    9
    、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue

    做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变,需要向其他服务器进程复制新的session值。

    10
    、为什么session不见了

    排除session正常失效的因素之外,服务器本身的可能性应该是微乎其微的,虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过;浏览器插件的可能性次之,笔者也遇到过3721插件造成的问题;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。

    出现这一问题的大部分原因都是程序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。

    七、跨应用程序的session共享

    常常有这样的情况,一个大项目被分割成若干小项目开发,为了能够互不干扰,要求每个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够访问彼此的session

    然而按照Servlet规范,session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范,但是实现的细节却可能各有不同,因此解决跨应用程序session共享的方法也各不相同。

    首先来看一下Tomcat是如何实现web应用程序之间session的隔离的,从 Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。

    根据这个特性,我们可以推测Tomcatsession的内存结构大致如下。


    笔者以前用过的iPlanet也采用的是同样的方式,估计SunONEiPlanet之间不会有太大的差别。对于这种方式的服务器,解决的思路很简单,实际实行起来也不难。要么让所有的应用程序共享一个session id,要么让应用程序能够获得其他应用程序的session id

    iPlanet
    中有一种很简单的方法来实现共享一个session id,那就是把各个应用程序的cookie路径都设为/(实际上应该是/NASApp,对于应用程序来讲它的作用相当于根)。

    <session-info>

    <path>/NASApp</path>

    </session-info>



    需要注意的是,操作共享的session应该遵循一些编程约定,比如在session attribute名字的前面加上应用程序的前缀,使得 setAttribute("name", "neo")变成setAttribute("app1.name", "neo"),以防止命名空间冲突,导致互相覆盖。

    Tomcat中则没有这么方便的选择。在Tomcat版本3上,我们还可以有一些手段来共享session。对于版本4以上的Tomcat,目前笔者尚未发现简单的办法。只能借助于第三方的力量,比如使用文件、数据库、JMS或者客户端cookieURL参数或者隐藏字段等手段。

    我们再看一下Weblogic Server是如何处理session的。

    从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/,这是不是意味着在Weblogic Server中默认的就可以共享session了呢?然而一个小实验即可证明即使不同的应用程序使用的是同一个session,各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下


    对于这样一种结构,在 session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量,比如使用文件、数据库、JMS或者客户端 cookieURL参数或者隐藏字段等手段,还有一种较为方便的做法,就是把一个应用程序的session放到ServletContext中,这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下,

    应用程序A

    context.setAttribute("appA", session);
    应用程序B

    contextA = context.getContext("/appA");

    HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");



    值得注意的是这种用法不可移植,因为根据ServletContextJavaDoc,应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值,以上做法在Weblogic Server 8.1中通过。

    那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢?原来是为了SSO,凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点,修改首先登录的那个应用程序的描述符weblogic.xml,把cookie路径修改为/appA 访问另外一个应用程序会重新要求登录,即使是反过来,先访问cookie路径为/的应用程序,再访问修改过路径的这个,虽然不再提示登录,但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM,因为浏览器和web服务器对basic认证方式有其他的处理方式,第二次请求的认证不是通过 session来实现的。具体请参看[7] secion 14.8 Authorization,你可以修改所附的示例程序来做这些试验。

    八、总结

    session
    机制本身并不复杂,然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器,服务器的经验当作普遍适用的经验,而是始终需要具体情况具体分析。

    摘要:虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答

    展开全文
  • session cookie token 区别

    千次阅读 2017-04-26 13:31:01
    session cookie token 的区别

    session cookie token 的区别

    session:服务器端;记录登录信息;当用户打开某个web应用时,便与web服务器产生一次session。服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后                     session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时                 候session会丢失。


    cookie:客户端;txt文件记录你的登录信息;cookie是保存在本地终端的数据。cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下              一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所              以每个域的cookie数量是有限的。


    token:客户端;验证你有权登录;token的意思是“令牌”,是用户身份的验证方式。


    token就是令牌,比如你授权(登录)一个程序时,他就是个依据,判断你是否已经授权该软件;cookie就是写在客户端的一个txt文件,里面包括你登录信息之类的,这样你下次在登录某个网站,就会自动调用cookie自动登录用户名;session和cookie差不多,只是session是写在服务器端的文件,也需要在客户端写入cookie文件,但是文件里是你的浏览器编号.Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。
    展开全文
  • session_set_cookie_params(): Cannot change session cookie parameters when session is active 报错什么鬼,就是说此时有一个处在生命周期内的“活体”,不能强行改变cookie,会被拒绝访问。 ok。。**session_...

    session_set_cookie_params(): Cannot change session cookie parameters when session is active

    报错什么鬼,就是说此时有一个处在生命周期内的“活体”,不能强行改变cookie,会被拒绝访问。
    ok。。
    if(isset($_SESSION)){
    session_destroy();
    }

    再重新设置以前把此“活体”销毁掉,就好了,真实有效。这是php7.2带来的问题,但是我们也看到php朝着更好的方向发展。其他方法请参考
    https://bugs.php.net/bug.php?id=75650&thanks=2

    展开全文
  • session cookie 原理

    千次阅读 2013-03-13 09:51:31
    session cookie 原理 在浏览器地址栏输入javascript:alert (document. cookie)会话(Session) 跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是CookieSessionCookie通过在客户端...
    
    

    在浏览器地址栏输入javascript:alert (document. cookie)会话(Session) 跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户 身份,Session通过在服务器端记录信息确定用户身份。本章将系统地讲述Cookie与Session机制,并比较说明什么时候不能用Cookie, 什么时候不能用Session。
    1.1 Cookie机制
    在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二 者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的 购物车内,这不属于同一个会话。
    而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据 需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用 户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。
    Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。
    1.1.1 什么是Cookie
    Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。
    由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。
    Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。

    查看某个网站颁发的Cookie很简单。在浏览器地址栏输入javascript:alert (document. cookie)就可以了(需要有网才能查看)。JavaScript脚本会弹出一个对话框显示本网站颁发的所有Cookie的内容,如图1.1所示。

    图1.1 Baidu网站颁发的Cookie
    图1.1中弹出的对话框中显示的为Baidu网站的Cookie。其中第一行BAIDUID记录的就是笔者的身份helloweenvsfei,只是Baidu使用特殊的方法将Cookie信息加密了。
    %注意:Cookie功能需要浏览器的支持。如果浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功 能就会失效。不同的浏览器采用不同的方式保存Cookie。IE浏览器会在“C:\Documents and Settings\你的用户名\Cookies”文件夹下以文本文件形式保存,一个文本文件保存一个Cookie。
    1.1.2 记录用户访问次数
    Java中把Cookie封装成了javax.servlet.http.Cookie类。每个Cookie都是该Cookie类的对象。服务器 通过操作Cookie类对象对客户端Cookie进行操作。通过request.getCookie()获取客户端提交的所有Cookie(以Cookie[]数组形式返回),通过response.addCookie(Cookie cookie)向客户端设置Cookie。
    Cookie对象使用key-value属性对的形式保存用户状态,一个Cookie对象保存一个属性对,一个request或者response同时使用多个Cookie。因为Cookie类位于包javax.servlet.http.*下面,所以JSP中不需要import该 类。
    1.1.3 Cookie的不可跨域名性
    很多网站都会使用Cookie。例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。那浏览器访问Google会不会也携带上Baidu颁发的Cookie呢?或者Google能不能修改Baidu颁发的Cookie呢?
    答案是否定的。Cookie具有不可跨域名性。根据Cookie规范,浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。
    Cookie在客户端是由浏览器来管理的。浏览器能够保证Google只会操作Google的Cookie而不会操作Baidu的Cookie, 从而保证用户的隐私安全。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。Google与Baidu的域名不一样,因此Google不 能操作Baidu的Cookie。
    需要注意的是,虽然网站images.google.com与网站www.google.com同属于Google,但是域名不一样,二者同样不能互相操作彼此的Cookie。
    %注意:用户登录网站www.google.com之后会发现访问images.google.com时登录信息仍然有效,而普通的Cookie是做不到的。这是因为Google做了特殊处理。本章后面也会对Cookie做类似的处理。
    1.1.4 Unicode编码:保存中文
    中文与英文字符不同,中文属于Unicode字符,在内存中占4个字符,而英文属于ASCII字符,内存中只占2个字节。Cookie中使用Unicode字符时需要对Unicode字符进行编码,否则会乱码。
    %提示:Cookie中保存中文只能编码。一般使用UTF-8编码即可。不推荐使用GBK等中文编码,因为浏览器不一定支持,而且JavaScript也不支持GBK编码。
    1.1.5 BASE64编码:保存二进制图片
    Cookie不仅可以使用ASCII字符与Unicode字符,还可以使用二进制数据。例如在Cookie中使用数字证书,提供安全度。使用二进制数据时也需要进行编码。
    %注意:本程序仅用于展示Cookie中可以存储二进制内容,并不实用。由于浏览器每次请求服务器都会携带Cookie,因此Cookie内容不宜过多,否则影响速度。Cookie的内容应该少而精。
    1.1.6 设置Cookie的所有属性
    除了name与value之外,Cookie还具有其他几个常用的属性。每个属性对应一个getter方法与一个setter方法。Cookie类的所有属性如表1.1所示。
    表1.1 Cookie常用属性
    属 性 名 描 述
    String name 该Cookie的名称。Cookie一旦创建,名称便不可更改
    Object value 该Cookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码
    int maxAge 该Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器 即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1
    boolean secure 该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false
    String path 该Cookie的使用路径。如果设置为“/sessionWeb/”,则只有contextPath为“/sessionWeb”的程序可以访问该Cookie。如果设置为“/”,则本域名下contextPath都可以访问该Cookie。注意最后一个字符必须为“/”
    String domain 可以访问该Cookie的域名。如果设置为“.google.com”,则所有以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.”
    String comment 该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明
    int version 该Cookie使用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范
    1.1.7 Cookie的有效期
    Cookie的maxAge决定着Cookie的有效期,单位为秒(Second)。Cookie中通过getMaxAge()方法与setMaxAge(int maxAge)方法来读写maxAge属性。
    如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到 对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信 息将永远有效。
    Cookie cookie = new Cookie("username", "helloweenvsfei"); // 新建Cookie
    cookie.setMaxAge(Integer.MAX_VALUE); // 设置生命周期为MAX_VALUE
    response.addCookie(cookie); // 输出到客户端
    如果maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie即失效。maxAge为 负数的Cookie,为临时性Cookie,不会被持久化,不会被写到Cookie文件中。Cookie信息保存在浏览器内存中,因此关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为–1。
    如果maxAge为0,则表示删除该Cookie。Cookie机制没有提供删除Cookie的方法,因此通过设置该Cookie即时失效实现删除Cookie的效果。失效的Cookie会被浏览器从Cookie文件或者内存中删除,例如:
    Cookie cookie = new Cookie("username", "helloweenvsfei"); // 新建Cookie
    cookie.setMaxAge(0); // 设置生命周期为0,不能为负数
    response.addCookie(cookie); // 必须执行这一句
    response对象提供的Cookie操作方法只有一个添加操作add(Cookie cookie)。要想修改Cookie只能使用一个同名的Cookie来覆盖原来的Cookie,达到修改的目的。删除时只需要把maxAge修改为0即可。
    %注意:从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期。
    1.1.8 Cookie的修改、删除
    Cookie并不提供修改、删除操作。如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie。
    如果要删除某个Cookie,只需要新建一个同名的Cookie,并将maxAge设置为0,并添加到response中覆盖原来的Cookie。注意是0而不是负数。负数代表其他的意义。读者可以通过上例的程序进行验证,设置不同的属性。
    %注意:修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。
    1.1.9 Cookie的域名
    Cookie是不可跨域名的。域名www.google.com颁发的Cookie不会被提交到域名www.baidu.com去。这是由Cookie的隐私安全机制决定的。隐私安全机制能够禁止网站非法获取其他网站的Cookie。
    正常情况下,同一个一级域名下的两个二级域名如www.helloweenvsfei.com和images.helloweenvsfei.com也不能交互使用Cookie,因为二者的域名并不严格相同。如果想所有helloweenvsfei.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数,例如:
    Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie
    cookie.setDomain(".helloweenvsfei.com"); // 设置域名
    cookie.setPath("/"); // 设置路径
    cookie.setMaxAge(Integer.MAX_VALUE); // 设置有效期
    response.addCookie(cookie); // 输出到客户端
    读者可以修改本机C:\WINDOWS\system32\drivers\etc下的hosts文件来配置多个临时域名,然后使用setCookie.jsp程序来设置跨域名Cookie验证domain属性。
    注意:domain参数必须以点(".")开始。另外,name相同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名完全不同的网站共有Cookie,可以生成两个Cookie,domain属性分别为两个域名,输出到客户端。
    1.1.10 Cookie的路径
    domain属性决定运行访问Cookie的域名,而path属性决定允许访问Cookie的路径(ContextPath)。例如,如果只允许/sessionWeb/下的程序使用Cookie,可以这么写:
    Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie
    cookie.setPath("/session/"); // 设置路径
    response.addCookie(cookie); // 输出到客户端
    设置为“/”时允许所有路径使用Cookie。path属性需要使用符号“/”结尾。name相同但domain相同的两个Cookie也是两个不同的Cookie。
    %注意:页面只能获取它属于的Path的Cookie。例如/session/test/a.jsp不能获取到路径为/session/abc/的Cookie。使用时一定要注意。
    1.1.11 Cookie的安全属性
    HTTP协议不仅是无状态的,而且是不安全的。使用HTTP协议的数据不经过任何加密就直接在网络上传播,有被截获的可能。使用HTTP协议传输 很机密的内容是一种隐患。如果不希望Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为true。浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。下面的代码设置secure属性为true:
    Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie
    cookie.setSecure(true); // 设置安全属性
    response.addCookie(cookie); // 输出到客户端
    %提示:secure属性并不能对Cookie内容加密,因而不能保证绝对的安全性。如果需要高安全性,需要在程序中对Cookie内容加密、解密,以防泄密。
    1.1.12 JavaScript操作Cookie
    Cookie是保存在浏览器端的,因此浏览器具有操作Cookie的先决条件。浏览器可以使用脚本程序如JavaScript或者VBScript等操作Cookie。这里以JavaScript为例介绍常用的Cookie操作。例如下面的代码会输出本页面所有的Cookie。
    <script>document.write(document.cookie);</script>
    由于JavaScript能够任意地读写Cookie,有些好事者便想使用JavaScript程序去窥探用户在其他网站的Cookie。不过这 是徒劳的,W3C组织早就意识到JavaScript对Cookie的读写所带来的安全隐患并加以防备了,W3C标准的浏览器会阻止JavaScript读写任何不属于自己网站的Cookie。换句话说,A网站的JavaScript程序读写B网站的Cookie不会有任何结果。
    1.1.13 案例:永久登录
    如果用户是在自己家的电脑上上网,登录时就可以记住他的登录信息,下次访问时不需要再次登录,直接访问即可。实现方法是把登录信息如账号、密码等保存在Cookie中,并控制Cookie的有效期,下次访问时再验证Cookie中的登录信息即可。
    保存登录信息有多种方案。最直接的是把用户名与密码都保持到Cookie中,下次访问时检查Cookie中的用户名与密码,与数据库比较。这是一种比较危险的选择,一般不把密码等重要信息保存到Cookie中。
    还有一种方案是把密码加密后保存到Cookie中,下次访问时解密并与数据库比较。这种方案略微安全一些。如果不希望保存密码,还可以把登录的时间戳保存到Cookie与数据库中,到时只验证用户名与登录时间戳就可以了。
    这几种方案验证账号时都要查询数据库。本例将采用另一种方案,只在登录时查询一次数据库,以后访问验证登录信息时不再查询数据库。实现方式是把账 号按照一定的规则加密后,连同账号一块保存到Cookie中。下次访问时只需要判断账号的加密规则是否正确即可。本例把账号保存到名为account的Cookie中,把账号连同密钥用MD1算法加密后保存到名为ssid的Cookie中。验证时验证Cookie中的账号与密钥加密后是否与Cookie中的ssid相等。相关代码如下:
    代码1.8 loginCookie.jsp
    <%@ page language="java" pageEncoding="UTF-8" isErrorPage="false" %>
    <%! // JSP方法
    private static final String KEY = ":cookie@helloweenvsfei.com";
    // 密钥
    public final static String calcMD1(String ss) { // MD1 加密算法
    String s = ss==null ? "" : ss; // 若为null返回空
    char hexDigits[] = { '0', '1', '2', '3', '4', '1', '6', '7', '8', '9',
    'a', 'b', 'c', 'd', 'e', 'f' }; // 字典
    try {
    byte[] strTemp = s.getBytes(); // 获取字节
    MessageDigest mdTemp = MessageDigest.getInstance("MD1"); // 获取MD1
    mdTemp.update(strTemp); // 更新数据
    byte[] md = mdTemp.digest(); // 加密
    int j = md.length; // 加密后的长度
    char str[] = new char[j * 2]; // 新字符串数组
    int k = 0; // 计数器k
    for (int i = 0; i < j; i++) { // 循环输出
    byte byte0 = md[i];
    str[k++] = hexDigits[byte0 >>> 4 & 0xf];
    str[k++] = hexDigits[byte0 & 0xf];
    }
    return new String(str); // 加密后字符串
    } catch (Exception e) {return null; }
    }
    %>
    <%
    request.setCharacterEncoding("UTF-8"); // 设置request编码
    response.setCharacterEncoding("UTF-8"); // 设置response编码

    String action = request.getParameter("action"); // 获取action参数

    if("login".equals(action)){ // 如果为login动作
    String account = request.getParameter("account");
    // 获取account参数
    String password = request.getParameter("password");
    // 获取password参数
    int timeout = new Integer(request.getParameter("timeout"));
    // 获取timeout参数

    String ssid = calcMD1(account + KEY); // 把账号、密钥使用MD1加密后保存

    Cookie accountCookie = new Cookie("account", account);
    // 新建Cookie
    accountCookie.setMaxAge(timeout); // 设置有效期

    Cookie ssidCookie = new Cookie("ssid", ssid); // 新建Cookie
    ssidCookie.setMaxAge(timeout); // 设置有效期

    response.addCookie(accountCookie); // 输出到客户端
    response.addCookie(ssidCookie); // 输出到客户端

    // 重新请求本页面,参数中带有时间戳,禁止浏览器缓存页面内容
    response.sendRedirect(request.getRequestURI() + "?" + System.
    currentTimeMillis());
    return;
    }
    else if("logout".equals(action)){ // 如果为logout动作

    Cookie accountCookie = new Cookie("account", "");
    // 新建Cookie,内容为空
    accountCookie.setMaxAge(0); // 设置有效期为0,删除

    Cookie ssidCookie = new Cookie("ssid", ""); // 新建Cookie,内容为空
    ssidCookie.setMaxAge(0); // 设置有效期为0,删除
    response.addCookie(accountCookie); // 输出到客户端
    response.addCookie(ssidCookie); // 输出到客户端
    //重新请求本页面,参数中带有时间戳,禁止浏览器缓存页面内容
    response.sendRedirect(request.getRequestURI() + "?" + System.
    currentTimeMillis());
    return;
    }
    boolean login = false; // 是否登录
    String account = null; // 账号
    String ssid = null; // SSID标识

    if(request.getCookies() != null){ // 如果Cookie不为空
    for(Cookie cookie : request.getCookies()){ // 遍历Cookie
    if(cookie.getName().equals("account")) // 如果Cookie名为
    account
    account = cookie.getValue(); // 保存account内容
    if(cookie.getName().equals("ssid")) // 如果为SSID
    ssid = cookie.getValue(); // 保存SSID内容
    }
    }
    if(account != null && ssid != null){ // 如果account、SSID都不为空
    login = ssid.equals(calcMD1(account + KEY));
    // 如果加密规则正确, 则视为已经登录
    }
    %>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <legend><%= login ? "欢迎您回来" : "请先登录" %></legend>
    <% if(login){ %>
    欢迎您, ${ cookie.account.value }. &nbsp;&nbsp;
    <a href="${ pageContext.request.requestURI }?action=logout">
    注销</a>
    <% } else { %>
    <form action="${ pageContext.request.requestURI }?action=login"
    method="post">
    <table>
    <tr><td>账号: </td>
    <td><input type="text" name="account" style="width:
    200px; "></td>
    </tr>
    <tr><td>密码: </td>
    <td><input type="password" name="password"></td>
    </tr>
    <tr>
    <td>有效期: </td>
    <td><input type="radio" name="timeout" value="-1"
    checked> 关闭浏览器即失效 <br/> <input type="radio"
    name="timeout" value="<%= 30 * 24 * 60 * 60 %>"> 30天
    内有效 <br/> <input type="radio" name="timeout" value=
    "<%= Integer.MAX_VALUE %>"> 永久有效 <br/> </td> </tr>
    <tr><td></td>
    <td><input type="submit" value=" 登 录 " class=
    "button"></td>
    </tr>
    </table>
    </form>
    <% } %>
    登录时可以选择登录信息的有效期:关闭浏览器即失效、30天内有效与永久有效。通过设置Cookie的age属性来实现,注意观察代码。运行效果如图1.7所示。

    图1.7 永久登录
    %提示:该加密机制中最重要的部分为算法与密钥。由于MD1算法的不可逆性,即使用户知道了账号与加密后的字符串,也不可能解密得到密钥。因此,只要保管好密钥与算法,该机制就是安全的。
    1.2 Session机制
    除了使用Cookie,Web应用程序中还经常使用Session来记录客户端状态。Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。
    1.2.1 什么是Session
    Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务 器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可 以了。
    如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。
    1.2.2 实现用户登录
    Session对应的类为javax.servlet.http.HttpSession类。每个来访者对应一个Session对象,所有该客户 的状态信息都保存在这个Session对象里。Session对象是在客户端第一次请求服务器的时候创建的。Session也是一种key-value的 属性对,通过getAttribute(String key)和setAttribute(String key,Object value)方法读写客户状态信息。Servlet里通过request.getSession()方法获取该客户的Session,例如:
    HttpSession session = request.getSession(); // 获取Session对象
    session.setAttribute("loginTime", new Date()); // 设置Session中的属性

    out.println("登录时间为:" + (Date)session.getAttribute("loginTime"));
    // 获取Session属性
    request还可以使用getSession(boolean create)来获取Session。区别是如果该客户的Session不存在,request.getSession()方法会返回null,而getSession(true)会先创建Session再将Session返回。
    Servlet中必须使用request来编程式获取HttpSession对象,而JSP中内置了Session隐藏对象,可以直接使用。如果 使用声明了<%@ page session="false" %>,则Session隐藏对象不可用。下面的例子使用Session记录客户账号信息。源代码如下:
    代码1.9 session.jsp
    <%@ page language="java" pageEncoding="UTF-8"%>
    <jsp:directive.page import="com.helloweenvsfei.sessionWeb.bean.Person"/>
    <jsp:directive.page import="java.text.SimpleDateFormat"/>
    <jsp:directive.page import="java.text.DateFormat"/>
    <jsp:directive.page import="java.util.Date"/>
    <%!
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    // 日期格式化器
    %>
    <%
    response.setCharacterEncoding("UTF-8"); // 设置request编码
    Person[] persons = { // 基础数据,保存三个人的信息
    new Person("Liu Jinghua", "password1", 34, dateFormat.parse
    ("1982-01-01")),
    new Person("Hello Kitty", "hellokitty", 23, dateFormat.parse
    ("1984-02-21")),
    new Person("Garfield", "garfield_pass", 23, dateFormat.parse
    ("1994-09-12")),
    };

    String message = ""; // 要显示的消息

    if(request.getMethod().equals("POST")){ // 如果是POST登录
    for(Person person : persons){ // 遍历基础数据,验证账号、密码
    // 如果 用户名正确 且 密码正确
    if(person.getName().equalsIgnoreCase(request.getParameter
    ("username"))
    && person.getPassword().equals(request.getParameter
    ("password"))){

    // 登录成功,设置将用户的信息以及登录时间保存到 Session
    session.setAttribute("person", person);// 保存登录的Person
    session.setAttribute("loginTime", new Date());
    // 保存登录的时间

    response.sendRedirect(request.getContextPath() + "/
    welcome.jsp");
    return;
    }
    }
    message = "用户名密码不匹配,登录失败。"; // 登录失败
    }
    %>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
    // ... HTML代码为一个FORM表单,代码略,请看随书光盘
    </html>
    登录界面验证用户登录信息,如果登录正确,就把用户信息以及登录时间保存进Session,然后转到欢迎页面welcome.jsp。welcome.jsp中从Session中获取信息,并将用户资料显示出来。welcome.jsp代码如下:
    代码1.10 welcome.jsp
    <%@ page language="java" pageEncoding="UTF-8"%>
    <jsp:directive.page import="com.helloweenvsfei.sessionWeb.bean.Person"/>
    <jsp:directive.page import="java.text.SimpleDateFormat"/>
    <jsp:directive.page import="java.text.DateFormat"/>
    <jsp:directive.page import="java.util.Date"/>
    <%!
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    // 日期格式化器
    %>
    <%
    Person person = (Person)session.getAttribute("person");
    // 获取登录的person
    Date loginTime = (Date)session.getAttribute("loginTime");
    // 获取登录时间
    %>
    // ... 部分HTML代码略
    <table>
    <tr><td>您的姓名: </td>
    <td><%= person.getName() %></td>
    </tr>
    <tr><td>登录时间: </td>
    <td><%= loginTime %></td>
    </tr>
    <tr><td>您的年龄: </td>
    <td><%= person.getAge() %></td>
    </tr>
    <tr><td>您的生日: </td>
    <td><%= dateFormat.format(person.getBirthday()) %></td>
    </tr>
    </table>
    程序运行效果如图1.8所示。

    图1.8 使用Session记录用户信息
    注意程序中Session中直接保存了Person类对象与Date类对象,使用起来要比Cookie方便。
    当多个客户端执行程序时,服务器会保存多个客户端的Session。获取Session的时候也不需要声明获取谁的Session。Session机制决定了当前客户只会获取到自己的Session,而不会获取到别人的Session。各客户的Session也彼此独立,互不可见。
    %提示:Session的使用比Cookie方便,但是过多的Session存储在服务器内存中,会对服务器造成压力。
    1.2.3 Session的生命周期
    Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。
    Session在用户第一次访问服务器的时候自动创建。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强 制生成Session。
    Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session“活跃(active)”了一次。
    1.2.4 Session的有效期
    由于会有越来越多的用户访问服务器,因此Session也会越来越多。为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器,Session就自动失效了。
    Session的超时时间为maxInactiveInterval属性,可以通过对应的getMaxInactiveInterval()获取,通过setMaxInactiveInterval(long interval)修改。
    Session的超时时间也可以在web.xml中修改。另外,通过调用Session的invalidate()方法可以使Session失效。
    1.2.5 Session的常用方法
    Session中包括各种方法,使用起来要比Cookie方便得多。Session的常用方法如表1.2所示。
    表1.2 HttpSession的常用方法
    方 法 名 描 述
    void setAttribute(String attribute, Object value) 设置Session属性。value参数可以为任何Java Object。通常为Java Bean。value信息不宜过大
    String getAttribute(String attribute) 返回Session属性
    Enumeration getAttributeNames() 返回Session中存在的属性名
    void removeAttribute(String attribute) 移除Session属性
    String getId() 返回Session的ID。该ID由服务器自动创建,不会重复
    long getCreationTime() 返回Session的创建日期。返回类型为long,常被转化为Date类型,例如:Date createTime = new Date(session.get CreationTime())
    long getLastAccessedTime() 返回Session的最后活跃时间。返回类型为long
    int getMaxInactiveInterval() 返回Session的超时时间。单位为秒。超过该时间没有访问,服务器认为该Session失效
    void setMaxInactiveInterval(int second) 设置Session的超时时间。单位为秒
    void putValue(String attribute, Object value) 不推荐的方法。已经被setAttribute(String attribute, Object Value)替代
    Object getValue(String attribute) 不被推荐的方法。已经被getAttribute(String attr)替代
    boolean isNew() 返回该Session是否是新创建的
    void invalidate() 使该Session失效
    Tomcat中Session的默认超时时间为20分钟。通过setMaxInactiveInterval(int seconds)修改超时时间。可以修改web.xml改变Session的默认超时时间。例如修改为60分钟:
    <session-config>
    <session-timeout>60</session-timeout> <!-- 单位:分钟 -->
    </session-config>
    %注意:<session-timeout>参数的单位为分钟,而setMaxInactiveInterval(int s)单位为秒。
    1.2.6 Session对浏览器的要求
    虽然Session保存在服务器,对客户端是透明的,它的正常运行仍然需要客户端浏览器的支持。这是因为Session需要使用Cookie作为 识别标志。HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一客户,因此服务器向客户端浏览器发送一个名为JSESSIONID的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session依据该Cookie来识别是否为同一用户。
    该Cookie为服务器自动生成的,它的maxAge属性一般为–1,表示仅当前浏览器内有效,并且各浏览器窗口间不共享,关闭浏览器就会失效。 因此同一机器的两个浏览器窗口访问服务器时,会生成两个不同的Session。但是由浏览器窗口内的链接、脚本等打开的新窗口(也就是说不是双击桌面浏览 器图标等打开的窗口)除外。这类子窗口会共享父窗口的Cookie,因此会共享一个Session。
    %注意:新开的浏览器窗口会生成新的Session,但子窗口除外。子窗口会共用父窗口的Session。例如,在链接上右击,在弹出的快捷菜单中选择“在新窗口中打开”时,子窗口便可以访问父窗口的Session。
    如果客户端浏览器将Cookie功能禁用,或者不支持Cookie怎么办?例如,绝大多数的手机浏览器都不支持Cookie。Java Web提供了另一种解决方案:URL地址重写。
    1.2.7 URL地址重写
    URL地址重写是对客户端不支持Cookie的解决方案。URL地址重写的原理是将该用户Session的id信息重写到URL地址中。服务器能 够解析重写后的URL获取Session的id。这样即使客户端不支持Cookie,也可以使用Session来记录用户状态。HttpServletResponse类提供了encodeURL(String url)实现URL地址重写,例如:
    <td>
    <a href="<%= response.encodeURL("index.jsp?c=1&wd=Java") %>">
    Homepage</a>
    </td>
    该方法会自动判断客户端是否支持Cookie。如果客户端支持Cookie,会将URL原封不动地输出来。如果客户端不支持Cookie,则会将用户Session的id重写到URL中。重写后的输出可能是这样的:
    <td>
    <a href="index.jsp;jsessionid=0CCD096E7F8D97B0BE608AFDC3E1931E?c=
    1&wd=Java">Homepage</a>
    </td>
    即在文件名的后面,在URL参数的前面添加了字符串“;jsessionid=XXX”。其中XXX为Session的id。分析一下可以知道, 增添的jsessionid字符串既不会影响请求的文件名,也不会影响提交的地址栏参数。用户单击这个链接的时候会把Session的id通过URL提交 到服务器上,服务器通过解析URL地址获得Session的id。
    如果是页面重定向(Redirection),URL地址重写可以这样写:
    <%
    if(“administrator”.equals(userName)){
    response.sendRedirect(response.encodeRedirectURL(“administrator.jsp”));
    return;
    }
    %>
    效果跟response.encodeURL(String url)是一样的:如果客户端支持Cookie,生成原URL地址,如果不支持Cookie,传回重写后的带有jsessionid字符串的地址。
    对于WAP程序,由于大部分的手机浏览器都不支持Cookie,WAP程序都会采用URL地址重写来跟踪用户会话。比如用友集团的移动商街等。
    %注意:TOMCAT判断客户端浏览器是否支持Cookie的依据是请求中是否含有Cookie。尽管客户端可能会支持Cookie,但是由于第 一次请求时不会携带任何Cookie(因为并无任何Cookie可以携带),URL地址重写后的地址中仍然会带有jsessionid。当第二次访问时服 务器已经在浏览器中写入Cookie了,因此URL地址重写后的地址中就不会带有jsessionid了。
    1.2.8 Session中禁止使用Cookie
    既然WAP上大部分的客户浏览器都不支持Cookie,索性禁止Session使用Cookie,统一使用URL地址重写会更好一些。Java Web规范支持通过配置的方式禁用Cookie。下面举例说一下怎样通过配置禁止使用Cookie。
    打开项目sessionWeb的WebRoot目录下的META-INF文件夹(跟WEB-INF文件夹同级,如果没有则创建),打开context.xml(如果没有则创建),编辑内容如下:
    代码1.11 /META-INF/context.xml
    <?xml version='1.0' encoding='UTF-8'?>
    <Context path="/sessionWeb" cookies="false">
    </Context>
    或者修改Tomcat全局的conf/context.xml,修改内容如下:
    代码1.12 context.xml
    <!-- The contents of this file will be loaded for each web application -->
    <Context cookies="false">
    <!-- ... 中间代码略 -->
    </Context>
    部署后TOMCAT便不会自动生成名JSESSIONID的Cookie,Session也不会以Cookie为识别标志,而仅仅以重写后的URL地址为识别标志了。
    %注意:该配置只是禁止Session使用Cookie作为识别标志,并不能阻止其他的Cookie读写。也就是说服务器不会自动维护名为JSESSIONID的Cookie了,但是程序中仍然可以读写其他的Cookie。


    转载源:http://www.cnblogs.com/weipeng/archive/2012/04/19/2457206.html

    展开全文
  • This news item was posted in PHP Code, PHP Framwork category and has 3 Comments so far. ... Warning: session_start() [function.session-start]: Cannot send session cookie - headers alre...
  • YII 框架 第七天(4) session cookie

    千次阅读 2013-09-25 21:24:32
    SESSION COOKIE
  • Warning: session_start() [function.session-... send session cookie - headers already sent by 配置php网站的时候,经常会在页首出现Warning: session_start() [function.session-start]: Cannot send s
  • 摘要:前言在实际开发中,我们经常会遇到这样的场景:app中打开某个H5页面时,H5经常需要知道你的sessioncookie来判断登录状态或者要求你告知sessioncookie。本文将给出个在实际开发中遇到的解决方案。喜欢探讨Android...
  • 不需要排查代码解决session_start前面不允许输出的问题 ...调用session_start()时抛出了Cannot send session cookie – headers already sent 的错误 寻找解决方案 在网上搜搜搜了一大堆,都是老生常谈的在se...
  • From:http://www.php-security.org/MOPB/PMOPB-46-2007.html SummaryPHPs ext/session does not URL encode the session id before placing it into the session cookie. Therefore characters with special
  • 每次服务器放松给访客的session的值是否是保存在cookie中的? 如果是 那么假如我通过js将cookie中的某个session值重置 是否会导致session的值发生变化?
  • Warning: session_start(): Cannot sendsession cookie - headers already sent by (output started at D:\Apache\Apache24\htdocs\www\index.php:5)in D:\Apache\Apache24\htdocs\www\inc\conn.php on line 
  • 主要参考以下几篇文章的理解。 http://sumongh.iteye.com/blog/82498 理解cookiesession机制 ...总结:Sessioncookie不仅仅是两种并列的http传送信息的方式,也不是ses
  • Asp.net Session Cookie机制

    千次阅读 2015-08-18 12:18:41
    常用的会话跟踪技术是CookieSessionCookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。 本章将系统地讲述CookieSession机制,并比较说明什么时候不能用Cookie,什么...
  • Warning Cannot send session cookie - headers already sent...问题的解决(PHP的UTF-8 BOM引起的问题) 习惯了用edit plus进行php编程,所以有时会出现一些不为人知的错误,很麻烦; 近日,在开发项目时,某些页面...
  • session cookie总结

    2013-05-31 23:35:33
    1. 使用cookie的注意事项有哪些? 1) setcookie()之前不可以有任何页面输出,就是空格,空白行也不可以; 2) setcookie()后,在当前页面调用$_COOKIE['cookiename']不会有输出,必须刷新或到下一个页面才可以...
  • IE8 的session cookie共享问题

    千次阅读 2012-03-16 12:14:28
    众所周知Session cookie被广泛用来做浏览器用户身份校验。在近期的项目测试中就遇到一个相关的问题,发现在两不同的IE8窗口里面并以两个不同用户身份分别登录后,前面登录的用户页面会在第二个用户登录以后刷新成第...
  • django session cookie配置

    千次阅读 2013-06-27 14:32:58
    SESSION_ENGINE="django.contrib.sessions.backends.file" #session后端存储方式(可以是数据库、缓存、文件) SESSION_FILE_PATH="/opt/dev/workspace/test/python/blog/var/...SESSION_COOKIE_NAME="yhhdsid_
  • 最近的项目要求指定的数据库只能是SQLServer,后端开发采用了SpringBoot,所以SpringBoot自带的利用Redis的Session管理机制就用不了,查找了很多的资料后,找到了利用SQLServer管理Session的解决办法。 首先,需要...
  • JSP Session cookie 详解

    千次阅读 2014-06-25 23:22:39
    一、JSP和Servlet中的Cookie   由于HTTP协议是无状态协议(虽然Socket连接是有状态的,但每次用HTTP协议进行数据传输后就关闭的Socket连接,因此,HTTP协议并不会保存上一...这就是下面要讲的SessionCookie
  • 在php端开启一个session时,有时候会出现很诡异的warning,Warning Cannot send session cookie - headers already sent。这个问题是因为在php session_start()之前已经有输出了。这样会导致session_start()调用失败...
  • session cookie application的区别

    千次阅读 2016-05-06 15:38:49
    方法 ...Session 小量,简单的数据 用户活动时间+一段延迟时间(一般 为20分钟) 单个用户 服务器端 Cookie 小量,简单的数据 可以根据需要设定 单个用户 客户端 1.
  • ini_set('session.cookie_path', '/'); ini_set('session.cookie_domain', '.test.com'); ini_set('session.cookie_lifetime', '1800'); if($_SERVER['HTTP_HOST']=='test.com'){ header('HTTP/1.1 301 Moved Perman
  • Session Cookie的HttpOnly和secure属性

    千次阅读 2018-11-05 17:36:14
    一、属性说明: 1 secure属性 当设置为true时,表示创建的 Cookie 会被以安全的形式向服务器传输,也...如果在Cookie中设置了&quot;HttpOnly&quot;属性,那么通过程序(JS脚本、Applet等)将无法读取到Co...
  • 破解Session cookie的方法

    千次阅读 2004-11-10 22:42:00
    所謂的 session cookie, 就是站台在你登錄成功後,送上一個 cookie,表示你已經通過驗證,但與一般cookie不同的是,他並不會存在你的硬碟上,也就是說:在你離開瀏覽器之後,就會消失,也就是意味:下次你重開瀏覽器...
  • PHP 设置 Session Cookie 过期

    千次阅读 2013-11-04 16:01:56
    /*  * 注意他们之间的前后顺序  */ session_set_cookie_params(5); //设置cookies 过期 session_start(); //开启session 存储 session_destroy();
  • 禁用WebSphere的session cookie跟踪功能

    千次阅读 2007-04-19 13:42:00
    禁用WebSphere的session cookie跟踪功能为了提高服务器性能,减少内存消耗。cas系统的服务端要求不产生session,配置方法是:1.应用程序->企业应用程序2.点击名称列中的cas应用3.选择“其他属性”中的“会话管理”4....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 55,329
精华内容 22,131
关键字:

sessioncookie