(五)Cookie和Session_JavaEE_学习笔记

一、会话技术

在日常生活中,对话要正常进行下去,前提是什么,知道说的上下文是什么?
Web访问过程中,也存在这个问题。对于一个标准的http请求来说,是没有状态的,也没有上下文。
http无状态:任何的http请求在服务器看来都是完全一模一样的,彼此之间没有任何区别。
因为http协议的无状态性,所以它无法进行一个会话的管理。
为了实现让服务器实现会话管理,需要引入一些会话技术。
引入会话技术的目的:可以给浏览器存储一些数据,购物车信息、浏览记录,存储登录状态。

问题:什么是会话?
会话可简单理解为:用户开一个浏览器,点击多个超链接,访问同一个服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。

会话过程中要解决的一些问题?
每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。
例如:用户点击超链接通过一个servlet购买了一个商品,程序应该想办法保存用户购买的商品,以便于用户点结帐servlet时,结帐servlet可以得到用户购买的商品为用户结帐。

多个页面共享一份数据,就可以引入会话技术。
保存会话数据的两种技术:cookie和session。

二、cookie

Cookie是客户端(浏览器)技术,服务器程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去访问。这样,WEB服务器就能通过cookie去识别用户了。web资源处理的就是用户各自的数据了。
在这里插入图片描述

过程描述

对于服务端需要做的事情:

  1. 生成cookie信息
  2. 将cookie以set-cookie响应头的形式发送出去
  3. 浏览器再次请求时,服务器需要把cookie请求头里面的数据取出来

注意:cookie是一个class
常用API:

@WebServlet("/cookie1")
public class ServletCookie1 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");
        //3.从请求头中取出cookie信息
        //request.getHeaders(key, value) 这个可以实现,但是也没有必要
        //request已经提供好了现存的API来供你使用
        //其中的value必须是string类型,并且cookie的value值里面不能有空格
        Cookie[] cookies = request.getCookies();
        if(cookies != null){
            for (Cookie cookie : cookies) {
                if("username".equals(cookie.getName())){
                    response.getWriter().println(cookie.getValue());
                }
            }
        }
        //1.生成cookie
        Cookie cookie = new Cookie("username", "zhangsan");
        //2.将cookie以set-Cookie响应头的形式发送出去
        //response.setHeader(); 可以通过这种方式去实现,但是没有必要
        //response已经给你想好了,提供了封装好的API
        response.addCookie(cookie);
    }
}

第一次去访问当前servlet
在这里插入图片描述
第二次刷新页面
在这里插入图片描述
案例1:显示上次访问当前页面的时间

@WebServlet("/last")
public class LastLoginServlet 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");
        //3.从请求头中取出cookie信息
        //request.getHeaders() 这个可以实现,但是也没有必要
        //request已经提供好了现存的API来供你使用
        Cookie[] cookies = request.getCookies();
        if(cookies != null){
            for (Cookie cookie : cookies) {
                if("lastlogin".equals(cookie.getName())){
                    String value = cookie.getValue();
                    //将long类型的字符数字转成对应的数字
                    Date date = new Date(Long.parseLong(value));
                    response.getWriter().println(date);
                }
            }
        }
        //1生成cookie  cookie的value值里面不能有空格
        Cookie cookie = new Cookie("lastlogin", System.currentTimeMillis() + "");
        //2.将cookie以set-Cookie响应头的形式发送出去
        //response.setHeader(); 可以通过这种方式去实现,但是没有必要
        //response已经给你想好了,提供了封装好的API
        response.addCookie(cookie);
    }
}

在这里插入图片描述
案例2:用两个浏览器来模拟两个用户登录一个页面,登录成功以后,显示出用户名

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/login" method="post">
    <input type="text" name="username"><br>
    <input type="submit">
</form>
</body>
</html>

LoginServlet

package cookie.login;

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 {
        response.setContentType("text/html;charset=utf-8");

        String username = request.getParameter("username");

        Cookie cookie = new Cookie("name", username);
        cookie.setMaxAge(60 * 2);
        response.addCookie(cookie);
        response.setHeader("refresh", "2;url=/info");
        response.getWriter().println("欢迎,正在跳转登录成功页面");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

InfoServlet

package cookie.login;

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 ("name".equals(cookie.getName())){
                    //cookie.setMaxAge(0);
                    response.addCookie(cookie);
                    response.getWriter().println("欢迎您," + cookie.getValue());
                }
            }
        }

    }
}

