会话技术
使用的场景:在web访问的过程中,会话指的就是客户端与服务端之间的通讯过程
HTTP协议无状态性:无状态性体现在在服务器看来,所有的客户端发送过来的请求报文都是完全相同的,服务器压根无法通过HTTP请求报文来区分各个不同的客户端
此时就需要使用Cookie来进行客户端和服务端之间的交流,以此来区分各个不同的客户端
会话技术主要是用来帮助服务端给客户端保存相关数据的
Cookie
客户端技术。客户端技术是指的是数据的存储、保存是在客户端进行的。
数据的产生是在服务器产生的,服务器在做出响应时,会把cookie信息返回给客户端(set-Cookie:key=value响应头),客户端接收到该信息之后,会将该cookie保存下来
当它再次访问服务器时,那么就会把该cookie给再次携带给服务器(Cookie:key=value请求头),通过这种方式,就可以知道请求来自于哪个客户端了。
Cookie本质上是一个响应头,数据是放在响应头中的
Cookie可以作用在服务器上的多个页面中
Cookie的值要求必须是字符串且不能包含空格
使用
使用的三个步骤
1.生成cookie对象
2.利用response.addCookie(cookie)将cookie发送给客户端
3.利用request.getCookies()可以接收cookie
在获取cookie的时候一定要注意判空!!
cookie有很多个,具体在使用的时候也需要使用if判断,否则报错
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //每次服务器接收浏览器的请求的时候,都应该先获取cookie Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { System.out.println(cookie.getName() + ":" + cookie.getValue()); } } Cookie cookie = new Cookie("forrr", "haha");//新建一个cookie用于返回给客户端 response.addCookie(cookie); } }
案例
登录案例:用户通过登录页面进行登录,登录成功之后进入一个新的页面, 要求可以显示出用户的用户名
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //这里面需要获取cookie并转发到欢迎页面 //首先要获取到请求参数 request.setCharacterEncoding("utf-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); //新建cookie Cookie cookie = new Cookie("username", username); //将cookie发送给客户端 response.addCookie(cookie); //跳转页面 response.setHeader("refresh","2;url="+request.getContextPath()+"/infoc"); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //这里只需要获取cookie然后进行判断即可 response.setContentType("text/html;charset=utf-8"); Cookie[] cookies = request.getCookies(); if (cookies!=null){ for (Cookie cookie : cookies) { if ("username".equals(cookie.getName())){ //如果cookie的名字是username,那么就说明是用户名,输出即可 response.getWriter().println("登陆成功"+ cookie.getValue()); } } } }
显示用户上次访问时间:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //因为cookie不能含有空格,那么这里的思路就是将当前时间的毫秒数设置到cookie中,显示的时候就转换成data //访问的时候也是get请求,获取cookie Cookie[] cookies = request.getCookies(); if (cookies!=null){ for (Cookie cookie : cookies) { if ("date".equals(cookie.getName())){ //因为cookie有很多个,那要找到是date的那个 String value = cookie.getValue(); Date date = new Date(Long.parseLong(value)); response.getWriter().println(date); } } } //先写设置cookie的逻辑 Cookie cookie = new Cookie("date", System.currentTimeMillis() + ""); response.addCookie(cookie); }
设置
设置存活时间
默认情况下,如果没有设置,则cookie的有效期是在浏览器开启的这段时间内有效(也就是存活在浏览器的内存中),浏览器关闭则失效;如果希望cookie可以持久化保存,则可以设置一个MaxAge=正数,表示的是在硬盘上存活多少秒
设置负数,其实就是默认情况,存在于浏览器内存中
设置为0就表示删除cookie
删除cookie
设置MaxAge=0,然后将该设置发送给客户端,一定要发送,否则无效
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //访问5次后就删除cookie Cookie[] cookies = request.getCookies(); if (cookies!=null){ for (Cookie cookie : cookies) { if ("date".equals(cookie.getName())){ count++; //因为cookie有很多个,那要找到是date的那个 String value = cookie.getValue(); Date date = new Date(Long.parseLong(value)); response.getWriter().println(date); if (count==5){ cookie.setMaxAge(0); response.addCookie(cookie); return; } } } } //先写设置cookie的逻辑 Cookie cookie = new Cookie("date", System.currentTimeMillis() + ""); cookie.setMaxAge(180); response.addCookie(cookie); }
设置路径
设置cookie的有效路径
默认情况下,访问当前服务器的所有资源时都会携带cookie,可以设置一个路径,仅当访问指定路径时才会携带cookie
如果设置了path,再次去删除cookie,那么此时需要注意,在删除时,必须要把path在写一遍,否则无法删除
如果cookie没有设置path,那么直接设置MaxAge=0即可
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie cookie = new Cookie("name", "zs"); cookie.setPath(request.getContextPath()+"/path2"); response.addCookie(cookie); }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if ("name".equals(cookie.getName())){ String value = cookie.getValue(); response.getWriter().println(value); cookie.setMaxAge(0); cookie.setPath(request.getContextPath()+"/path2"); response.addCookie(cookie); } } } }
设置域名
客户端对于设置指定域名的cookie是有限制的。你不可以设置和当前域名无关的cookie
比如当前域名是localhost,你设置了一个域名是baidu.com的cookie,不允许的(安全性考虑)
但可以进行父子类名的设定
ccc.com
xxxx.ccc.com
比如,你当前所在的系统是在ccc.com,你设置了一个域名是ccc.com的cookie
紧接着你去访问sub.ccc.com,那么此时多个域名的系统之间可以共享当前的cookie信息
好处是可以运用在一个集团的系统中
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie cookie = new Cookie("domain", "fh.com"); cookie.setDomain("fh.com"); response.addCookie(cookie); }
cookie优缺点
优点:小巧、减轻了服务器压力
缺点:容量有限制、存储字符串、只能够存储一些非敏感数据
Session
服务器技术。
数据的产生以及数据的存储均是在服务器上面完成的。相当于一个客户端和一个session对象进行绑定。
只要是同一个客户端访问不同的servlet,都可以拿到同一个session对象,session就可以作为一个共享数据的场所。
但是由于HTTP协议的无状态性,服务器无法识别不同的客户端,于是就需要借助于Cookie,服务器使用cookie将JESESSIONID传给客户端,客户端下次访问的时候带着这个id,这样就可以拿到同一个session对象
使用
拿到session对象
HttpSession getSession()
如果当前请求有关联的session对象,那么返回;如果没有关联的对象,就需要创建一个;必须携带一样的id才会返回
HttpSession getSession(boolean create)
如果当前请求有关联的session对象,那么返回;如果没有关联的session对象,并且create是true,则创建一个,如果是false,则返回null。
如何判断当前请求有没有关联的session对象?
仅凭请求报文中有没有携带Cookie:JSESSIONID=xxxx(key一定要求是固定的写法)
如果自定义一个session可能会有问题,必须使用session.getId()
因为必须要使用相同的ID来获取同一个session对象,如果是自定义的会导致id不同
如果不使用request.getSession,那么是不会创建对象的,本质是这个原因
Session的执行过程
第一次访问request.getSession会创建一个新的Session对象
第二次再访问则不会创建对象
关闭浏览器之后再次访问会创建新的session对象,因为此时请求头中没有用携带的cookie了,关闭浏览器会使cookie失效
利用session对象进行数据存取
HttpSession session = request.getSession(); session.setAttribute("username", "zhangsan");
取出数据
Object username = session.getAttribute("username");
三个域的区别
Context:存储一些全局性的数据,服务器上的任何客户端都可以访问
Session:存一些用户的数据,同一个客户端之间数据共享,不同的客户端不能共享,通过cookie中的JESSIONID来判断
request:某一次请求时需要用到,后面就不用了,只有转发的两个组件之间可以共享,其他的不行
问题一:关闭浏览器,session对象会销毁吗?数据能访问到吗?
没有,数据访问不到了。
生成了一个新的session对象,使得原先的session以及数据不可达。
问题二:关闭服务器,session对象会销毁吗?数据能访问到吗?
销毁了后依然可以访问到,不能通过idea来验证,原因是idea的tomcat在重启的时候会将原先的tomcat相关文件全部删除,然后重新读取新的tomcat配置文件
使用tomcat管理器来关闭应用
1.本地tomcat的webapps下需要有maanger应用
2.本地tomcat conf/tomcat-users.xml文件配置
<role rolename="manager-gui"/> <user username="tomcat" password="tomcat" roles="manager-gui"/>
重启服务器之后,session的地址发生了变化,但是session的id以及数据均没有变化。
服务器关闭时,会将session的相关信息全部序列化到本地硬盘上面
下次再次启动时,服务器会重新读取序列化文件里面的数据,生成新的session对象,将文件里面的信息全部注入到新的session对象中。
如何使用本地tomcat来部署我们idea里面的web应用
复制idea中的映射地址即可
购物车案例
主页
package com.fh.Session.cart; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.List; @WebServlet(value = "/index",loadOnStartup = 1) public class IndexServlet extends HttpServlet { @Override public void init() throws ServletException { //初始化商品列表 Product p1 = new Product("1", "西瓜"); Product p2 = new Product("2", "梨子"); Product p3 = new Product("3", "桃子"); Product p4 = new Product("4", "香蕉"); List<Product> list = new ArrayList<>(); list.add(p1); list.add(p2); list.add(p3); list.add(p4); getServletContext().setAttribute("products",list); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); List<Product> products = (List<Product>) getServletContext().getAttribute("products"); for (Product product : products) { //循环打印出商品的列表,根据不同的商品渲染页面 response.getWriter().println("<div><a href='" + request.getContextPath()+"/product?id="+product.getId()+ "'>"+product.getName()+"</a></div>"); } } }
详情页
package com.fh.Session.cart; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @WebServlet("/product") public class ProductServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); //获取请求参数,判断不同的商品 String id = request.getParameter("id"); if (id==null||"".equals(id.trim())){ response.getWriter().println("参数不合法"); return; } //获取商品列表,判断该id对应的是哪个商品,然后再生成页面 List<Product> products = (List<Product>) getServletContext().getAttribute("products"); HttpSession session = request.getSession(); LinkedList<String> footprint = (LinkedList<String>) session.getAttribute("lastView"); //获取历史足迹中的商品列表 if (footprint==null){ //如果是null,那么就要创建 footprint = new LinkedList<>(); session.setAttribute("lastView",footprint); } for (Product product : products) { //查看当前商品 if (id.equals(product.getId())){ response.getWriter().println(product); } } //查看完毕后,添加进list中 if (footprint.contains(id)){ footprint.remove(id); }else { if (footprint.size()==2){ footprint.removeLast(); //删除最后一个,因为每次都要放在最新的 } } footprint.addFirst(id); response.getWriter().println("<a href='" + request.getContextPath() + "/index" + "'>返回首页</a>"); response.getWriter().println("<a href='" + request.getContextPath() + "/addCart?id=" + id + "'>加入购物车</a>"); response.getWriter().println("<a href='" + request.getContextPath() + "/viewCart" + "'>查看购物车</a>"); response.getWriter().println("<a href='" + request.getContextPath() + "/lastView" + "'>查看历史足迹</a>"); } }
添加购物车
package com.fh.Session.cart; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.ArrayList; import java.util.List; @WebServlet("/addCart") public class AddCartServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); //获取请求参数,判断不同的商品 String id = request.getParameter("id"); if (id==null||"".equals(id.trim())){ response.getWriter().println("参数不合法"); return; } HttpSession session = request.getSession(); //必须先取出session中的list,如果不去除的话,就会导致每次都重复创建list覆盖 List<String> cart = (List<String>) session.getAttribute("cart"); if (cart==null){ //如果此时的购物车list是null,那么就需要创建,否则就不执行创建,也就不存在覆盖的问题 cart = new ArrayList<>(); session.setAttribute("cart",cart); } cart.add(id);//每次将商品的id添加进去即可 } }
查看购物车
package com.fh.Session.cart; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.List; @WebServlet("/viewCart") public class ViewCartServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); HttpSession session = request.getSession(); List<String> cart = (List<String>) session.getAttribute("cart"); if (cart==null){ response.getWriter().println("购物车为空"); return; } List<Product> products = (List<Product>) getServletContext().getAttribute("products"); for (String id : cart) { for (Product product : products) { if (id.equals(product.getId())){ response.getWriter().println(product); } } } } }
查看历史记录
package com.fh.Session.cart; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @WebServlet("/lastView") public class LastServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); HttpSession session = request.getSession(); List<Product> products = (List<Product>) getServletContext().getAttribute("products"); LinkedList<String> footprint = (LinkedList<String>) session.getAttribute("lastView"); if (footprint==null){ response.getWriter().println("历史足迹为空!"); } for (String id : footprint) { for (Product product : products) { if (id.equals(product.getId())) { response.getWriter().println(product.getName()); } } } } }
思路:判断是否在历史足迹中—>有就删除---->判断是否已满---->满了就删除最久远的,因为我们是头插法,所以直接删除尾部元素---->将新元素添加到头部
session生命周期
创建:
对象创建是通过第一次调用request.getSession()来创建的
数据的创建:
通过调用setAttribute来放入session域
销毁:
对象的销毁是当应用卸载、服务器关闭
数据的销毁:
session对象的销毁不会导致数据的销毁。
数据的销毁只和以下情况有关:
1.session有效期到达(默认情况下,tomcat默认配置是30min),30分钟这个session没有被访问,就会删除
2.主动调用session.invalidate()方法
调用removeAttribute()(该方法只是把session里面的某个键值对清空,不是清空整个session)
禁用cookie之后的策略
session底层依赖于cookie,如果cookie禁用之后,session可以采用URL重写的方式(主流的网站都要求打开cookie)