会话是啥意思(客户端会话跟踪技术 Cookie 浅谈)
前言
用户打开浏览器 ,第一次访问 Web 服务器资源时 ,会话建立 ,直到有一方断开了连接则会话结束 ,例如浏览器或者服务器断开 。在一次会话中可以包含多次的请求和响应 。
上述的整个过程称为会话 。
例如 ,当我们在浏览器访问一个网站时 ,浏览器和这个网站服务器就建立了一次会话 ,后面在这个网站中的所有操作都属于这一次会话 ,当我们关闭浏览器程序或者服务器关闭则会话结束 。
现实中 ,服务器会被多个用户同时访问 ,为了识别多次请求是否来自同一个浏览器 ,在一次会话的多次请求间共享数据 ,出现了会话跟踪技术 。会话跟踪技术是一种维护浏览器状态的方法,服务器识别浏览器的过程就被称为会话跟踪 。
例如 ,实现购物车的功能中 ,第一次请求,用户将商品加入购物车 ,下一次请求 ,用户去购物车结算 ,第二次请求需要展示前一次添加到购物车中的商品 ,此时就要用到数据共享 。再比如说页面展示用户的登录信息 ,网站登录页面记住密码等功能 ,都会用到数据共享 。
为什么之前浏览器和服务器不支持数据共享?
之前说过 ,浏览器和服务器之间使用 HTTP 协议请求来传输数据 ,而 HTTP 协议是无状态的 ,也就是说浏览器发起的两次请求是毫不相干的 , ,每次浏览器向服务器发起请求时 ,服务器都会将该请求视为新的请求 。
HTTP 协议之所以无状态,是因为该协议想让每次请求之间相互独立 ,互不影响 ,提高了性能,但是也伴随着多次请求之间数据无法共享的问题 ,而会话跟踪技术正是解决了这个难题 。
会话跟踪技术
我们有两种方式来实现会话跟踪技术 ,分别是客户端会话跟踪技术 Cookie 和服务器会话跟踪技术 Session 。其中 Cookie 是存储在客户端浏览器的 ,而 Session 是存储在服务器端的 。两者各有利弊 ,接下来我们可以从两者的原理 ,使用等一同探讨。
什么是 Cookie ,Cookie 是如何实现的?如何使用?接下来带着这些问题深入研究 。
Cookie的概念
Cookie 是指客户端会话跟踪技术 ,其特点是将数据保存到客户端 ,以后每次请求都会携带 Cookie 数据进行访问 。
Cookie的工作流程
浏览器发送 HTTP 请求给 Web 服务器资源时 ,服务器资源接收请求并进行业务处理 ,在这个过程中会创建一个 Cookie 对象 ,并将请求参数中的数据存入 Cookie。服务器响应数据时会把 Cookie 对象响应给浏览器 ,浏览器接收到响应后,会把 Cookie 中的数据存放到浏览器内存中 ,在同一次会话中浏览器再一次发送请求给 Web 服务器资源时 ,会携带 Cookie 对象中的所有数据 。此时就实现了同一会话的不同请求之间的数据共享 。
例如:
如上图,浏览器第一次访问 Web 服务器时 ,会话建立 ,那么如何实现一次会话的多次请求之间的数据共享呢?
浏览器发送 HTTP 请求 1 给服务器 ,服务器 Servlet 1 接收请求并进行业务处理 ,在处理过程中创建 Cookie 对象并将数据 name=zhangsan 存入 Cookie 。响应数据时 ,Cookie 对象被响应到浏览器 ,浏览器接收到响应数据会把 Cookie 对象中的数据存放到浏览器内存中 ,此时会话建立 。
在同一次会话中 ,浏览器发送 HTTP 请求 2 给服务端 Servlet 2 ,此时会携带 Cookie 对象中所有的数据 。 Servlet 2 接收到请求和数据后 ,就可以获取到 Cookie 对象中的数据 ,实现了一次会话中多次请求的数据共享 。
Cookie的基本使用
对于 Cookie 的使用 ,我们主要关注后台代码对 Cookie 的操作,Cookie 的操作主要分为发送 Cookie 和接收 Cookie 。
发送 Cookie:
创建 Cookie 对象:
Cookie cookie = new Cookie("key","value");发送 Cookie 到浏览器:
response.addCookie(cookie);下面练习将 Cookie 发送到客户端浏览器 。
第一步:
创建 Maven Web 项目 ,命名为 cookie-demo ,在 pom.xml 中添加相关依赖 。
<dependencies> <!--servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!--jsp--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> <!--jstl--> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </build>第二步:编写 Servlet,编码:
@WebServlet("/servletA") public class ServletA extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //创建Cookie对象 Cookie cookie = new Cookie("name", "zhangsan"); //通过response发送Cookie对象 response.addCookie(cookie); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }第三步:启动服务器 ,在浏览器中访问对象的服务器资源 。
不难发现 ,此时响应数据中已经携带了 Cookie 的数据 ,如图:
浏览器中存储的 Cookie 数据怎么查看呢?一般情况下我们可以通过浏览器的设置查看 ,在不同的浏览器设置中一般都有 Cookie 选项 ,我们发现其中就存放了很多的 Cookie 数据 。如图:
同样的我们也可以通过 F12 开发者工具查看 ,如图:
获取 Cookie:
获取客户端携带的所有 Cookie:
Cookie[] cookie = request.getCookies();使用 Cookie 对象方法获取数据:
cookie.getName(); cookie.getValue();我们只需要遍历数组对象 ,并且使用 Cookie 对象方法 ,便可以获取每一个 Cookie 对象对应的值 。
下面练习获取浏览器请求中的 Cookie 数据。
第一步:编写一个 Servlet ,命名为 ServletB ,编码:
@WebServlet("/servletB") public class ServletB extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取Cookie数组 Cookie[] cookies = request.getCookies(); //遍历数组对象 for(Cookie cookie:cookies){ //获取数据 String name = cookie.getName(); if("name".equals(name)){ String value = cookie.getValue(); System.out.println(name+":"+value); break; } } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }第二步:在浏览器访问对应的资源 ,此时控制台输出了从浏览器获取的 Cookie 对象的值 。如图:
在一次会话中 ,我们使用浏览器访问了两个不同的资源 ServletA 和 ServletB,并且实现了数据的共享 。当我们关闭浏览器重新启动后访问 ServletB ,此时会发现我们已经无法访问到 name:zhangsan 这条数据了。
Cookie原理分析
Cookie 的实现是基于 HTTP 协议的 ,其中涉及到了下面两个信息:
响应头:set-cookie 请求头:cookie例如在上面的案例中:
我们在前面的案例中使用 request 在 ServletB 中获取到了 Servlet 响应给浏览器的数据,在同一会话的两次请求之间实现了数据共享 。
在 ServletA 响应数据时 ,Tomcat 服务器是基于 HTTP 协议来实现的 ,当 TomCat 发现要响应一个 Cookie 对象时 ,就会在响应头重添加数据:
set-Cookie:name=zhangsan浏览器获取当响应结果后 ,从响应头中获取到对应的值 name=zhangsan ,并将数据存储在浏览器内存中 。
在同一次会话中 ,浏览器发送 HTTP 请求给 ServletB ,浏览器会自动在请求头中添加:
Cookie:name=zhangsan服务器接收到浏览器的请求 ,Request 对象会将请求头中的 cookie 对应的值封装为 Cookie 对象 ,存放在数组中 。此时我们就可以根据需求获取数据 。
例如:
访问 ServletA 时 ,查看响应头数据如下:
访问 ServletB 时 ,查看请求头数据:
Cookie的存活时间
思考:
前面我们通过 ServletA 响应了存放 name=zhangsan 的 Cookie 对象给浏览器 ,浏览器接收响应数据并将 Cookie 数据存放到浏览器内存中,通过浏览器再次发送 HTTP 请求给 ServletB ,并且使用 Response 对象获取了 Cookie 数据 ,那么在向 ServletB 发送请求之前,如果我们将浏览器彻底关闭并重新打开 ,还能通过 ServletB 获取到 Cookie 数据吗?
通过测试不难发现 ,如果将浏览器彻底关闭再次打来访问 ServletB 时 ,我们无法获取 Cookie 对象数据 。
Cookie 的存活时间是指从创建到销毁的整个时间 ,那么 Cookie 对象能存活多久呢?
因为 Cookie 存放在浏览器内存中 ,所以在默认情况下 ,如果浏览器关闭 ,内存释放 ,此时 Cookie 就会被销毁 。这也是为什么案例中我们再次打开浏览器无法获取 Cookie 的原因 。
但是 ,有时我们需要 Cookie 的数据持久化存储 ,例如在实现登录时记住我的功能时 ,我们希望再次打开浏览器访问登录页面时 ,数据能被重新获取 。Cookie 提供了对应的 API 来解决这个问题,我们可以通过 setMaxAge() 方法来设置 Cookie 的存活时间 。
setMaxAge(int seconds)参数为存活的秒数 ,通过设置其参数来控制 Cookie 的存活时间:
正数:将 Cookie 写入浏览器所在的电脑硬盘吗 ,持久化存储,到指定时间后自动删除 负数:默认值 ,存放在浏览器内存中 ,浏览器关闭 ,内存释放 ,Cookie 销毁 零:删除对应的 Cookie示例:
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //创建Cookie对象 Cookie cookie = new Cookie("name", "zhangsan"); //设置Cookie存活时间 cookie.setMaxAge(60*60*24*7); //通过response发送Cookie对象 response.addCookie(cookie); }设置完 Cookie 存活时间后 ,我们重启服务器 ,在浏览器访问 ServletA ,重启浏览器 ,再次访问 ServletB ,此时不难发现 ,Cookie 数据能够被获取到 ,说明 Cookie 并没有因为浏览器内存释放而销毁 。
在浏览器设置中我们也可以看到 ,有些 Cookie 数据甚至设置存放一年之久 。如图:
Cookie存储中文
其实在之前的案例中,如果我们设置的参数值为中文的话 ,在浏览器访问时会被提示错误信息的 ,因为 Cookie 是不能直接存储中文的 。那么如果我们有存储中文的需求时该怎么解决这个问题呢?
这里就又要用到 URL 编码,具体怎么实现呢?
例如上面的例子中 ,在 ServletA 中响应 Cookie 数据时 ,对中文数据进行 URL 编码 ,并且将编码后的数据存放到 Cookie 中 ,在 Servlet 中获取 Cookie 的值时 ,只需要附加的进行 URL 解码 ,变不会出现中文乱码的问题。
示例:
第一步:在 ServletA 中进行 URL 编码:
@WebServlet("/servletA") public class ServletA extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //数据 String s = "张三"; //URL编码 String encode = URLEncoder.encode(s, "utf-8"); System.out.println(encode); //将编码后的数据存放到Cookie中 Cookie cookie = new Cookie("name", encode); //设置Cookie存活时间 cookie.setMaxAge(60*60*24*7); //通过response发送Cookie对象 response.addCookie(cookie); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }访问服务器资源 ,如图:
第二步:在 ServletB 中进行 URL 解码:
@WebServlet("/servletB") public class ServletB extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取Cookie数组 Cookie[] cookies = request.getCookies(); //遍历数组对象 for(Cookie cookie:cookies){ //获取数据 String name = cookie.getName(); if("name".equals(name)){ //获取数据 String value = cookie.getValue(); //URL解码 value = URLDecoder.decode(value, "utf-8"); System.out.println(name+":"+value); break; } } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }访问服务器资源 ,如图:
此时 ,在控制台打印了没有乱码的中文数据 。如图:
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!