分别在两个浏览器进行访问,输入不同的用户名
在这里插入图片描述
在这里插入图片描述
2.3.cookie设置
2.3.1设置保存时间

日常生活中我们会发现,有的登录凭证过一段时间以后失效了,有的登录凭证时间非常长。登录凭证的有效时长(其实就是cookie的存活时间)是可以设置的。
通过
cookie.setMaxAge()
语句进行设置,单位为秒。
如果没有设置,则默认是负数,表示的是仅存在于浏览器内存中,关闭浏览器则失效。
设置正数,表示cookie可以在硬盘中存活多少秒。

2.3.2删除cookie
cookie类没有提供单独的删除方法,但可以通过设置setMaxAge=0进行删除cookie。要想删除成功,一定要写回去,即response.addCookie(cookie)
在这里插入图片描述
删除cookie时,如果没有设置path,那么删除时也不需要去设置
但是如果设置了path,那么删除时需要设置和生成时一样的path。
在这里插入图片描述

2.3.3设置路径
比如我想设置访问某些路径会携带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("path","path1");
        //路径的写法:浏览器看的,必须要加应用名
        cookie.setPath(request.getContextPath() + "/path2");
        response.addCookie(cookie);
    }
}
package 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[] cookies = request.getCookies();
        if (cookies != null){
            for (Cookie cookie : cookies){
                if ("path".equals(cookie.getName())){
                    cookie.setPath(request.getContextPath() + "/path2");
                    response.addCookie(cookie);
                    String value = cookie.getValue();
                    response.getWriter().println(value);
                }
            }
        }
    }
}

第一次访问path1
在这里插入图片描述
第二次访问path1
在这里插入图片描述
此时不会携带cookie,
接下来去访问path2
在这里插入图片描述
发现携带了cookie。

2.3.4设置domain
域名。总体原则:不可以设置无关的域名。浏览器是不允许的。
比如localhost设置了一个baidu.com的域名,出于安全性考虑,设置不成功的。
在这里插入图片描述
跨域,cookie是不允许发送及保存的。
端口号不同,也算跨域。比如,Localhost:8080 localhost:80 也算跨域。

父子域名之间存在一个规则:
baidu.com 一级域名
video.baidu.com 二级域名
story.video.baidu.com 三级域名

父子域名之间存在着如下cookie规律:
设置了一个父域名的cookie baidu.com
那么接下来它的二级和三级子域名均可以拿到该cookie。

设置父子域名。
在这里插入图片描述
访问父域名。查看cookie
在这里插入图片描述
接下来,去访问子域名,看一下能否带上cookie,注意此时不要再去访问servlet。
Video.zs.com
Story.video.zs.com
在这里插入图片描述
在这里插入图片描述

三、session

session是一个服务端技术。服务器给用户的浏览器开辟一块内存空间session对象,那么这个session对象就是给某个浏览器来服务的。关联方式:session对象的id值通过set-Cookie响应头发送给浏览器,浏览器保存下来,接下来再次访问时,会携带该id,服务器取出id值,然后找到这个id对应的session对象。

3.1 session的使用
如何创建session对象?
Request里面提供了两个API:
在这里插入图片描述
在这里插入图片描述
tips:写代码先理清思路,可以画流程图,再去写代码。

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 ServletSession1 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执行的整个流程,告诉你session的逻辑,对于我们来说
        //只需要去调用这一个API,那么服务器会自动帮我们把全部的过程全部执行完
        System.out.println(session);
        System.out.println(session.getId());
    }
}

3.2 session的执行流程
第一次访问,遇到了request.getSession(),先判断有没有session对象,(怎么判断有没有?判断请求头中有没有cookie:JSESSIONID=XXXXXX,有效的cookie头),没有则创建一个session对象,同时将session的id值通过set-Cookie响应头发送给客户端。
第二次访问,遇到了request.getSession(),客户端此时已经携带了cookie:JSESSIONID=xxxxx,所以此时直接将关联的session对象返回,结束。

