会话跟踪

什么是会话?

可简单理解为,用户打开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭服务器,整个过程称为一个会话。从特定客户端到服务器的一系列请求称为会话。记录会话信息的技术称为会话跟踪。

理论上一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作属于另一个会话,二者不能混淆。例如用户A在任何时候购买的任何商品都应该放在A的购物车里,而不能放在B或C的购物车里,这不属于同一个会话。

会话跟踪技术

会话跟踪是Web程序中常用的技术,用来跟踪用户的整个会话,主要有四种会话跟踪方法,设置隐藏表单字段,URL重写,Cookie和Session。常用的会话跟踪技术是Cookie和Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。

为什么需要会话跟踪技术?

HTTP是一种“无状态”的协议,每次客户端发起请求时,每次的连接都是独立的,服务器不会保存之前客户端请求的任何记录,也就是比鱼的记忆还差,服务器每次响应完后就会忘了和哪个客户端通信过,而且通信的内容也会全部忘记。举个不恰当的例子,你朋友找你借钱,你借给他500块,一借完,你就忘了借给谁了,而且借的数额也忘了......tan90度,服务器无法知道下一次来访问的还是不是上次访问的用户,为了保持访问用户与服务器的交互状态,那么服务器需要使用会话跟踪技术,来跟踪用户的整个会话,记录客户的状态,弥补HTTP协议的不足。

Cookie和Session

在这里引用一个非常形象的例子来阐述Cookie和Session的联系和区别:

假如一个咖啡店有喝5杯咖啡免费送一杯咖啡的优惠,但是一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:

  1. 该店的店员很厉害,能够记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。但是HTTP协议本身是无状态的。
  2. 发给顾客一张卡片,上面有唯一的卡号,消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态,也就是Cookie。
  3. 发给顾客一张会员卡,除了唯一的卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态,也就是Session。

理解Cookie

Cookie实际上是存放在客户端浏览器的一小块文本。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来记录用户状态。

Cookie是如何工作的?

img

上面的例子中,顾客第一次来的时候,店家会发给顾客一张记录顾客信息的卡片,顾客下次去咖啡店的时候会带上这张卡片,依靠这张卡片就可以做到识别客户,记录用户状态。Cookie的工作原理也是如此,客户端浏览器第一次请求服务器后,服务器会颁发一个Cookie(name = JSESSIONID,该Cookie就相当于那唯一的卡号)响应给客户端浏览器,以后客户端的每次请求都会带上这个Cookie,这样服务器就可以根据这唯一的Cookie识别不同的用户。

浏览器应该支持每台Web服务器有20个Cookie,总共有300个Cookie,并且可能将每个Cookie的大小限定为 4 KB。

Cookie的创建,获取,修改与删除

  1. Cookie的创建

    创建Cookie对象,利用Cookie的构造函数来创建Cookie,Cookie的构造函数有两个参数,name和value,必须要有这两个参数,value固定为String类型的。

    Cookie是存放在浏览器端的,所以还需要把Cookie从servlet里发送到浏览器端,利用response里的addCookie方法可以做到,原理是通过在响应中设置set-Cookie标头,以Key/Value键值对的形式发送到浏览器,在这其中还有很多细节需要了解,比如Cookie如何加到HTTP的Header中?

    Cookie cookie = new Cookie("username", "kindleheart");
    response.addCookie(cookie);
    

    你可以使用google浏览器查看响应头中的set-Cookie标头,你能看到你刚刚添加的Cookie和它的属性值

    img

    响应首部中黄色的部分就是从Servlet发送到浏览器的Cookie,请求首部中红色划线部分是浏览器发回到服务器的Cookie,没有你刚创建的Cookie,因为你创建的Cookie刚发送到浏览器,在你的请求之后,你再请求一次就可以在请求首部看到你刚创建的Cookie。

    img

  2. Cookie的获取

    在Servlet中只能使用getCookies方法获取所有的Cookie,没有通过Cookie名来获取Cookie的方法。所以需要你自己来编写代码实现。

    	public static Cookie getCookieByName(String name, Cookie[] cookies) {
    		if(cookies!=null){
    			for (Cookie c : cookies) {
    				//通过名称获取
    				if(name.equals(c.getName())){
    					//返回
    					return c;
    				}
    			}
    		}
    		return null;
    	}
    
  3. Cookie的修改与删除  

    Cookie并不提供修改,删除操作。如果要修改某个Cookie,需要新建一个同名的Cookie,只需要新建一个同名的Cookie,并添加到response中覆盖原来的Cookie。下面的两行代码就可把username的value值 "kindleheart"改为"Hush"。

    Cookie cookie = new Cookie("username", "Hush");
    cookie.setPath("/Demo");
    response.addCookie(cookie);
    

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

    Cookie cookie = new Cookie("username", "");
    cookie.setMaxAge(0);
    response.addCookie(cookie);
    

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

