会话技术
使用的场景:在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)