会话技术
会话,现实生活中会话的含义:交谈、谈话。
在web访问过程中,会话代表什么意思呢?
浏览器打开一个网站,然后访问当前网站的诸多页面,最后关闭,整个的过程叫做一个会话。
HTTP协议,是一个无状态协议。在服务器看来,任何浏览器发送过来的请求数据都是完全相同的,一模一样,服务器是无法却别该请求来自于哪个浏览器。
比如:http://192.168.2.100/app/index.html
A和B用户同时访问了当前页面,发送的HTTP请求报文是完全相同的,对于服务器来说,它是无法区别该请求来自于a还是b
实际场景下,又有这样的需求,我们需要服务器可以给我们浏览器去保存一些数据,比如浏览记录、购物车
就会涉及到我们今天介绍的技术,会话技术。
会话技术i其实是为了解决HTTP协议无状态性而产生的一个解决方案。
会话技术可以分为两种,一个是客户端技术,一个是服务器技术。
客户端技术Cookie
Cookie是由服务器创建的,然后接下来,Cookie会被发送给客户端(响应报文,响应头 Set-Cookie:name=value),客户端会将cookie信息保存下来,下次再次访问服务器时,它会把整个cookie重新带回去给服务器(请求报文,请求头 Cookie:name=value),服务器接收到cookie(又由于cookie的值是唯一的),所以就可以利用cookie来进行会话管理。
服务器技术Session
Cookie
package com.cskaoyan.cookie;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;import java.io.IOException;import java.util.Date;@WebServlet("/cookie1")public class CookieServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//接收cookie的代码应该写在上面Cookie[] cookies = request.getCookies();if(cookies != null){for (Cookie cookie : cookies) {if("lastVisit".equals(cookie.getName())){String value = cookie.getValue();Date date = new Date(Long.parseLong(value));response.getWriter().println(date);}}}//在内部调用了getHeaders(Cookie)//显示用户的上次访问时间//生成cookie cookie的value值里面不能有空格Cookie cookie = new Cookie("lastVisit", System.currentTimeMillis() + "");// 以Set-Cookie响应头发送给客户端//response.setHeader("Set-Cookie", "latLogin=xxx");//EE规范同样给我们封装了一个简便的设置cookie的方法response.addCookie(cookie);//addCookie{// setHeader(Set-cookie, cookie.getName = cookie.getValue)// }}}
设置存活时间
cookie默认情况下只存在于浏览器的内存中,关闭浏览器,则cookie失效,如果希望cookie能够进行持久化保存,那么可以设置一个存活时间
Cookie cookie = new Cookie("username", username);//单位是秒cookie.setMaxAge(180);
设置一个以秒为单位的时间(正数),表示它会在硬盘上存活多久,如果设置的是负数,和不设置是一样的
0表示的是删除cookie
package com.cskaoyan.cookie;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;import java.io.IOException;@WebServlet("/login")public class LoginServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setCharacterEncoding("utf-8");response.setContentType("text/html;charset=utf-8");String username = request.getParameter("username");String password = request.getParameter("password");//假设登录成功,那么需要将用户名放入到cookie中Cookie cookie = new Cookie("username", username);//单位是秒cookie.setMaxAge(180);response.addCookie(cookie);response.getWriter().println("登录成功,即将跳转至个人主页....");response.setHeader("refresh", "2;url=" + request.getContextPath() + "/info");}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}}
package com.cskaoyan.cookie;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;import java.io.IOException;@WebServlet("/info")public class InfoServlet 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");Cookie[] cookies = request.getCookies();if(cookies != null){for (Cookie cookie : cookies) {if("username".equals(cookie.getName())){response.getWriter().println("欢迎您," + cookie.getValue());//0表示删除cookiecookie.setMaxAge(0);response.addCookie(cookie);}}}}}
设置路径
默认情况下,当访问当前服务器下面所有的资源时,默认都会携带cookie,如果希望仅某些页面去携带cookie,那么可以设置一个path
package com.cskaoyan.cookie.path;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;import java.io.IOException;@WebServlet("/path1")public class PathServlet1 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {Cookie cookie = new Cookie("key", "path");//直接写/应用名开头的路径cookie.setPath(request.getContextPath() + "/path2");response.addCookie(cookie);}}
package com.cskaoyan.cookie.path;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;import java.io.IOException;@WebServlet("/path2")public class PathServlet2 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//如果cookie设置了path,这个时候希望将cookie删除,那么可以怎么做呢//需要特别注意一点,在删除cookie的时候,有一个注意事项,如果当前cookie设置了path//那么删除cookie的时候,需要再将path再写一遍//注意:删除cookie时浏览器删除Cookie[] cookies = request.getCookies();if(cookies != null){for (Cookie cookie : cookies) {if("key".equals(cookie.getName())){response.getWriter().println(cookie.getValue());cookie.setMaxAge(0);cookie.setPath(request.getContextPath() + "/path2");response.addCookie(cookie);}}}}}
设置域名
注意:cookie有一个大的原则,不可以设置和当前域名无关的cookie,比如当前域名localhost,然后你设置了一个域名叫做cskaoyan.com的cookie,那么其实时设置不成功的。
aaa.com ——— 你申请的
sub.aaa.com ———- 这个域名是属于你的
third.sub.aaa.com————这个域名也是属于你的
父子域名
如果你设置了一个aaa.com域名的cookie,那么默认情况下,下面所有的子域名都可以共享该cookie
package com.cskaoyan.cookie.domain;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;import java.io.IOException;@WebServlet("/domain2")public class DomainServlet2 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {Cookie cookie = new Cookie("user", "aaa.com");cookie.setDomain("aaa.com");response.addCookie(cookie);}}
cookie的优缺点
优点:小、轻便、放在浏览器,服务器没有压力
缺点:存储数据比较少,value值只能是字符串类型,有限制;安全问题;
cookie其实可以用来存放一些非敏感性数据,
Session
服务器技术。session对象是在服务器产生的,但是呢,保存也是在服务器上面。
session你可以认为是服务器给每个浏览器所开辟的一块内存空间,将这块内存空间和浏览器做一个绑定,如果今后当前浏览器再次访问服务器,如果需要进行数据的存取,那么就放置在当前内存空间中。
浏览器在访问session的时候,session会给浏览器创建一个独一无二的session对象,session对象有一个id,会把session的id加上响应报文中发送给客户端,客户端接收到之后将其保存,下次再次访问服务器时,将该id再携带回来,服务器就可以取出session的id值,那么就可以拿到原先的session对象。
只需要记住一点,每个浏览器拿到的都是不同的session对象。
创建session