Cookie的属性

除了name与value之外,Cookie还有其它的一些可选属性,比如注释、路径和域限定符、最大生存时间和版本号。每个属性对应着一个get方法和一个set方法。

一些Web浏览器在处理可选属性方面存在bug,因此有节制地使用这些属性可提高Servlet的互操作性。

    img

  1. Cookie的有效期

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

    • maxAge > 0:表示Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。
    • maxAge < 0,则表示该Cookie仅仅在关闭窗口前有效。maxAge为负数的Cookie,为临时Cookie,不会被持久化,不会被写到Cookie文件中,而是保存在浏览器内存中,因此关闭浏览器该Cookie就消失了。
    • maxAge = 0,有效时间为0,就表示为删除Cookie。Cookie机制没有提供删除Cookie的方法,因此通过设置Cookie即时失效实现删除Cookie的效果。失效的Cookie会被浏览器从Cookie文件或者内存中删除。
  2. Cookie的域名

    Cookie是不可跨域名的,比如在www.google.com颁发的Cookie不会被提交到www.baidu.com中去,同一个一级域名下的两个二级域名如www.kindleheart.com和images.kindleheart.com也不能互相使用Cookie,因为二者的域名并不严格相同。如果想所有kindleheart.com名下的二级域名都可以使用该Cookie,可以设置domain参数。

    Cookie cookie = new Cookie("username", "kindleheart");
    cookie.setDomain(".kindleheart.com");
    response.addCookie(cookie);
    
  3. Cookie的路径

    domain属性决定访问Cookie的域名,而path属性决定允许访问Cookie的路径(ContextPath)。如果只允许Demo工程下的程序使用Cookie,可以这么写:

    Cookie cookie = new Cookie("username", "kindleheart");
    cookie.setPath("/Demo");
    response.addCookie(cookie);
    

Session

理解Session

前面已经介绍了Cookie可以让服务器跟踪每个客户端的访问,Cookie存放在客户端浏览器,但是每次客户端的访问都必须传回这些Cookie,如果Cookie很多,那么无形地会增加了客户端与服务器端的数据传输量,而Session正是解决这个问题的。

Session存放在服务器端,同一个客户每次和服务器端进行交互时,不需要每次传回所有的Cookie值,而是只要传回一个ID,这个ID是客户端第一次访问时生成的,而且每个客户端都是唯一的,这样每个客户端都有了一个唯一的ID,客户端只需要传回这个ID就行了,这个ID通常是name为JSESSIONID的一个Cookie。

Session是如何工作的

实际上有以下三种方式使得Session正常工作。

  • 基于Cookie,如果没有修改Context容器的Cookies标识,则默认也是支持的。客户端每次请求的时候,Cookie会被返回到服务器,利用请求头中的Cookie标头。      img

  • 基于URL Path Parameter(URL重写),默认支持。

    浏览器不支持Cookie或者用户把浏览器的Cookie功能关闭了,浏览器就会把该用户的Session的ID信息(JSESSIONID)重写到用户请求的URL参数中,服务器再从URL参数中解析出Session的ID。

    首先我们把浏览器的Cookie关闭,再使用HttpServletRequest类提供的encodeURL(String url)实现地址重写,下面是一个计录浏览次数的小例子:

     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html;charset=utf-8");
            HttpSession session = request.getSession();
            int count = 0;
            if(session.getAttribute("count") != null) {
                int c = (int) session.getAttribute("count");
                count = c + 1;
            }
            session.setAttribute("count", count);
            PrintWriter out = response.getWriter();
            out.println("<html>");
            out.println("<body>");
            out.println("<h1>登入" + count + "次</h1>");
            //URL重写把JSESSIONID发送到服务器
            out.println("<a href='" + response.encodeURL("IndexServlet") + "'>click me</a>");
            out.println("</body>");
            out.println("</html>");
            out.close();
        }
    

    在浏览器的地址栏URL里文件名后面URL参数前面可以看到 jsessionid = XXX。      img

    注意:如果浏览器支持Cookie,那么Tomcat仍然会解析Cookie里的中的Session ID,并会覆盖URL中的Session ID,也就是你在URL就中看不到 jsessionid = XXX了。

  • 基于SSL,默认不支持,只有connector.getAttribute("SSLEnabled")为TRUE时才支持。

    这一种方式通过javax.servlet.request.ssl_session属性值设置Session ID。

