目录

1 Cookie对象

1.1 定义

1.2 操作cookie 

1.2.1 开启浏览器cookie

1.2.2 读写Cookie对象

1.2.3 Cookie对象的方法

1.2.4 cookie操作

1.2.5 Cookie弊端

1.3 Cookie原理

1.4 关闭缓存

2 Session对象

2.1 定义

2.2 session 如何工作 

2.3 两种方式获取sessionID

2.4 会话结束的四种方法

2.5 session实操

2.6 分布式 Session 框架 

2.6.1 存在哪些问题 

2.6.2 可以解决哪些问题

2.6.3 总体实现思路

3 Cookie与Session区别

3.1 Cookie和Session的关系

3.2 禁用Cookie后Session还能用吗

3.3 解决方案


1 Cookie对象

1.1 定义

Cookie 是一种保持 Web应用程序连续性的一种方法。
Cookie 能够帮助 Web 站点保存有关访问者的信息,以便服务器更好识别访问者。
是实现会话跟踪的一种重要手段作为客户端信息的一类,cookie信息以文本的的形式保存在客户端的硬盘上。
在系统盘下\Documents and Settings,当前登录名对应的目录,可以看到一个名为“cookie”子目录。在该目录下,有许多文本文件,保存的就是cookie信息。
Cookie文件是属于文本文件,但是经过加密过的,一般通过普通文本工具无法查看到内容。由于Cookie文件是经过加密处理的,所以在一般的用户看来只是一堆没有任何意义的数字和字母组合,但对于服务器的处理程序可以通过它完成很多功能。
浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4 KB,不同的浏览器对cookie限制不同。          

1.2 操作cookie 

1.2.1 开启浏览器cookie

浏览器开启cookie,找到internet属性-->隐私-->高级,点击替代自动cookie,再点击总是允许会话            

1.2.2 读写Cookie对象

使用cookie必须结合request对象和response对象。写cookie到客户端使用response,读取客户端的cookie使用request            

 Cookie c=new Cookie(String,String);
 c.setMaxAge(int);//存活时间,单位:秒
 response.addCookie(c);

读取cookie  

                  Cookie[] cs=request.getCookies();
                    for(int i=0;i<cs.length;i++){
                        //遍历cookie数组
                    }

1.2.3 Cookie对象的方法

public Cookie(String,String)构造方法
 void setMaxAge(int)设置cookie的过期时间。如果不设置,则cookie只在当前会话中有效,且不会保存到客户端磁盘中,关闭浏览器后会消失。
String getName()获取cookie的名称
String getValue()获取cookie的值
 void setValue(String)设置cookie的值
 void setPath(String)设置cookie在哪个目录下的JSP文件中有效。如果参数是/,此时服务器上的所有页面都可以接收到该cookie

1.2.4 cookie操作

通过Cookie记录用户名和密码,当用户登录后通过Cookie保存用户名,下次访问该页面时,显示用户名信息。
首先,在登录页面输入用户名和密码登录后将用户名和密码通过cookie保存在客户端访问allcook.jsp页面可以查看刚才保存的cookie信息。
login2.jsp:                    

               <%@ page contentType="text/html;charset=gbk"%>
                        ……
                        <form action="addcook.jsp" method="post">
                            用户名:<input type="text" name="nam"><br>
                            密  码:<input type="text" name="pwd"><br>
                            <input type=submit value="登录">
                        </form>
                        ……

addcook.jsp                       

                   <%
                        request.setCharacterEncoding("gbk");
                        String nam=request.getParameter("nam");
                        String pwd=request.getParameter("pwd");
                        //创建Cookie对象
                        Cookie c1=new Cookie("nam",nam);
                        Cookie c2=new Cookie("pwd",pwd);
                        c1.setMaxAge(300);//保存在客户端磁盘中,时间5分钟
                        c2.setMaxAge(300);
                        //将Cookie写入客户端磁盘
                        response.addCookie(c1);
                        response.addCookie(c2);
                    %>
                    Cookie被写在了客户端的文件中