本质:请求头中是否有Cookie:JSESSIONID=XXXX
第一次访问,请求头中肯定没有Cookie:JSESSIONID=xxxx,所以执行到request.getSession的时候,那么就会创建一个session对象,同时以set-Cookie:JSESSIONID=xxxx将session的id返回给浏览器,浏览器会将id保存下来
第二次访问,请求头中会携带Cookie:JSESSIONID=xxxx,所以执行到request.getSession的时候,(根据session的id去找到对应的session对象),就i把当前的session对象返回。
关闭服务器,session对象会销毁吗
肯定会。
关闭浏览器,session对象会销毁吗
没有销毁。此时session对象类似不可达。不可达的原因在于cookie请求头默认情况下只存在浏览器的内存中,关闭浏览器则失效,
JSESSIONID就会丢失。
session的使用
存取数据。
session域。
关闭服务器,重启服务器,还可以访问到原先session中的数据吗
注意:不要使用idea的tomcat关闭来验证,否则你将得不到正确的结论。
还可以进一步验证,比如使用本地的tomcat来部署我们的应用。
需要使用tomcat自带的管理系统来将应用给卸载。
在本地安装的tomcat conf/tomcat-users.xml文件中增加如下设置
package com.cskaoyan.session;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;@WebServlet("/session1")public class SessionServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//如何去创建一个session呢?HttpSession session = request.getSession();session.setAttribute("username", "zhangsan");System.out.println(session.getId());System.out.println(session);}}
package com.cskaoyan.session;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;@WebServlet("/session2")public class SessionServlet2 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//如何去创建一个session呢?HttpSession session = request.getSession();String username = (String) session.getAttribute("username");System.out.println(session.getId());System.out.println(session);System.out.println(username);}}