返回的session对象就是给你当前的浏览器来服务的。你访问多个servlet,那么这几个servlet就可以在session中进行数据的存取。

三种域:
context域:对应应用,自始至终只有一个,范围最大。
session域:对应用户,每个浏览器都有一个,范围其次。
request域:每个请求都有一个,范围最小。

操作session的API:
setAttribute(key,value)
getAttribute(key)

3.4 问题一:关闭浏览器,session会销毁吗?
不会。
还可以取得到原来的数据吗?
取不到。

3.5 问题二:关闭服务器,session对象会销毁吗?
会。
数据会被销毁吗?
数据不会被销毁。

在这里插入图片描述
在这里插入图片描述
从上面的结果可以看出应用重启后session的地址和id值都发生了改变,但是session里面的数据却是一样的。
得出结论:在应用被卸载的时候,session对象的确被销毁了,但是里面的数据并没有丢失。当应用被停止之前,session里面的数据会以序列化的方式保存在硬盘中,当重启应用时,又会重新读取文件里面的数据到内存中,会创建一个新的session对象,然后将原先的session的id和数据全部赋值给新的session。

序列化文件在哪?
在这里插入图片描述
如果采用的是重新部署来验证,可能你会得到相反的结果,原因在于idea在每次启动的时候,都会到tomcat的目录去复制对应的配置文件,而把原先的配置文件删除,所以此时session.ser文件时被删除了,而不是读取到内存中。其实是idea自身的问题。
用本地的tomcat直接部署你的idea应用。这个时候也是可以得到相同结论的。

3.5.1 配置tomcat的manager
//localhost/manager
本地安装的tomcat里面的webapps目录manager应用不要删除。
Tomcat的conf/tomcat-users.xml文件中配置节点
< role rolename=“manager-gui”/>
< user username=“tomcat” password=“tomcat” roles=“manager-gui”/>

3.6 session的生命周期
Session对象的创建:request.getSession()。
关闭服务器会销毁session对象,但是里面的数据不会丢失。
关闭浏览器,session对象和里面的数据都不会受影响,处于不可达的状态(访问不到)。
Session可以设置一个有效时间:有效时间内没有访问,数据自动被销毁。
Tomcat中默认的时间是30min。
在这里插入图片描述
没有访问的情况下(如果你访问了一次,那么直接重新从30min开始)
想主动去销毁session,使用session.invalidate();

销毁session里面的数据只有两种方式:
1.设置有效期
2.主动销毁,调用invalidate

3.7 案例(重难点)
案例:实现历史足迹。不做很严格的要求:必须实现最近的20条,每次点击都会变更(这里可以用LRU)。
简单地放入session中,用list存储即可,然后显示出来。

indexServlet.java(首页)

package com.cskaoyan.cart;

import com.cskaoyan.cart.bean.Product;
import sun.swing.BakedArrayList;

import javax.servlet.ServletContext;
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 {
        //初始化一些数据参数  商品信息
        List<Product> products = new ArrayList();
        products.add(new Product(1,"iphone X"));
        products.add(new Product(2, "MI10"));
        products.add(new Product(3, "P40"));
        products.add(new Product(4, "VIVO"));
        ServletContext servletContext = getServletContext();
        servletContext.setAttribute("products", products);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //JDBC----数据库查询拿到数据
        List<Product> products = (List<Product>) getServletContext().getAttribute("products");
        for (Product product : products) {
            String detail = response.encodeURL(request.getContextPath() + "/detail?id=" + product.getId());
            response.getWriter().println("<div><a href='" + detail + "'>" + product.getName() + "</a></div>");
        }
    }
}

Product.java(产品类)

package com.cskaoyan.cart.bean;

public class Product {

    private Integer id;

    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Product() {
    }