allcook.jsp:                   

                <%@ page contentType="text/html;charset=gbk"%>
                        ……
                        <%
                        //获取客户端所有Cookie对象
                        Cookie[] cs=request.getCookies();
                            for(int i=0;i<cs.length;i++){
                                String name=cs[i].getName();
                                String value=cs[i].getValue();
                                out.println(name+"="+value+"<br>");
                            }
                        %>

1.2.5 Cookie弊端

cookie注意事项:
cookie信息是在浏览器中的,但并不是所有的浏览器都支持cookie功能,不同浏览器对cookie有不同的限制
Cookie传输的数据大小(4KB)限制了一些需要大数据存储应用的需求。
不同的网站中存储的cookie不是通用的。
cookie的安全性上面临居多挑战和质疑。
例如:cookie信息可能泄露个人隐私或被用来收集用户上网习惯,收集用户信息登录用户账号

1.3 Cookie原理

Cookie 是如何加到 HTTP 的 Header 中的呢?当我们用 Servlet 3.0 规范来创建一个Cookie对象时,该Cookie既支持Version 0又支持Version 1,如果你设置了Version 1中的配置项,即使你没有设置版本号,Tomcat 在最后构建 HTTP 响应头时也会自动将 Version的版本设置为 1。下面看一下 Tomcat 是如何调用 addCookie 方法的,图 10-1 是 Tomcat 创建 Set-Cookie 响应头的时序图

从图 10-1 中可以看出,真正构建 Cookie 是在 org.apache.catalina.connector.Response类中完成的,调用 generateCookieString 方法将 Cookie 对象构造成一个字符串,构造的字符串的格式如 userName=“junshan”;Version=“1”; Domain=“xulingbo.net”; MaxAge=1000。然后将这个字符串命名为 Set-Cookie 添加到 MimeHeaders 中

1.4 关闭缓存

 清除页面缓存

在jsp页面里
<%response.setHeader("Pragma","No-cache"); 
response.setHeader("Cache-Control","no-cache"); 
response.setDateHeader("Expires", 0); 
response.flushBuffer();%> 


在html页里
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache"> 
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache"> 
<META HTTP-EQUIV="Expires" CONTENT="0">

清除cookie

<% 
Cookie killMyCookie = new Cookie("mycookie", null); 
killMyCookie.setMaxAge(0); 
killMyCookie.setPath("/"); 
response.addCookie(killMyCookie); 
%> 

清除session

清除session方法
<%@ page language="java" %> 
<% 
session.invalidate(); 
%> 

 在页面关闭时清除session,需要捕获windows.onclose事件,再调用清除session方法

2 Session对象

2.1 定义

前面已经介绍了 Cookie 可以让服务端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些 Cookie,如果 Cookie 很多,则无形地增加了客户端与服务端的数据传输量,而 Session 的出现正是为了解决这个问题。

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

session对象提供一种机制,让服务器能辨认不同的客户端。是服务器端和客户端另一种保持状态的有效方法。 与Cookie不同的是Session信息保存在服务器端。服务器通过不同的session对象来记录不同的客户端。
session的方法:

String getId()取得session的ID
void invalidate()取消session对象,并将对象存放的内容完全抛弃
void setMaxInactiveInterval(int)设定最大session不活动时间,若超过这个时间不活动,session将会失效,单位为秒

2.2 session 如何工作 

有了 Session ID,服务端就可以创建 HttpSession 对象了,第一次触发通过request.getSession()方法。如果当前的Session ID还没有对应的HttpSession对象,那么就创建一个新的,并将这个对象加到 org.apache.catalina. Manager 的 sessions 容器中保存。Manager 类将管理所有 Session 的生命周期,Session 过期将被回收,服务器关闭,Session将被序列化到磁盘等。只要这个 HttpSession 对象存在,用户就可以根据 Session ID 来获取这个对象,也就做到了对状态的保持。

