zoukankan      html  css  js  c++  java
  • 会话技术之Cookie详解

      很早之前写过一篇关于Cookie和Session的文章,那是2017年的事咯,当时还是个学生,技术也菜,对知识理解的也不深。恰巧有机会重新学习Java Web,今天就再次来简单的聊一聊Cookie与Session。

      1.会话与会话技术

      ​在日常生活中,我们拨打电话接通后到挂断前,在这期间两人的交流就是一个会话。Web应用中的会话类似生活中的打电话,用户登录(拨号)、一系列的请求和响应(交流)、用户退出登录(挂断电话)。

      ​我们在电话刚接通时(此手机无来电显示功能),首先会来一句,你好,我是XXX。两人互通暗号之后,确认了双方的身份,之后的交流也都是基于前面的信息来进行交流的,直到两人都完成了此次会晤的目的,挂断电话,此次会话结束。

      ​但是我们知道,客户端和浏览器端通信使用的Http协议是无状态的,即每次请求对于服务端来讲都是一个新的请求,无法基于前面的信息进行交流,所以想象下,客户端和服务端两方的交流情形(每到第四步的时候就会拐回第一步)。

      ​基于上面的两个对比,我们也可以知道,Web应用需要一种可以保持前面信息(之前的对话)的技术,这就是会话技术。客户端和服务器端每次的交流都可以被追踪,类似于电话里两人的交流,可以记住前面所说的话。

      2.原有技术的不足

      ​在我们之前学习的过程中,我们知道Java Web应用中是有域对象的,如HttpServletRequest、ServletContext,下面我们来下看这个两个域对象能否保存会话信息。

      HttpServletRequest:每次Http请求,Servlet容器就会创建一个ServletRequest对象,该对象中保存着此次请求的所传递的数据,并且还可以通过其绑定属性,来传递一些数据。但是,每次请求都会创建一个全新的ServletRequest,其生命周期为当前Http请求,如果会话过程中调用不同的接口,两次请求无法通过ServletRequest来共享信息。

      ServletContext:HttpServletRequest存在的问题,对于ServletContext来说似乎不是问题,因为ServletContext在整个应用中是全局共享的,因此调用不同的接口,是可以通过其来追踪之前的信息的。但是,如果如果多个用户同时来,似乎是个比较麻烦的事情。

      ​基于ServletContext遇到的多用户问题,如果你用过redis做验证码或者其他缓存的话,其实多用户对你来说并不是问题,我们可以在需要保存的信息对应的key前增加唯一表示,比如用户的账号,形成一个如lizishu-code的key,这样即可解决多用户的问题。

      ​当时这样来做,我们需要每次请求是都需要携带我们的账号信息,存在的问题主要有如下几点:

      安全问题,如果只验证账号名,很容易被攻击,因此同时需要携带密码;

      客户端如何保持账号信息,总不能从登录页开始,每个页面间的跳转都携带着账号和密码;

      效率低,每次请求服务器都会默认来一遍身份验证;

      ​原有技术无法有效的解决问题怎么办?OMG,当时是提出一个新技术了,也就是我们要讲的两位主角,Cookie和Session。Cookie用来解决客户端如何保存信息的问题,Session来解决多用户问题,即每个客户端会对应一个session,当前会话产生的信息可以保存在session中,使用Cookie和Session的关联性来解决安全问题。下面我们具体的来讲解。

      3.Cookie的概念

      ​Cookie是会话技术的一种,主要用于将会话过程产生的数据保存到客户端(浏览器),从而使客户端每次和服务端之间可以更好的进行交互。

      ​我们通过一个生活中的例子来理解下Cookie的概念。在生活中,大家应该都有接触过会员卡,比如理发店、超市等等。我们办理会员卡后,店家会给我们一张卡片,以后每次来消费时,只需出示这张卡片,就可以获取到你的余额、积分、消费记录等信息,然后基于上面的信息来进行折扣的计算、积分的累加,余额的扣减等操作。

      ​在Web应用中,Cookie的功能就类似于上面的例子中的会员卡,第一次请求时,会创建一个Cookie,当用户再次访问服务器时,就会携带上Cookie(会员卡),服务端也会根据处理结果,将一些信息放到Cookie中,以保存在客户端。

      ​上图描述了Cookie在浏览器和服务器之间的传输过程。当用户第一次访问服务器时,服务器可以在响应信息(response)中增加Set-Cookie响应头,将信息以Cookie为载体发送给浏览器。浏览器接收到服务器发送来的Cookie信息,就会将他保存在浏览器的缓冲区内。这样,当浏览器再次访问服务器时,就会将Cookie放在请求消息中,Web服务器就可以通过request中的用户信息来分辨此次请求是由哪个用户发起的。

      ​从上面我们可以发现,服务器端可以通过HttpServletRsponse来向客户端发送Cookie,那浏览器只能缓存服务器端发来的Cookie么?答案当时是NO,浏览器也可以通过JS来创建Cookie对象,并将其存储。下面我们分别来介绍这两种方式。

      4.服务器端的Cookie

      ​为了封装Cookie信息,Tomcat在Servlet Api中提供了一个Cookie类,其中提供了许多方法,让我们可以方便快捷的创建Cookie和设置其属性。

      ​首先我们来看下Cookie的构造方法,Cookie类只提供了一个构造方法,其源码如下所示:

      public Cookie(String name, String value) {

      if (name == null || name.length() == 0) {

      throw new IllegalArgumentException(

      lStrings.getString("err.cookie_name_blank"));

      }

      if (!isToken(name) ||

      name.equalsIgnoreCase("Comment") || // rfc2019

      name.equalsIgnoreCase("Discard") || // 2019++

      name.equalsIgnoreCase("Domain") ||

      name.equalsIgnoreCase("Expires") || // (old cookies)

      name.equalsIgnoreCase("Max-Age") || // rfc2019

      name.equalsIgnoreCase("Path") ||

      name.equalsIgnoreCase("Secure") ||

      name.equalsIgnoreCase("Version") ||

      name.startsWith("$")) {

      String errMsg = lStrings.getString("err.cookie_name_is_token");

      Object[] errArgs = new Object[1];

      errArgs[0] = name;

      errMsg = MessageFormat.format(errMsg, errArgs);

      throw new IllegalArgumentException(errMsg);

      }

      this.name = name;

      this.value = value;

      }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      ​从上面我们可以看到,创建一个Cookie对象需要传入两个参数name,value,也是我们比较熟悉的Key-Value模式,并且name不能为null或空字符串,不可等于保留的token名。需要注意的是,Cookie对象一旦创建,其name属性就不可更改了,value可以被修改。

      ​下面我们来看下Cookie类提供的设置其属性的方法:

      ​下面我们对其几个重要的属性进行讲解:

      maxAge:表示此Cookie在客户端的有效期,单位是秒。默认值为-1,当整个浏览器关闭后,此Cookie失效;当maxAge值为正数时,即此Cookie还剩余多少秒的有效期;当值为0时,此Cookie失效,浏览器进行删除操作;

      path:表示此Cookie对path下的所属的目录和子目录有效,比如path设置为/rest,放客户端发起/rest/aaaServlet请求时,就会携带上此Cookie;如果想让此Cookie对站点所有的目录有效的话,可以设置为/;

      domain:表示可以访问此Cookie的域名,如果不设置的话,会根据当前请求url来设置;当我们手动设置时,可以采用以".“开头来定义此Cookie更大的域名访问范围,比如设置domain为”.csdn.net",则当访问"https://lizishudd.blog.csdn.net/“时,一样可以使用此”.csdn.net"下的cookie(当然这里还需要Cookie的path同时满足要求);

      ​下面我们来简单的演示下服务器端是如何向客户端(浏览器)发送Cookie对象的,我们新建一个CookieServlet,urlPattern默认,其中的doGet方法如下:

      protected void doGet(HttpServletRequest request, HttpServletResponse response)

      throws ServletException, IOException {

      // 设置编码方式

      request.setCharacterEncoding("utf-8");

      response.setContentType("text/html;charset=utf-8");

      Cookie cookie = new Cookie("name", "lizishu");

      //xx.localhost域名下的所有应用都可以使用此Cookie

      cookie.setPath("/");

      cookie.setDomain(".localhost");

      //有效期5min

      cookie.setMaxAge(300);

      response.addCookie(cookie);

      }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      ​浏览器中输入URL:http://localhost:8080/FirstProject/CookieServlet,访问此Servelt后,我们打开Chrome的控制态F12->Application->Storage->Cookies->http://localhost:8080(根据你输入的url来定),截图如下:

      ​在浏览器中存在Cookie后,我们随便在浏览器中在进行一次请求,http://localhost:8080/FirstProject/xxxx,我们来看下其请求头,因为xxxx没有对应的目录,因此404,不过这个不是重点,重点是,此次请求中Request Headers中已经携带了浏览器中存储的Cookie。

      ​在服务器端如何获取Cookie,可参考上一篇博文:https://blog.csdn.net/qq_34666857/article/details/104677407

      5.浏览器端的Cookie

      ​Cookie的创建工作除了在服务器端,还可以在浏览器端通过JavaScript完成。下面我们来看下如何使用JavaScript来来创建Cookie和修改Cookie(这里仅仅是为了演示,因此使用原生的Js,Jquery等其他JS框架都有对Cookie的支持)。

      ​首先我们来看下JavaScript是如何操作Cookie的:

      //创建一个Cookie,属性默认

      document.cookie="password=123456";

      //创建一个Cookie,设置属性:过期时间,path

      document.cookie="attribute=pathDomain; expires=Thu, 14 Dec 2021 12:00:00 GMT; path=/";

      //读取Cookie,返回name1=value1;...;namen=valuen 形式的字符串

      document.cookie;

      //修改Cookie,重新创建一遍,name相同会覆盖之前Cookie,修改了过期时间

      document.cookie="attribute=pathDomain; expires=Thu, 14 Dec 2020 12:00:00 GMT; path=/";

      //删除Cookie,可以指定过期时间为当前时间;注意:因为过期时间以浏览器的服务器时间为准,一般会有八小时时差

      document.cookie="password=123; expires=" + new Date(  郑州妇科医院哪里好:http://www.zztjfkyy.com/郑州妇科医院哪家好:http://www.zztjfkyy.com/郑州妇科医院排名:http://www.zztjfkyy.com/);

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      ​上面即是我们的原生JS操作Cookie的示例代码,我们将其在Chrome的控制台中运行,执行过程如下图所示:

      ​我们来看下Application中的Cookie信息。

      ​下面提供两个简单的Cookie操作的JS函数:

      //创建Cookie,并设置有效期(单位天)

      function setCookie(cname,cvalue,exdays)

      {

      var d = new Date();

      d.setTime(d.getTime()+(exdays*24*60*60*1000));

      var expires = "expires=" + d.toGMTString();

      document.cookie = cname + "=" + cvalue + "; " + expires;

      }

      //获取对应Cookie的值,通过字符串截取的方式

      function getCookie(cname)

      {

      var name = cname + "=";

      var ca = document.cookie.split(';');

      for(var i=0; i<ca.length; p="" i++)<="">

      {

      var c = ca[i].trim();

      if (c.indexOf(name)==0) return c.substring(name.length,c.length);

      }

      return "";

      }

      //删除Cookie,过期时间提前1天,解决时差问题

      function delCookie(cname)

      {

      var d = new Date();

      d.setTime(d.getTime()-(24*60*60*1000));

      var expires = "expires=" + d.toGMTString();

      document.cookie = cname + "=; " + expires;

      }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      6.总结

      ​因为篇幅问题,本文只对Cookie进行讨论,下文会对Session进行详细的讨论。Cookie的出现让浏览器保存会话信息变得非常方便,而浏览器发起Http请求时,会将所有当前请求可用的Cookie全部带上,也大大的方便了程序员的开发工作。

      ————————————————

  • 相关阅读:
    如何实现parseFloat保留小数点后2位
    C#正则表达式整理备忘
    HRESULT:0x80070057 (E_INVALIDARG)的异常的解决方案
    c# using的几种用法
    QQ截图 有快捷键的,如Shift+S
    史上最深刻的黄段子
    文本框回车自动提交
    C#与ASP.NET中DateTime.Now函数详解
    ASP.NET页面生命周期
    .NET中HttpWebRequest详解
  • 原文地址:https://www.cnblogs.com/sushine1/p/12752505.html
Copyright © 2011-2022 走看看