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

    会话技术之Cookie


    文章中所有源代码都在我的这个GitHub的公开库--->servletStar来一个好吗?秋梨膏!


    国际惯例,学什么之前都得 HelloWorld 一下。

    @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //设置编码
            resp.setContentType("text/html; charset=UTF-8");
            //new 一个cookie
            Cookie cookie =new Cookie("username","ling");
            //加入这个 cookie
            resp.addCookie(cookie);
            //输出相关提示
            resp.getWriter().write("Hello World! 写入了一个cookie");
        }
    

    调试模式下,可以看到已经存入自定义的 Cookie 。

    image-20200904094045754

    • Cookie(String name,String value) :构造方法

    • setComment(String purpose) 与 getComment(): 用于注释,如果浏览器向用户展示 Cookie,需要使用到。

    • setValue(String newValue) 与 getValue() ⽅法:为 cookie 赋值或者取值。

    • setMaxAge(int expiry) 与 getMaxAge()⽅法 :设置,获取 Cookie 的有效期。

    • setPath(String uri) 与 getPath() ⽅法:Cookie 的path 属性决定允许访问Cookie 的路径。⼀般地,Cookie 发布出来,整个⽹⻚的资源都可以使⽤。现在我只想某个 Servlet 可以获取到 Cookie,其他的资源不能获取。

    • setDomain(String pattern) 与getDomain() ⽅法 :想要同级的域名可以共享Cookie 需要用到该方法。

    • getName⽅法 :取得该cookie 的 name ,name 在创建后不可以修改。

    • setSecure(boolean flag) :HTTP协议是⽆状态的,还是不安全的!如果不想Cookie在⾮安全协议中传输,设置Cookie的secure属性为true,浏览器只会在 HTTPS 和 SSL 等安全协议中传输该 Cookie。设置secure属性不会将Cookie的内容加密。如果想要保证安全,最好使⽤ MD5 算法加密。

     @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //设置编码
            resp.setContentType("text/html; charset=UTF-8");
            //new 一个cookie
            //这里与此前设值不同需要进行编码处理
            Cookie newCookie =new Cookie("Chinese-Cookie", URLEncoder.encode("中文Cookie","UTF-8"));
            //加入这个 cookie
            resp.addCookie(newCookie);
            //取出该中文 Cookie 自然是需要解码的
            Cookie[] cookies = req.getCookies();
            for (Cookie cookie: cookies
                 ) {
                String name = cookie.getName();
                String values = URLDecoder.decode(cookie.getValue(),"UTF-8");
                resp.getWriter().write(name +"-----"+values+"<br>");
            }
            //输出相关提示
            resp.getWriter().write("Hello World! 写入了一个中文Cookie");
        }
    

    页面是可以看到中文 Cookie 的正确输出,上面还有HelloWorld 时候保存下来的 Cookie。

    image-20200904100447270

    当然在浏览器保存的值是编码后的 Cookie 。

    image-20200904100822056

    • 正值(n)表示该cookie将在n秒后过期。请注意,该值是Cookie过期的最长期限,而不是Cookie的当前期限,单位是 秒。
    • 负值表示cookie不会持久存储,并且在网络浏览器退出时将被删除。
    • 零值将导致cookie 被删除。这是删除Cookie 的一个途径。
    	//设置有效期 
    newCookie.setMaxAge(60 * 60 * 24 *2);  //以秒为单位,设置期限为两天,现在是2020-09-04
    

    image-20200904101210104

    注意要在添加该 Cookie 之前设置有效期,最后记得加入该 Cookie。

    此外,不知道你是否注意到

    image-20200904113206810

    原来 HelloWorld 时候保存下来的 Cookie ,我们并没有设置它的有效期,浏览器显示它的有效期是 Session ,这就意味着,这是默认的值,仅限于此次会话,关闭浏览器该 Cookie 就会失效。

    不知道你收否注意到:

    image-20200904132313714

    在此之前,首先要明确Cookie 具有不可跨域名性,访问不同的网站,浏览器会颁发不同的 Cookie 给服务器,也不会修改别的网站的 Cookie。即使⼀级域名相同,⼆级域名不同,也不能获取到 Cookie。

    但是一个庞大的网站会像一个树状结构,必定会细分有二级域名,如果这时候,需要二级域名也可以访问到相同一级域名下的 Cookie ,需要使用到 setDomain(String pattern) 方法。再次强调,要在添加该 Cookie 之前设置有效期,最后记得加入该 Cookie 。

        //设置运行访问Cookie 的域名
    newCookie.setDomain(".ling.com");   //取值规定为".域名"
    

    此时 www.blog.ling.com 也可以访问到 www.ling.com 发布的Cookie。

    个人测试可以使用 Tomcat 配置不同的临时域名进行请求测试,具体配置可以参照 Tomcat 简单配置使用,基本工作原理 中的配置临时域名部分进行配置。

    在浏览器中 Domain 旁边是 Path 属性。

    image-20200904131205924

    可以在浏览器中看到,每一个 Cookie 都会有自己的路径。path 属性决定允许的访问路径。一般来说 Cookie 一旦发布,相同域名的网站都可以访问到。

    现在有一需求是,我只想,某些路径以及该路径下的子路径可以访问到该 Cookie。这时候可以使用 setPath(String uri) 方法。

    假设你的浏览器当前已经有了两个Cookie:

    1. c1:name=id; value=itcast; path=/ling/;
    2. c2:name=name; value=qdmmy6; path=/ling/servlet/。

    当访问http://localhost/ling/*时,请求头中会包含c1,而不会包含c2。

    当访问http://localhost/ling/servlet/*时,请求头中会包含c1和c2。

    也就是说,在访问子路径时,会包含其父路径的Cookie,而在访问父路径时,不包含子路径的Cookie。

    此前的项目名笔者设置为空,现在设置为 /ling ,以便验证结论是否正确

    image-20200904150035302

    Cookie的SetPath设置cookie的路径,这个路径直接决定服务器的请求是否会从浏览器中加载某些cookie。

    首先默认情况如果不设置cookie的path,默认是 /项目名称/当前路径的上一层地址如:请求路径:/cookie_demo/servlet/login, cookie的路径:/cookie_demo/servlet

    如果我们设置path,如果当前访问的路径包含了cookie的路径(当前访问路径在cookie路径基础上要比cookie的范围小)cookie就会加载到request对象之中。

    先不进行访问路径设置,访问 http://localhost:8080/ling/cookies/testCookies ,查看默认路径。

    image-20200904145752882

    就是 /项目名称/当前路径的上一层地址 。

    再访问 http://localhost:8080/ling/request/getServlet ,验证是否能访问该Cookie。

    image-20200904150138865

    在未进行设置的情况下,当然是不行的。

    下面进行设置:

        //设置访问路径
            newCookie.setPath("/ling/");  //  /ling/ 路径下所有子路径都可以访问
    

    先访问 http://localhost:8080/ling/cookies/testCookies 设置Cookie。

    image-20200904145515201

    再访问 http://localhost:8080/ling/request/getServlet ,验证是否能访问该Cookie。

    image-20200904145401787

    删除,修改 Cookie 时,新建的 Cookie 除了 value 、maxAge 之外的所有属性都要与原 Cookie 相同。否则浏览器将视为不同的 Cookie,不予覆盖,导致删除修改失败!

    为什么不把修改放在删除后说,为了验证在除了 value 、maxAge 之外的其他属性不同时,是否能删除原 Cookie。

    这里的其他属性,就以 Path 属性举例。

    注意前提条件

    已知,在浏览器已经将 name : Chinese-Cookie ;value:%E4%B8%AD%E6%96%87Cookie---2 ;Path :/ling/cookies . 的 Cookie1 保存到硬盘上。

    现在 new 一个 Cookie2 将 Path 设置为 /ling/ 再将其删除(即 setMaxAge(0) )。

    这次的访问路径:http://localhost:8080/ling/cookies/testCookies

     //设置编码
            resp.setContentType("text/html; charset=UTF-8");
            //new 一个cookie
            //这里与此前设值不同需要进行编码处理
            Cookie newCookie =new Cookie("Chinese-Cookie", URLEncoder.encode("中文Cookie---3,我是Cookie2 。。。","UTF-8"));
            //cookie 的各种设置
                //设置有效期
            newCookie.setMaxAge(0);  //设置期限为0 ,试图删除 Cookie1 
                //设置访问路径
            newCookie.setPath("/ling/");  //  /ling/ 路径下所有子路径都可以访问
            //加入这个 cookie
            resp.addCookie(newCookie);
    	//取出该中文 Cookie 自然是需要解码的
            Cookie[] cookies = req.getCookies();
            for (Cookie cookie: cookies
                 ) {
                String name = cookie.getName();
                String values = URLDecoder.decode(cookie.getValue(),"UTF-8");
                resp.getWriter().write(name +"-----"+values+"<br>");
            }
            //输出相关提示
            resp.getWriter().write("Hello World! 写入了一个中文Cookie");
    

    预期是不能删除 Cookie1 ,得到的将是 Cookie1 ,因为 Cookie2 刚刚 new 出来虽然加了进去,可是已经 setMaxAge(0) ,就像这九子夺嫡,四阿哥已经登基,其他皇子想上台面,就像 Cookie2想输出在页面,自然也是轮不到他。

    image-20200904154635036

    这次来真的,删掉 Cookie1 。

     	//设置编码
            resp.setContentType("text/html; charset=UTF-8");
            //new 一个cookie
            //这里与此前设值不同需要进行编码处理
            Cookie newCookie =new Cookie("Chinese-Cookie", URLEncoder.encode("中文Cookie---3,我是Cookie2 。。。","UTF-8"));
            //cookie 的各种设置
                //设置有效期
            newCookie.setMaxAge(0);  //设置期限为0 ,试图删除 Cookie1 
            //加入这个 cookie
            resp.addCookie(newCookie);
    	//取出该中文 Cookie 自然是需要解码的
            Cookie[] cookies = req.getCookies();
            for (Cookie cookie: cookies
                 ) {
                String name = cookie.getName();
                String values = URLDecoder.decode(cookie.getValue(),"UTF-8");
                resp.getWriter().write(name +"-----"+values+"<br>");
            }
            //输出相关提示
            resp.getWriter().write("Hello World! 写入了一个中文Cookie");
    

    唉?你这没设置路径啊?

    噢,你得结合上面说的访问路径和 Cookie 的路径这一小节来看,因为访问路径是 http://localhost:8080/ling/cookies/testCookies ,这次的 Cookie2 默认Path 就是 /ling/cookies

    image-20200904153737663

    可以看到,是可以获取到在硬盘里的 Cookie1 的,输出在页面的还是 Cookie1 ,Cookie2 终究上不了台面,只是 Cookie1 被 Cookie2 覆盖以后,两大高手同归于尽,浏览器再无 Cookie1 Cookie2 。

    HTTP协议不仅仅是⽆状态的,⽽且是不安全的!如果不希望Cookie在⾮安全协议中传输,可以设置Cookie的secure属性为true,浏览器只会在HTTPS和SSL等安全协议中传输该Cookie。

    //设置只在安全协议下传输 Cookie
            newCookie.setSecure(true);
    

    当然了,设置secure属性不会将Cookie的内容加密。如果想要保证安全,最好使⽤md5算法加密。

    显示用户上次访问时间

    首先明确需求:

    • 第一次登录,记录下时间;
    • 第n次登录(n >= 2),显示前一次登录时间,并更新时间。

    要判断是否是第一次登录,可以通过判断是否含有指定 Cookie 来决定。

    @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //设置时间样式
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss");
            //编码
            resp.setContentType("text/html;charset=UTF-8");
            PrintWriter writer = resp.getWriter();
            //获取Cookie
            Cookie[] cookies = req.getCookies();
            //循环取出 Cookie
            for(int i = 0 ; cookies.length >0 && i < cookies.length -1 ; i++){
                //判断是否含有所需要的的 Cookie--"login-time"
                if(cookies[i].getName().equals("login-time")){
                    //注意进行编码
                    writer.write("欢迎您,您上次登录的时间是:  "+ URLDecoder.decode(cookies[i].getValue(),"UTF-8"));
                    //有则显示上一次时间,注意要解码,并 new Cookie() 更新时间,以便下一次显示使用
                    cookies[i].setValue(URLEncoder.encode(simpleDateFormat.format(new Date()),"UTF-8"));
                    //设置有效期
                    cookies[i].setMaxAge(2000);
                    resp.addCookie(cookies[i]);
                    return ;
                }
            }
            //没有则显示是第一次登录,设置登录时间
            Cookie cookie =new Cookie("login-time", URLEncoder.encode(simpleDateFormat.format(new Date()),"UTF-8"));
            cookie.setMaxAge(2000);
            resp.addCookie(cookie);
            writer.write("欢迎您,您是第一次登录。");
        }
    

    第一次登录:

    image-20200904205511277

    刷新一下:

    image-20200904205757353

    需要注意的几个点

    • 编码问题:需要设置编码,否则输出的中文会出现乱码。

    • 更新 Cookie 要设置Cookie 的有效期,否则更新的 Cookie 有效期默认为 Session ,关闭浏览器后,Cookie将会被销毁,不会被保存在硬盘。

    • Cookie 不支持特殊符号。下图表示 Cookie 的 Value 中有空格,会报服务器的错。解决方案有两个:

      • 把空格换成别的符号,比如下划线 _ ,横杆 - 等等。
      • 如上代码所示,进行编码,不过注意,取值的时候也要进行解码。

    image-20200904195320751

    web.xml

    参照 HttpServletRespnse 对象相关基本应用 进行配置 Servlet 。

  • 相关阅读:
    Guava教程
    Hibernate各种主键生成策略与配置详解
    JPA的坑多服务主键重复
    如何用redis来生成唯一Id
    【Gym 100712A】Who Is The Winner?
    【POJ 1416】Shredding Company
    【CodeForces 620D】Professor GukiZ and Two Arrays
    【CodeForces 621B】Wet Shark and Bishops
    【Gym 100015A】Another Rock-Paper-Scissors Problem
    【CodeForces 618B】Guess the Permutation
  • 原文地址:https://www.cnblogs.com/l1ng14/p/13616238.html
Copyright © 2011-2022 走看看