从上图中可以看出,从 request.getSession 中获取的 HttpSession 对象实际上是StandardSession 对象的门面对象,这与前面的 Request 和 Servlet 是一样的原理。下图 是 Session 工作的时序图 

从时序图中可以看出,从 Request 中获取的 Session 对象保存在 org.apache.catalina.Manager 类中,它的实现类是 org.apache.catalina.session.StandardManager,通过requestedSessionId 从 StandardManager 的 sessions 集合中取出 StandardSession 对象。由于一个 requestedSessionId 对应一个访问的客户端,所以一个客户端也就对应一个StandardSession 对象,这个对象正是保存我们创建的 Session 值的。下面我们看一下StandardManager 这个类是如何管理 StandardSession 的生命周期的。

StandardManager 类负责 Servlet 容器中所有的 StandardSession 对象的生命周期管理。当 Servlet 容器重启或关闭时,StandardManager 负责持久化没有过期的 StandardSession 对象,它会将所有的 StandardSession 对象持久化到一个以“SESSIONS.ser”为文件名的文件中。到 Servlet 容器重启时,也就是 StandardManager 初始化时,它会重新读取这个文件,解析出所有 Session 对象,重新保存在 StandardManager 的 sessions 集合中

2.3 两种方式获取sessionID

session.getId();

request.getRequestedSessionId();

2.4 会话结束的四种方法

1.在web.xml文件中设置他的最大活动时间                   

                  <!--设置 session 的过期时间,单位是分钟-- >
                    <session-config>
                          <session-timeout>30</session-timeout> 
                    </session-config>
                    值为-1表示永不过期,不推荐使用。

2.在JSP中通过session.invalidate()方法直接关闭一个会话
3.关闭浏览器后,服务器并不知道浏览器已经关闭,session对象依然存在,但”无人认领”,将自动过期。
4.调用session.setMaxInactiveInterval(1),1秒钟内不发请求则过期。

2.5 session实操