Session的生命周期

  1. Session的创建

    Session在用户第一次请求服务器的时候自动创建,只有访问Servlet,JSP等动态资源才会创建,访问HTML,IMAGE等静态资源并不会创建Session,如果没有创建成功,也可以使用request.getSession(true)强制创建Session。

  2. Session的获取

    通过HttpServletRequest对象的getSession方法获取一个HttpSession实例。

    //获取此会话
    HttpSession session = request.getSession();
    
  3. Session的撤销

    Session的撤销有三种可能的情况:

    • Session超时,Session的默认有效期为30分钟,你如果30分钟内没有请求服务器,Session就会撤销,你30分钟内请求了服务器,服务器就认为你active了一次,重新计算有效期。

      可以在web.xml文件里或者使用setMaxInactiveInterval(20 * 60)方法设置Session的有效期,注意的是web.xml文件里参数以分为单位,15分钟,setMaxInactiveInterval(20 * 60)中的参数以秒为单位,这里是20分钟。

      <web-app>
        <display-name>Archetype Created Web Application</display-name>
        <session-config>
          <session-timeout>15</session-timeout>
        </session-config>
      </web-app>
      
      //设置Session的有效期为20分钟
      session.setMaxInactiveInterval(20 * 60);
      
    • 通过会话对象使用invalidate方法使Session无效。

      //使此会话无效
      session.invalidate();
      
    • 应用结束。

Session的属性操作

  1. Session增加属性

    Session中的属性也是以键值对的形式存储的,用setAttibute(name, value)方法添加属性,value是Object对象的,所以value不限于String类型,可以是任何数据类型。

    //添加一个name为count的属性,值为250
    int count = 250;
    session.setAttribute("count", count);
    
  2. Session获取属性值

    //获取name为count的value值
    int count = (int) session.getAttribute("count");
    
  3. Session修改属性

    Session修改属性,直接使用setAttibute(name, value)方法覆盖相同name的Session即可。

    //把name为count的属性值由250修改到520
    session.setAttribute("count", 250);
    session.setAttribute("count", 520);
    
  4. Session删除属性

    Session删除属性,使用removeAttribute(name)方法,删除对应name的属性。

    //删除name为count的属性
    session.removeAttribute("count");
    

Cookie与Session的比较

Cookie与Session都是为了保持用户访问的连续状态,之所以为了要保持这种状态,一方面是为了实现业务方便,另一方面就是简化服务器端的程序设计,提高访问性能。但是两者的实现原理不太一样,各自都有优点和缺陷,下面通过比较说明这两者的特点和适用场合。

  1. 存取方式上

    Cookie中只能保持ASCLL字符串,如果存取Unicode字符或者二进制数据,需要进行UTF-8,GBK,或者BASE64等方式的编码,而Session中可以存取任何类型的数据。

  2. 隐私安全上

    Cookie存放在客户端浏览器,对客户端是可见的,客户端的一些程序可能会窥探复制甚至修改Cookie中的内容。而Session存放在服务器端,对用户是透明的,不存在敏感信息泄露的危险。

  3. 有效期上

    Cookie可以设置长期有效,浏览器关闭也有效,虽然Session可以设置很长的有效期,但是Session依赖名为JSESSIONID的Cookie,该Cookie的maxAge默认为-1,浏览器关闭Cookie就失效,所以该Session也就失效了。

  4. 服务器的负担上

    Session存放在服务器端,每个用户都会产生一个Session。如果并发非常大的网站,会产生大量的Session,消耗大量内存,因此像Baidu,Google这样并发量极高的网站是不会使用Session来追踪会话的,而Cookie保存在客户端,不占用服务器资源,对于并发量极高的网站Cookie是更好的选择。

  5. 从浏览器支持上

    Cookie是需要浏览器支持的,如果浏览器不支持Cookie,就需要使用Session以及URL地址重写。

  6. 从跨域名上

    Cookie支持跨域名访问,只要设置domain属性即可,但Session不能够跨域名访问,Session仅在他的域名下有效。

posted on 2018-10-12 20:25  kindleheart  阅读(310)  评论(0编辑  收藏  举报