zoukankan      html  css  js  c++  java
  • 使用Cookie进行会话管理

    一、会话的概念

      会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。
      有状态会话:一个同学来过教室,下次再来教室,我们会知道这个同学曾经来过,这称之为有状态会话。

    二、会话过程中要解决的一些问题?

      每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。

    三、保存会话数据的两种技术

    3.1、Cookie

      Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。

    3.2、Session

      Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于 session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它 web资源时,其它web资源再从用户各自的session中取出数据为用户服务。

    四、Java提供的操作Cookie的API

      Java中的javax.servlet.http.Cookie类用于创建一个Cookie

    Cookie类的主要方法

    方法

    类型

    描述

    Cookie(String name, String value)

    构造方法

    实例化Cookie对象,传入cooke名称和cookie的值

    public String getName()

    普通方法

    取得Cookie的名字

    public String getValue()

    普通方法

    取得Cookie的值

    public void setValue(String newValue)

    普通方法

    设置Cookie的值

    public void setMaxAge(int expiry)

    普通方法

    设置Cookie的最大保存时间,即cookie的有效期,当服务器给浏览器回送一个cookie时,如果在服务器端没有调用setMaxAge方法设置cookie的有效期,那么cookie的有效期只在一次会话过程中有效,用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一次会话, 当用户关闭浏览器,会话就结束了,此时cookie就会失效,如果在服务器端使用setMaxAge方法设置了cookie的有效期,比如设置了30分 钟,那么当服务器把cookie发送给浏览器时,此时cookie就会在客户端的硬盘上存储30分钟,在30分钟内,即使浏览器关了,cookie依然存 在,在30分钟内,打开浏览器访问服务器时,浏览器都会把cookie一起带上,这样就可以在服务器端获取到客户端浏览器传递过来的cookie里面的信 息了,这就是cookie设置maxAge和不设置maxAge的区别,不设置maxAge,那么cookie就只在一次会话中有效,一旦用户关闭了浏览 器,那么cookie就没有了,那么浏览器是怎么做到这一点的呢,我们启动一个浏览器,就相当于启动一个应用程序,而服务器回送的cookie首先是存在 浏览器的缓存中的,当浏览器关闭时,浏览器的缓存自然就没有了,所以存储在缓存中的cookie自然就被清掉了,而如果设置了cookie的有效期,那么 浏览器在关闭时,就会把缓存中的cookie写到硬盘上存储起来,这样cookie就能够一直存在了。

    public int getMaxAge()

    普通方法

    获取Cookies的有效期

    public void setPath(String uri)

    普通方法

    设置cookie的有效路径,比如把cookie的有效路径设置为"/xdp",那么浏览器访问"xdp"目录下的web资 源时,都会带上cookie,再比如把cookie的有效路径设置为"/xdp/gacl",那么浏览器只有在访问"xdp"目录下的"gacl"这个目 录里面的web资源时才会带上cookie一起访问,而当访问"xdp"目录下的web资源时,浏览器是不带cookie的。也就是说访问子目录会带上父目录的cookie,访问父目录不会携带子目录的cookie。

    public String getPath()

    普通方法

    获取cookie的有效路径

    public void setDomain(String pattern)

    普通方法

     设置cookie的有效域

    public String getDomain()

    普通方法

     获取cookie的有效域

      response接口也中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。 同样,request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。

    五、Cookie使用范例

    5.1、使用cookie记录用户上一次访问的时间

    package chat;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Date;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("/CookieTest")
    public class CookieTest extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 设置服务器端以UTF-8编码进行输出
            response.setCharacterEncoding("UTF-8");
            // 设置浏览器以UTF-8编码进行接收,解决中文乱码问题
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
    
            boolean hasLastAccessTimeCookie = false;
            // 获取浏览器访问访问服务器时传递过来的cookie数组
            Cookie[] cookies = request.getCookies();
            // 如果用户是第一次访问,那么得到的cookies将是null
            if (cookies != null) {
                for (int i = 0; i < cookies.length; i++) {
                    Cookie cookie = cookies[i];
                    if (cookie.getName().equals("lastAccessTime")) {
                        out.write("您上次访问的时间是:");
                        Long lastAccessTime = Long.parseLong(cookie.getValue());
                        Date date = new Date(lastAccessTime);
                        out.write(date.toLocaleString());
                        hasLastAccessTimeCookie = true;
                    }
                }
            }
    
            if (!hasLastAccessTimeCookie) {
                out.write("这是您第一次访问本站!");
            }
    
            // 用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
            Cookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis() + "");// 创建一个cookie,cookie的名字是lastAccessTime
    
            // 将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
            response.addCookie(cookie);
    
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    
    }

    (1)第一次访问时这个Servlet时,效果如下所示:

     请求头:

     响应头:

    查看返回的cookie信息:(默认path是当前访问的资源路径,到期时间是会话结束)

    (2)点击浏览器的刷新按钮,进行第二次访问,此时就服务器就可以通过cookie获取浏览器上一次访问的时间了,效果如下:

    (3)上面的例子中,在程序代码中并没有使用setMaxAge方法设置cookie的有效期,所以当关闭浏览器之后,cookie就失效了,要想在关闭了浏览器之后,cookie依然有效,那么在创建cookie时,就要为cookie设置一个有效期。为了访问本站内所有资源都可以携带该cookie,设置路径为根路径。如下所示:

            // 用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
            Cookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis() + "");// 创建一个cookie,cookie的名字是lastAccessTime
            // 设置有效期为一天
            cookie.setMaxAge(24 * 60 * 60);
            // 设置路径为根路径
            cookie.setPath("/");
            // 将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
            response.addCookie(cookie);

    删掉cookie访问:

     查看cookie信息如下:

      用户第一次访问时,服务器发送给浏览器的cookie就存储到了硬盘上,如下所示:

      这样即使关闭了浏览器,下次再访问时,也依然可以通过cookie获取用户上一次访问的时间。

    (4)测试访问系统其它资源也会携带该cookie:

    补充:关于setPath和setDomain控制cookie的作用范围

    1.setPath控制:

      setPath设置为/的时候可以在同一应用服务器内共享,比如我们的tomcat里面有两个应用:WebSocket和WebSocket2。当我们设置为 / 的时候在两个应用内都可以共享。可以理解为 / 是相对于域名开始后的路径,想要在某个应用内可见就设为 /appName;如果设置在某个应用的某个子请求可见就设为 /appName/subName。

    WebSocket应用的设置Cookie:

    package chat;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.net.URLEncoder;
    import java.util.Date;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("/CookieTest")
    public class CookieTest extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 设置服务器端以UTF-8编码进行输出
            response.setCharacterEncoding("UTF-8");
            // 设置浏览器以UTF-8编码进行接收,解决中文乱码问题
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
    
            boolean hasLastAccessTimeCookie = false;
            // 获取浏览器访问访问服务器时传递过来的cookie数组
            Cookie[] cookies = request.getCookies();
            // 如果用户是第一次访问,那么得到的cookies将是null
            if (cookies != null) {
                for (int i = 0; i < cookies.length; i++) {
                    Cookie cookie = cookies[i];
                    if (cookie.getName().equals("lastAccessTime")) {
                        out.write("您携带的cookie信息:");
                        out.write(cookie.getValue());
                        hasLastAccessTimeCookie = true;
                    }
                }
            }
    
            if (!hasLastAccessTimeCookie) {
                out.write("这是您第一次访问本站!");
            }
    
            // 用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
            Cookie cookie = new Cookie("lastAccessTime", URLEncoder.encode("WebSocket Application", "UTF-8"));
            // 设置路径为根路径
            cookie.setPath("/");
            // 将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
            response.addCookie(cookie);
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    
    }

    WebSocket2应用的设置Cookie:

    import java.io.IOException;
    import java.io.PrintWriter;
    import java.net.URLEncoder;
    import java.util.Date;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("/CookieTest")
    public class CookieTest extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 设置服务器端以UTF-8编码进行输出
            response.setCharacterEncoding("UTF-8");
            // 设置浏览器以UTF-8编码进行接收,解决中文乱码问题
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
    
            boolean hasLastAccessTimeCookie = false;
            // 获取浏览器访问访问服务器时传递过来的cookie数组
            Cookie[] cookies = request.getCookies();
            // 如果用户是第一次访问,那么得到的cookies将是null
            if (cookies != null) {
                for (int i = 0; i < cookies.length; i++) {
                    Cookie cookie = cookies[i];
                    if (cookie.getName().equals("lastAccessTime")) {
                        out.write("您携带的cookie信息:");
                        out.write(cookie.getValue());
                        hasLastAccessTimeCookie = true;
                    }
                }
            }
    
            if (!hasLastAccessTimeCookie) {
                out.write("这是您第一次访问本站!");
            }
    
            // 用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
            Cookie cookie = new Cookie("lastAccessTime", URLEncoder.encode("WebSocket2 Application", "UTF-8"));
            // 设置路径为根路径
            cookie.setPath("/");
            // 将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
            response.addCookie(cookie);
    
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    
    }

    结果:

     (2)当路径不同,name相同会被认为是两个cookie。比如说我们将WebSocket2的路径设为/WebSocket2

            // 用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
            Cookie cookie = new Cookie("lastAccessTime", URLEncoder.encode("WebSocket2 Application", "UTF-8"));
            // 设置路径为根路径
            cookie.setPath("/WebSocket2");
            // 将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
            response.addCookie(cookie);

    结果:

    查看使用的cookie:

    (3)如果想设置为在某个应用内共享,对于应用WebSocket可以设置path为 /WebSocket,对于应用WebSocket2设置路径为/WebSocket2即可。

    2.setDomain的用法

      谈谈域名的体系结构。从WWW.CNNIC.NET.CN 这个域名来看,它是由几个不同的部分组成的,这几个部分彼此之间具有层次关系。其中最后的.CN是域名的第一层,.NET是第二层,.CNNIC是真正的 域名,处在第三层,当然还可以有第四层,如:INNER.CNNIC.NET.CN,至此我们可以看出域名从后到前的层次结构类似于一个倒立的树型结构。 其中第一层的.CN叫做地理顶级域名。

      目前互联网上的域名体系中共有三类顶级域名:一是地理顶级域名,共有243个国家和地区的代码。例如.CN 代表中国,.JP代表日本,.UK代表英国等等,另一类是类别顶级域名,共有7个:.COM(公司),.NET(网络机构),.ORG(组织机 构),.EDU(美国教育),.GOV(美国政府部门),.ARPA(美国军方),.INT(国际组织)。由于互联网最初是在美国发展起来的,所以最初的 域名体系也主要供美国使用,所以.GOV,.EDU,.ARPA虽然都是顶级域名,但却是美国使用的。只有.COM,.NET,.ORG成了供全球使用的 顶级域名。相对于地理顶级域名来说,这些顶级域名都是根据不同的类别来区分的,所以称之为类别顶级域名。随着互联网的不断发展,新的顶级域名也根据实际需 要不断被扩充到现有的域名体系中来。新增加的顶级域名是.BIZ(商业),.COOP(合作公司),.INFO(信息行业),.AERO(航空 业),.PRO(专业人士),.MUSEUM(博物馆行业),.NAME(个人)。

    假设有两个域名:

    a.b.com.cn  域名1

    b.b.com.cn  域名2

      域名1与域名2都是b.com.cn的子域名,b.com.cn又是com.cn的子域名。

    在域名1使用的cookie中可以设置域名:a.b.com.cn     b.com.cn   (设置为.com.cn或者.cn是不可以的,如果可以就乱了)。

    同理域名2使用的cookie可以设为 :     b.b.com.cn     b.com.cn

      这么看来,设为b.com.cn是域名1和域名2都可以访问。也就是父域名的cookie子域名可以看到,所以同父域名做SSO就比较简单。与path一样,domain不同name相同的cookie属于两个cookie。

    例如:

    (1)先在C:WindowsSystem32driversetchosts下面增加虚拟域名:

    127.0.0.1 www.a.b.com.cn
    127.0.0.1 www.b.b.com.cn

    (2)设置域名为b.com.cn(自己测试发现.b.com.cn效果一样)

            // 用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
            Cookie cookie = new Cookie("lastAccessTime", URLEncoder.encode("WebSocket Application", "UTF-8"));
            // 设置路径为根路径
            cookie.setPath("/");
            cookie.setDomain("b.com.cn");
            // 将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
            response.addCookie(cookie);

    (3)第一次访问

     (4)第二次访问:

     查看使用的cookie:

     (5)使用域名1也可以访问

     (6)修改域名为指定的域名一

            // 用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
            Cookie cookie = new Cookie("lastAccessTime", URLEncoder.encode("WebSocket Application", "UTF-8"));
            // 设置路径为根路径
            cookie.setPath("/");
            cookie.setDomain("a.b.com.cn");
            // 将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
            response.addCookie(cookie);

      经测试自己从域名2访问也不会携带cookie,也就是说将cookie设置为其他域名之后,自己域内访问也不会携带cookie。

     (7)如果父域名与子域名有相同的cookie,都会携带上去:

     查看使用的cookie:

    六、Cookie细节

    1. 一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
    2. 一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
    3. 浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
    4. 如果创建了一个cookie,并将他发送到浏览器,默认情况下它 是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用 maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。

    6.1、删除Cookie

    注意:删除cookie时,path必须一致,否则不会删除(Domain也必须一致)

            // 用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
            Cookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis() + "");// 创建一个cookie,cookie的名字是lastAccessTime
            // 设置有效期为0就会删除cookie
            cookie.setMaxAge(0);
            // 设置路径为根路径
            cookie.setPath("/");
            // 将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
            response.addCookie(cookie);

    访问:

    查看cookie:

    再次发请求不会携带cookie:

     6.2、cookie中存取中文

      要想在cookie中存储中文,那么必须使用URLEncoder类里面的encode(String s, String enc)方法进行中文转码,例如:

            // 用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
            Cookie cookie = new Cookie("lastAccessTime", URLEncoder.encode("测试cookie", "UTF-8"));
            // 设置路径为根路径
            cookie.setPath("/");
            // 将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
            response.addCookie(cookie);

      在获取cookie中的中文数据时,再使用URLDecoder类里面的decode(String s, String enc)进行解码,例如:

    URLDecoder.decode(cookies[i].getValue(), "UTF-8"

    补充:cookie还有两个属性,Secure和HttpOnly

            cookie2.setHttpOnly(true);
            cookie2.setSecure(true);

      HttpOnly比较好理解,设置HttpOnly=true的cookie不能被js获取到,无法用document.cookie打出cookie的内容。可以防止XSS攻击。跨站脚本攻击(Cross Site Scripting)缩写为CSS,但这会与层叠样式表(Cascading Style Sheets,CSS)的缩写混淆。因此,有人将跨站脚本攻击缩写为XSS。

      Secure属性是说如果一个cookie被设置了Secure=true,那么这个cookie只能用https协议发送给服务器,用http协议是不发送的。换句话说,cookie是在https的情况下创建的,而且他的Secure=true,那么之后你一直用https访问其他的页面(比如登录之后点击其他子页面),cookie会被发送到服务器,你无需重新登录就可以跳转到其他页面。但是如果这是你把url改成http协议访问其他页面,你就需要重新登录了,因为这个cookie不能在http协议中发送。

  • 相关阅读:
    Linux 的启动流程(转)
    HDU 4686 Arc of Dream (矩阵快速幂)
    自己编写jQuery动态引入js文件插件 (jquery.import.dynamic.script)
    Oracle job procedure 存储过程定时任务
    Spring3 整合MyBatis3 配置多数据源 动态选择SqlSessionFactory
    Spring3 整合Hibernate3.5 动态切换SessionFactory (切换数据库方言)
    Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法
    软件设计之UML—UML的构成[上]
    软件设计之UML—UML中的六大关系
    ActiveMQ 即时通讯服务 浅析
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/7447432.html
Copyright © 2011-2022 走看看