    public Product(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

DetailServlet.java

package com.cskaoyan.cart;

import com.cskaoyan.cart.bean.Product;

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 {
        //如何区分我点击的不同商品呢?
        //   localhost/detail?id=1/2/3/4
        response.setContentType("text/html;charset=utf-8");
        String id = request.getParameter("id");
        try {
            //判断id值是否合法,前端请求,任意伪造的
            Integer.parseInt(id);
        }catch (Exception e){
            response.getWriter().println("输入的参数不合法!!!!");
            return;
        }
        List<Product> products = (List<Product>) getServletContext().getAttribute("products");
        response.getWriter().println("<!DOCTYPE html>\n" +
                "<html lang=\"en\">\n" +
                "<head>\n" +
                "    <meta charset=\"UTF-8\">\n" +
                "    <title>Title</title>\n" +
                "</head>\n" +
                "<body>");
        for (Product product : products) {
            if(Integer.parseInt(id) == product.getId()){
                response.getWriter().println(product);
            }
        }
        String addCart = response.encodeURL(request.getContextPath() + "/addCart?id=" + id);
        String viewCart = response.encodeURL(request.getContextPath() + "/viewCart");
        String index = response.encodeURL(request.getContextPath() + "/index");

        response.getWriter().println("<div><a href='" + addCart + "'>加入购物车</a></div>");
        response.getWriter().println("<div><a href='"  + viewCart + "'>查看购物车</a></div>");
        response.getWriter().println("<div><a href='" + index + "'>返回首页</a></div>");
        response.getWriter().println("</body>\n" +
                "</html>");
    }
}

AddCartServlet .java

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");
        try {
            Integer.parseInt(id);
        }catch (Exception e){
            response.getWriter().println("输入的参数不合法!!!!");
            return;
        }
        HttpSession session = request.getSession();
        // 为什么不能set----每次都会把前面的覆盖
        //session.setAttribute();
        //要先get,然后再set   1,2,1 String     1 2 3 1 List     1 2 3 Set
        List<String> cart = (List<String>) session.getAttribute("cart");
        if(cart == null){
            cart = new ArrayList<>();
        }
        cart.add(id);
        session.setAttribute("cart", cart);
        response.getWriter().println("添加购物车成功,返回首页继续选购吧.....");
        String index = response.encodeURL(request.getContextPath() + "/index");
        response.setHeader("refresh","2;url=" + index);
    }
}

ViewCartServlet .java

package com.cskaoyan.cart;

import com.cskaoyan.cart.bean.Product;

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");
        //直接从session里面取出数据即可
        HttpSession session = request.getSession();
        // 存的是商品的id值  商品信息显示出来
        List<String> cart = (List<String>) session.getAttribute("cart");
        List<Product> products = (List<Product>) getServletContext().getAttribute("products");
        if(cart == null){
            response.getWriter().println("当前购物车为空,先去选购吧....");
            response.setHeader("refresh","2;url=" + request.getContextPath() + "/index");
            return;
        }
        for (String s : cart) {
            for (Product product : products) {
                if(Integer.parseInt(s) == product.getId()){
                    response.getWriter().println(product);
                }
            }
        }
    }
}

HistoryUtils.java

package com.cskaoyan.cart.utils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.LinkedList;

public class HistoryUtils {

    /**
     * 将访问的商品id加入到历史记录中
     * 只展示最近的3条历史记录
     * @param id
     * @param request
     */
    public static void updateRecentView(String id, HttpServletRequest request) {
        HttpSession session = request.getSession();
        LinkedList<String> history = (LinkedList<String>) session.getAttribute("history");
        if(history == null){
            history = new LinkedList<>();
        }
        //加入的时候,有几种可能性
        //1.当前记录中已经包含了该id   2.当前记录中不包含id
        if(history.contains(id)){
            //把当前id更新进去,肯定不会导致history的size超
            //把id记录删了,然后添加在最前面
            history.remove(id);
        }else {
            if(history.size() > 2){
                //把最久远的踢了
                history.removeLast();
            }
        }
        history.addFirst(id);
    }
}

加入购物车就是向浏览器写入cookie数据,删除cookie购物车数据就没有了,这只是未登录的情况。登录后,购物车数据会写到数据库中。

在项目开发中尽量使用包装类型,因为:
Integer 包装类型 面向对象的思想 默认null
int 默认0
要考虑到默认赋值在程序运行过程中是否会出现意想不到的错误。

3.8 session依赖于cookie,如果cookie禁用怎么办
全部链接要进行操作,否则无法打通。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值