当用户正常登录验证后,转发到管理页面,而当用户未正常登录则无法访问管理页面。
                login.jsp:                 

                    ……
                    <form action="check.jsp" method="post">
                        用户名:<input type="text" name="nam"><br>
                        <input type=submit value="登录">
                    </form>

                   show.jsp:                   

                 <%@ page contentType="text/html;charset=gbk"%>
                    …//如果不登录,直接访问此页面时,则通过session存入的值进行验证
                        <% if(session.getAttribute("nam")==null){ %>    
                            您还没有登录,无法访问此页面
                             //此处设置响应转发回登录页面,进行登录。
                        <%} else {%>
                        welcome,<%=session.getAttribute("nam")+"" %>
                            <a href="exit.jsp">注销登录</a>
                        <%} %>
                        <br><br>
                        <center>
                        <%
                            if(session.getAttribute("nam")==null){
                        %>
                        <a href="login.jsp">点击这里</a>返回登录页面.
                        <%} else {%>
                        网页正文内容
                        
                    <%@ page contentType="text/html;charset=gbk"%>
                    <%    //清空会话中的值,并销毁当前会话对象
                        session.invalidate();
                        response.sendRedirect("show.jsp");
                    %>

                check.jsp:                    

             <%@ page contentType="text/html;charset=gbk"%>
                    <%
                        request.setCharacterEncoding("gbk");
                            String nam=request.getParameter("nam");
                            if(nam!=null){//此处进行登录验证
                                session.setAttribute("nam",nam);//登录成功在session放置用户名
                                response.sendRedirect("show.jsp");
                            } else response.sendRedirect("login.jsp");
                    %>

2.6 分布式 Session 框架 

从前面的分析可知,Session 和 Cookie 各自有优点和缺点。在大型互联网系统中,单独使用 Cookie 和 Session 都是不可行的,原因很简单。因为如果使用 Cookie,则可以很好地解决应用的分布式部署问题,大型互联网应用系统的一个应用有上百台机器,而且有很多不同的应用系统协同工作,由于 Cookie 是将值存储在客户端的浏览器里,用户每次访问都会将最新的值带回给处理该请求的服务器,所以也就解决了同一个用户的请求可能不在同一台服务器处理而导致的 Cookie 不一致的问题。 

2.6.1 存在哪些问题 

这种“谁家的孩子谁抱走”的处理方式的确是大型互联网的一个比较简单但的确可以解决问题的处理方式,但是这种处理方式也会带来了很多其他问题,如下所述。

  • 客户端 Cookie 存储限制。随着应用系统的增多,Cookie 数量也快速增加,但浏览器对于用户 Cookie 的存储是有限制的。例如,对 IE7 之前的 IE 浏览器,Cookie个数的限制是 20 个;而对后续的版本,包括 Firefox 等,Cookie 个数的限制都是50 个,总大小不超过 4KB,超过限制就会出现丢弃 Cookie 的现象,这会严重影响应用系统的正常使用。
  • Cookie 管理的混乱。在大型互联网应用系统中,如果每个应用系统都自己管理每个应用使用的 Cookie,则会导致混乱,由于通常应用系统都在同一个域名下,Cookie 又有上面一条提到的限制,所以没有统一管理很容易出现 Cookie 超出限制的情况。 

安全令人担忧。虽然可以通过设置 HttpOnly 属性防止一些私密 Cookie 被客户端访问,但是仍然不能保证 Cookie 无法被篡改。为了保证 Cookie 的私密性通常会对 Cookie 进行加密,但是维护这个加密 Key 也是一件麻烦的事情,无法保证定期更新加密 Key 也是带来安全性问题的一个重要因素。

当我们对以上问题不能再容忍下去时,就不得不想其他办法处理了。 

2.6.2 可以解决哪些问题

既然 Cookie 有以上问题,Session 也有它的好处,那么为何不结合使用 Session 和 Cookie呢?下面是分布式 Session 框架可以解决的问题。

  • Session 配置的统一管理
  • Cookie 使用的监控和统一规范管理
  • Session 存储的多元化
  • Session 配置的动态修改
  • Session 加密 key 的定期修改
  • 充分的容灾机制,保持框架的使用稳定性
  • Session 各种存储的监控和报警支持
  • Session 框架的可扩展性,兼容更多的 Session 机制如 wapSession
  • 跨域名 Session 与 Cookie 如何共享的问题。现在同一个网站可能存在多个域名,如何将 Session 和 Cookie 在不同的域名之间共享是一个具有挑战性的问题。

2.6.3 总体实现思路

分布式 Session 框架的架构图如图 10-9 所示。

为了达成上面所说的几个目标,我们需要一个服务订阅服务器,在应用启动时可以从这个订阅服务器  订阅这个应用需要的可写 Session 项和可写 Cookie 项,这些配置的 Session和 Cookie 可以限制这个应用能够使用哪些 Session 和 Cookie,甚至可以控制 Session 和Cookie 可读或者可写。这样可以精确地控制哪些应用可以操作哪些 Session 和 Cookie,可以有效控制 Session 的安全性和 Cookie 的数量。 

3 Cookie与Session区别

由上可知,Cookie 是由服务器在 HTTP 响应中发送给客户端(通常是浏览器)的一小段数据。客户端将这些信息保存在本地,并在后续的请求中自动将其发送回服务器。而 Session 是在服务器端创建的一种机制,用于跟踪用户的会话状态。服务器会给每个用户分配一个唯一的会话 ID,并将该 ID 通过 Cookie 或其他方式传递给客户端。客户端随后在请求时携带会话 ID,服务器根据这个 ID 从内存或数据库中检索与该用户相关的会话数据。

3.1 Cookie和Session的关系

严格意义上来说,Cookie 和 Session 是没有任何关系的,但 Session 的实现中借助了 Cookie 机制。

通过以下 Session 执行的机制,我们就能知道 Session 是如何借助 Cookie 完成自己的执行流程的:

  • 会话创建:通常情况下,当用户登录成功后,服务器会为该用户创建一个新的会话。在创建会话过程中,服务器会为该会话生成一个唯一的标识符,通常称为 Session ID。
  • Session ID 传递:服务器将生成的 Session ID 通过响应的方式发送给客户端,使用 SetCookie 命令,将用户的 Session ID 保存在 Cookie 中,通常是一个名为 JSESSIONID 的 Cookie。
  • Session 数据存储:在服务器端,Session 数据会被存储在一个能够关联 Session ID 的数据结构中(例如内存、数据库或者文件存储等)。常用的方式是将 Session ID 作为键,与对应的 Session 用户身份数据进行关联。
  • Session ID 验证与检索:当用户发送一个新的请求时,客户端会将之前存储的 Session ID 携带在请求的 Cookie 或请求头中发送给服务器。服务器会根据 Session ID 找到对应的 Session 数据,从而获得用户的状态信息。
  • Session 数据使用:服务器在获取到 Session 数据后,可以根据具体需求读取、修改或删除其中保存的状态信息。服务器可以通过 Session 来管理用户的登录状态、购物车内容、用户配置等。
  • Session 过期与销毁:Session 有一个有效期限,一般通过设置一个固定的时间,或者在一定时间内没有用户活动时会将 Session 标记为过期。当 Session 过期时,服务器会销毁对应的 Session 数据,释放内存或其他资源。

所以默认情况下,Session 是借助 Cookie 来完成身份标识的传递的,这样服务器端才能根据 Session ID 和保存的会话信息进行关联,用于找到某个具体登录的用户,所以说:默认情况下,Session 机制是依赖 Cookie 实现的。

3.2 禁用Cookie后Session还能用吗

那么问题来了,禁用 Cookie 后 Session 还能用吗?答案是:默认情况下禁用 Cookie 后,Session 是无法正常使用的。

这是因为大多数 Web 服务器都是依赖于 Cookie 来传递 Session 的会话 ID 的。客户端浏览器禁用 Cookie 时,服务器将无法把会话 ID 发送给客户端,客户端也无法在后续请求中携带会话 ID 返回给服务器,从而导致服务器无法识别用户会话。

但是,默认情况下禁用 Cookie 后,Session 就不能用了,但可以通过一些手段来解决这个问题。

3.3 解决方案

以下的两种解决方案可以绕过 Cookie 继续运行 Session:

  • URL 中携带 SessionID:可以通过 URL 重写的方式将 Session ID 添加到所有的 URL 中。服务器生成 Session ID 后,将其作为 URL 的一部分传递给客户端,客户端在后续的请求中将 Session ID 带在 URL 中。服务器端需要相应地解析 URL 来获取 Session ID,并维护用户的会话状态。
  • 隐藏表单字段传递 SessionID:将 Session ID 添加到 HTML 表单的隐藏字段中。在每个表单中添加一个隐藏的字段,保存 Session ID,客户端提交表单时会将 Session ID 随表单数据一起发送到服务器,服务器通过解析表单数据中的 Session ID 来获取用户的会话状态。

这些方法虽然可以在禁用 Cookie 的情况下继续使用 Session,但需要在服务器端进行相应的代码修改和配置。但同时这些手段也带来了以下几个新问题:

  • 增加了编码复杂度:需要改前端和后端代码才能继续使用 Session 机制,增加了编码复杂度。
  • 增加了安全风险:这些替代方法可能会增加一些安全风险,因为 Session ID 将以明文形式出现在 URL 或表单中,很容易被第三方劫持和获取。

更多推荐