最终session里面的数据还是存在的,还可以访问到。session的地址发生了变化,但是session的id没有变。
秽土转生。
session在临死之前,它会序列化到本地硬盘上面(把里面的id、attributes属性全部序列化到本地硬盘上面),形成一个文件,当应用重新被启动的时候,读取文件,将里面的数据重新读取到内存中,会创建新的session,将这些id、属性值全部赋值给新的session对象。
关闭浏览器,如果我希望还可以访问到原先session中的数据,应该怎么办
package com.cskaoyan.session;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.*;import java.io.IOException;@WebServlet("/session3")public class SessionServlet3 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {HttpSession session = request.getSession();session.setAttribute("username", "sssss");Cookie cookie = new Cookie("JSESSIONID", session.getId());cookie.setMaxAge(180);response.addCookie(cookie);}}
package com.cskaoyan.session;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;@WebServlet("/session4")public class SessionServlet4 extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {HttpSession session = request.getSession();String username = (String) session.getAttribute("username");System.out.println(username);}}
session域
Context、Request、Session
Context > Session > Request
使用:
Context域:范围最大的一个域。和用户无关的。所以可以用来存放一些和用户无关的,所有用户都相同的数据,比如商城的商品分类
Session域:可以用来存放用户相关的数据。比如用户的购物车数据、用户的浏览记录、用户名等
Request域:非常的小,如果希望在一次请求中用到该数据,那么就可以使用request域,让转发的两个组件之间进行共享该数据。
session的生命周期
创建:对象的创建(request.getSession())
销毁:对象的销毁(应用被卸载、服务器关闭)
数据赋值:setAttribute、getAttribute、removeAttribute
对象的销毁并不意味着数据的销毁,如果希望数据销毁只有两种方式:
1.主动调用session.invalidate();
2.session默认有效期到达,session也会失效 如果30min内没有访问当前session,那么session失效,如果在30min内访问了一次session,那么重新开始计时
package com.cskaoyan.session;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;@WebServlet("/login")public class LoginServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");String username = request.getParameter("username");String password = request.getParameter("password");request.getSession().setAttribute("username", username);response.getWriter().println("登录成功,跳转中......");response.setHeader("refresh", "2;url=" + request.getContextPath() + "/info");}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}}
package com.cskaoyan.session;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;@WebServlet("/info")public class InfoServlet 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 username = (String) request.getSession().getAttribute("username");response.getWriter().println("欢迎您 " + username + "<a href='" + request.getContextPath() + "/logout" + "'>点我注销</a>");}}
package com.cskaoyan.session;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;@WebServlet("/logout")public class LogoutServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//注销的逻辑应该怎么写?request.getSession().invalidate();}}
重构到一个servlet中。因为我们发现这些servlet都是在说用户相关,所以可以放到一个servlet中。
package com.cskaoyan.session;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;@WebServlet("/user")public class UserServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String op = request.getParameter("op");if("login".equals(op)){login(request, response);}}private void login(HttpServletRequest request, HttpServletResponse response) throws IOException {response.setContentType("text/html;charset=utf-8");String username = request.getParameter("username");String password = request.getParameter("password");request.getSession().setAttribute("username", username);response.getWriter().println("登录成功,跳转中......");response.setHeader("refresh", "2;url=" + request.getContextPath() + "/user?op=info");}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String op = request.getParameter("op");if("info".equals(op)){info(request, response);}else if("logout".equals(op)){logout(request, response);}}private void logout(HttpServletRequest request, HttpServletResponse response) {request.getSession().invalidate();//跳转}private void info(HttpServletRequest request, HttpServletResponse response) throws IOException {response.setContentType("text/html;charset=utf-8");String username = (String) request.getSession().getAttribute("username");response.getWriter().println("欢迎您 " + username + "<a href='" + request.getContextPath() + "/user?op=logout" + "'>点我注销</a>");}}
购物车案例
package com.cskaoyan.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 {@Overridepublic void init() throws ServletException {//初始化商品信息Product iphone12 = new Product("1", "iphone12", 6999.0);Product mate40 = new Product("2", "huawei mate40", 6699.0);Product product = new Product("3", "mi 11 pro", 5999.0);List<Product> products = new ArrayList<>();products.add(iphone12);products.add(mate40);products.add(product);getServletContext().setAttribute("products", products);}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//初始化商品信息List<Product> products = (List<Product>) getServletContext().getAttribute("products");for (Product product : products) {response.getWriter().println("<a href='" + request.getContextPath() + "/detail?id=" + product.getId() + "'>" + product.getName() +"</a>");}}}
package com.cskaoyan.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.List;@WebServlet("/detail")public class DetailServlet 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 || id.isEmpty()){response.getWriter().println("invalid parameter");return;}response.getWriter().println("<!DOCTYPE html>\n" +"<html lang=\"en\">\n" +"<head>\n" +" <meta charset=\"UTF-8\">\n" +" <title>Title</title>\n" +"</head>\n" +"<body>");List<Product> products = (List<Product>) getServletContext().getAttribute("products");for (Product product : products) {if(product.getId().equals(id)){response.getWriter().println(product);}}String index = request.getContextPath() + "/index";String addCart = request.getContextPath() + "/addCart?id=" + id;String viewCart = request.getContextPath() + "/viewCart";response.getWriter().println("<a href='" + index + "'>返回首页</a>");response.getWriter().println("<a href='" + addCart + "'>加入购物车</a>");response.getWriter().println("<a href='" + viewCart + "'>查看购物车</a>");response.getWriter().println("</body>\n" +"</html>");}}
package com.cskaoyan.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("购物车为空,去选购吧.....");String index = request.getContextPath() + "/index";response.setHeader("refresh", "2;url=" + index);return;}List<Product> products = (List<Product>) getServletContext().getAttribute("products");for (Product product : products) {for (String id : cart) {if(id.equals(product.getId())){response.getWriter().println(product);}}}String index = request.getContextPath() + "/index";response.getWriter().println("<a href='" + index + "'>返回首页</a>");}}
package com.cskaoyan.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 || id.isEmpty()){response.getWriter().println("invalid parameter");return;}HttpSession session = request.getSession();List<String> cart = (List<String>) session.getAttribute("cart");if(cart == null){cart = new ArrayList<>();session.setAttribute("cart", cart);}cart.add(id);response.getWriter().println("添加到购物车成功....");String index = request.getContextPath() + "/index";response.setHeader("refresh", "2;url=" + index);// List<String> list = new ArrayList<>();// list.add(id);//不能用set,因为永远只有一个 session.setAttribute("id", id);}}
package com.cskaoyan.cart;public class Product {private String id;private String name;private Double price;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Double getPrice() {return price;}public void setPrice(Double price) {this.price = price;}public Product(String id, String name, Double price) {this.id = id;this.name = name;this.price = price;}public Product() {}@Overridepublic String toString() {return "Product{" +"id='" + id + '\'' +", name='" + name + '\'' +", price=" + price +'}';}}
session依赖于cookie,如果cookie禁用会怎么样
EE规范提供了一个URL重写方法,此时JSESSIONID会附着在地址栏的后面
今晚可以做的
1.提到了数量问题
2.实现历史足迹。
