Java学习 day55_cookie

本文深入探讨了HTTP协议的无状态性及其带来的问题,介绍了会话技术如何解决这个问题。通过实例分析了Cookie和Session在Web应用中的工作原理和使用方法,包括它们的创建、设置属性、生命周期以及如何实现用户登录和购物车功能。此外,还讨论了Cookie的存储限制、安全性以及禁用Cookie后的Session策略,展示了如何在关闭浏览器后保持Session数据的技巧。
摘要由CSDN通过智能技术生成

会话技术

我们编写一段代码,做一个实验,引出会话技术。

package com.cskaoyan.cookie;

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.Enumeration;

@WebServlet("/req")
public class RequestServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //打印一下客户端发送过来的HTTP请求报文信息以及客户端的主机地址信息
        String remoteAddr = request.getRemoteAddr();
        int remotePort = request.getRemotePort();
        System.out.println("客户机地址:" + remoteAddr + "端口号:" + remotePort);
        String method = request.getMethod();
        String requestURI = request.getRequestURI();
        String protocol = request.getProtocol();
        System.out.println(method + " " + requestURI + " " + protocol);
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()){
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            System.out.println(headerName + ":" + headerValue);
        }
    }
}

使用本地的浏览器发起一个HTTP请求,抓取HTTP请求报文

客户机地址:192.168.1.26端口号:50735
GET /app/req HTTP/1.1
host:192.168.1.26
connection:keep-alive
upgrade-insecure-requests:1
user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73
accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
accept-encoding:gzip, deflate
accept-language:zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6

使用另外一台机器(另外一个客户端),再次发起一个HTTP请求

客户机地址:192.168.1.3端口号:56214
GET /app/req HTTP/1.1
host:192.168.1.26
connection:keep-alive
upgrade-insecure-requests:1
user-agent:Mozilla/5.0 (Linux; Android 10; HarmonyOS; LIO-AN00; HMSCore 6.0.1.306) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 HuaweiBrowser/11.1.3.300 Mobile Safari/537.36
accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
accept-encoding:gzip, deflate
accept-language:zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7

通过去比较两个请求报文,发现有没有什么不同?

不同的客户端发送的HTTP请求报文,发现是一模一样的。

和我们之前的上网经历做比较,发现其实存在一个问题。比如我们在逛某个商城的时候,可以往购物车里面加入商品,接下来,下次访问时,发现商品依然还在。服务器可以帮助我们去保存记忆一些数据。

但是通过上面的实验,我们发现不同客户端发送的HTTP请求报文居然是一模一样的、那么这是怎么实现的呢?

既然客户端发送的HTTP请求报文都是完全相同的,服务器来说,应该是无法区分客户端才对,那为什么服务器能够区分客户端,并且给客户端保存一些数据呢?

会话,其实就是指的是对话,沟通的含义。HTTP协议是无状态性,表现在不同的客户端发送过来的请求报文都是完全相同的,服务器时无法区分多个HTTP请求报文是同一个客户端发送的还是不同客户端发送的。进而服务器也不可能给某个客户端去保存某些数据,但是实际上和我们的web访问经历是违背的,我们在上网的过程中,我们发现的确服务器时可以帮助客户端去保存某些数据的,那么是如何做到这一点的呢?

其实就是利用会话技术。会话的意思就是表示对话,沟通。对于标准的HTTP协议来说,你可以认为此时服务器是非常健忘的,它是不可能记住一些信息的,这个时候也就无法进行对话。会话技术其实就是为了能够解决HTTP协议的无状态性的特点。

对话:

A:你吃饭了嘛

B:吃过了

A:你吃的什么

B:吃的面

A:吃的什么面,宽面还是细面

B:大碗细面

A:多少钱

B:25块钱

对话能够正常进行下去的前提是啥:前提是能够知道所说的上下文的语义。

但是对于web访问过程而言,HTTP协议它是一个无状态性协议,体现在所有的客户端发送的HTTP请求报文都是完全相同的,服务器其实压根区分不了彼此,也就无法进行后续的操作。

客户端:我要查看xxx商品信息

服务器:返回了这个商品的信息

客户端:帮我把这个商品加入到我的购物车里面

服务器:晕了。(HTTP协议的无状态性,所有的客户端发送的请求报文都是完全相同的,无法区分彼此。这个商品指的是什么呢?我的购物车,我又是指的是谁呢?)

在标准的HTTP协议情况下,服务器时无法记住客户端的,是无法给客户端保存一些数据的,很不方便。后面进而引入了会话技术,会话技术的出现其实就是为了解决HTTP协议无状态这一不足,引入了会话技术之后,服务器就可以知道请求来自于哪个客户端,那么就可以给当前客户端保存一些数据。


客户端技术

代表是cookie。一小部分数据。该数据的产生是在服务器上面产生的,产生之后,数据会随着HTTP响应报文(响应头)传输给客户端,客户端会随即将该数据保存在浏览器中,当浏览器下次再去访问服务器时,浏览器就会把该cookie信息再次携带回给服务器(请求头),服务器就知道了请求来自于哪个客户端。

这块一开始没弄懂服务器是怎么通过cookie来区分是哪个客户端的,而且也不明白为什么一个字段就能区分,难道不应该用更长的数字序列才能区分吗?
后来通过项目实践,明白了其实就是通过cookie给你的这个信息,你能找到对应的用户的数据库内容,进而拿到用户的信息,这不就做到区分用户了吗。

在这里插入图片描述


cookie的本质:

数据的产生是在服务器,服务器产生这个数据之后,接下来通过set-Cookie响应头,将该数据返回给客户端,客户端会将该数据保存,当客户端下次再去访问服务器时,通过Cookie请求头将信息再次携带回给服务器,服务器取出cookie请求头里面的信息,就可以知道了是哪个客户端


Cookie的使用

Creates a cookie, a small amount of information sent by a servlet to a Web browser, saved by the browser, and later sent back to the server. A cookie’s value can uniquely identify a client, so cookies are commonly used for session management.

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("/cookie")
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是一个class,直接new一个cookie对象
        Cookie cookie = new Cookie("username", "zhangsan");
        //通过set-Cookie:usename=zhangsan发送给客户端
        //实际上EE规范同样给我们提供了一个简便的方式,用来发送cookie给客户端
        //开发者只需要调用该方法,那么服务器会帮我们去取出key和value值,然后生成set-Cookie响应头
        response.addCookie(cookie);
    }
}

通过HTTP去访问,应当去看什么东西?

1.第一次访问主要关注set-Cookie响应头(此时正常情况下来说,应该是不会有cookie请求头)

2.第二次访问主要关注Cookie请求头


案例一

使用cookie来实现登录功能,要求,用户输入用户名、密码进行登录,登录成功之后页面显示出当前用户的用户名。

在学习cookie之前,如果我希望可以在页面显示用户的用户名,可以怎么做?可以用转发。

login.html--------->LoginServlet(username reqeust域)---------转发到另外一个servlet(取出username)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/app/login" method="post">
        <input type="text" name="username"><br>
        <input type="password" name="password"><br>
        <input type="submit">
    </form>
</body>
</html>
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 {
        response.setContentType("text/html;charset=utf-8");
        String username = request.getParameter("username");
        //假设进行校验,用户名、密码校验通过   JDBC
        Cookie cookie = new Cookie("username", username);
        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 {
        //从请求头里面取出cookie信息,显示在页面上
        //该API的原理相当于在HTTP请求报文被拆封成request对象时,将请求头进行封装
        //相当于获取指定的请求头,然后封装成cookie[]
        Cookie[] cookies = request.getCookies();
        if(cookies != null){
            for (Cookie cookie : cookies) {
                if("username".equals(cookie.getName())){
                    response.getWriter().println(cookie.getValue());
                }
            }
        }
    }
}

案例二

​ 显示上次访问当前页面的时间。

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("/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 {
        //生成一个cookie信息
        Cookie[] cookies = request.getCookies();
        if(cookies != null){
            for (Cookie cookie : cookies) {
                if("lastLogin".equals(cookie.getName())){
                    String value = cookie.getValue();
                    // Long.parseLong----Integer.parsetInt  "111111"--->11111
                    Date date = new Date(Long.parseLong(value));
                    response.getWriter().println(date);
                }
            }
        }
        // cookie的value指里面不可以有空格
        Cookie cookie = new Cookie("lastLogin", System.currentTimeMillis() + "");
        response.addCookie(cookie);
    }
}

cookie设置

设置存活时间

如果cookie没有设置存活时间,则cookie默认情况下是存储在浏览器的内存中的,浏览器关闭,则cookie失效;如果希望cookie能够持久化保存,则应当设置一个时间。

Cookie cookie = new Cookie("lastLogin", System.currentTimeMillis() + "");
//单位是秒
cookie.setMaxAge(180);
response.addCookie(cookie);

设置正数,表示存活多少秒

设置负数,表示的是存活在浏览器内存中,和没有设置是一样的

设置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;
import java.util.Date;

@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 {
        //生成一个cookie信息
        Cookie[] cookies = request.getCookies();
        if(cookies != null){
            for (Cookie cookie : cookies) {
                if("lastLogin".equals(cookie.getName())){
                    String value = cookie.getValue();
                    // Long.parseLong----Integer.parsetInt  "111111"--->11111
                    Date date = new Date(Long.parseLong(value));
                    response.getWriter().println(date);
                    //特别注意一点:这里设置maxAge=0只是服务器设置了cookie对象
                    //但是你要清楚cookie是保存在哪的?保存在客户端的?
                    //服务器是没法直接删除cookie的,服务器给客户端发送了一个指示,让客户端去删除cookie
                    response.addCookie(cookie);
                }
            }
        }
        // cookie的value指里面不可以有空格
        Cookie cookie = new Cookie("lastLogin", System.currentTimeMillis() + "");
        //单位是秒
//        cookie.setMaxAge(180);

        response.addCookie(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("/last2")
public class LastLoginServlet2 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("lastLogin".equals(cookie.getName())){
                    String value = cookie.getValue();
                    // Long.parseLong----Integer.parsetInt  "111111"--->11111
                    Date date = new Date(Long.parseLong(value));
                    response.getWriter().println(date);
                    //特别注意一点:这里设置maxAge=0只是服务器设置了cookie对象
                    //但是你要清楚cookie是保存在哪的?保存在客户端的?
                    //服务器是没法直接删除cookie的,服务器给客户端发送了一个指示,让客户端去删除cookie
                    cookie.setMaxAge(0);
                    response.addCookie(cookie);
                }
            }
        }
    }
}

首先在last中生成一个cookie信息,在last2中我们获取到该cookie信息,打印出来,同时设置MaxAge=0

同时还需要再次设置response.addCookie(cookie)将该cookie的设置返回给客户端,客户端拿到该设置以后就会删除该cookie,下次再次访问时,就不会携带cookie了、


设置路径

默认情况下,当访问当前主机下所有资源时都会携带cookie,可以设置一个path,仅当访问指定路径时才会携带cookie。有的页面,访问静态资源文件不需要携带cookie,只设置访问某个html时才携带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("/path")
public class PathServlet 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("name", "path");
        cookie.setPath(request.getContextPath() + "/path2");
        response.addCookie(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("/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("name".equals(cookie.getName())){
                    response.getWriter().println(cookie.getValue());
                }
            }
        }
    }
}

在path中生成了一个cookie,设置了路径是访问path2会携带,也就是说访问path时,不会携带cookie的,只有访问path2时才会携带cookie。

其中,关于删除cookie有一个注意事项,如果cookie没有设置path,则直接设置MaxAge=0就表示删除cookie

如果cookie设置了path,在删除cookie时,还需要将原先的path再写一遍

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("/path")
public class PathServlet 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("name", "path");
        cookie.setPath(request.getContextPath() + "/path2");
        response.addCookie(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("/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("name".equals(cookie.getName())){
                    response.getWriter().println(cookie.getValue());
                    cookie.setMaxAge(0);
                    cookie.setPath(request.getContextPath() + "/path2");
                    response.addCookie(cookie);
                }
            }
        }
    }
}

正常的cookie只能在一个应用中共享,即一个cookie只能由创建它的应用获得。
1.可在同一应用服务器内共享方法:设置cookie.setPath("/");
本机tomcat/webapp下面有两个应用:cas和webapp_b,
1)原来在cas下面设置的cookie,在webapp_b下面获取不到,path默认是产生cookie的应用的路径。
2)若在cas下面设置cookie的时候,增加一条cookie.setPath("/");或者cookie.setPath("/webapp_b/");就可以在webapp_b下面获取到cas设置的cookie了。
3)此处的参数,是相对于应用服务器存放应用的文件夹的根目录而言的(比如tomcat下面的webapp),因此cookie.setPath("/");之后,可以在webapp文件夹下的所有应用共享cookie,而cookie.setPath("/webapp_b/");是指cas应用设置的cookie只能在webapp_b应用下的获得,即便是产生这个cookie的cas应用也不可以。


设置域名

总的原则是浏览器是不允许设置和当前域名无关的cookie的,比如当前域名 localhost,设置了一个域名叫做aaa.com的cookie,浏览器是不允许的。

比如你申请了一个aaa.com的域名

sub.aaa.com域名属于你的吗?是的

third.sub.aaa.com域名属于你的吗?也是的

如果你设置了一个父级域名的cookie,比如设置了一个cookie的domain是aaa.com,那么接下来当你访问当前域名的子域名时,浏览器会自动帮你携带cookie,可以实现cookie的共享

设置了一个域名叫做jd.com的cookie

当你访问product.jd.com时,account.jd.com时,search.jd.com时都可以携带cookie信息。

在hosts文件中做了如下设置

127.0.0.1 ccc.com
127.0.0.1 sub.ccc.com
127.0.0.1 third.sub.ccc.com

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("/domain")
public class DomainServlet 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", "doamin");
        //如果设置了改行代码,那么一定不用通过localhost来范文
        cookie.setDomain("ccc.com");
        response.addCookie(cookie);
    }
}

操作流程:

ccc.com/app/domain 该地址下生成一个一个cookie

紧接着访问sub.ccc.com/app以及third.sub.ccc.com/app时,发现请求头中均携带了刚刚生成的cookie信息,那么可以用来进行全局性的cookie共享

集团系统的cookie信息共享,尤其是用户的登录状态。

客户端技术的优点:

1.存储在客户端,减轻服务器的压力

缺点:

1.只可以存储字符类型,如果希望存储对象,很不方便

2.cookie存储的容量一般有限制,如果数据很大,则无法存储

3.cookie存储在客户端,不是特别安全,只可以存储一些非敏感数据


服务器技术

代表是session。核心其实就是在服务器内存中开辟一块空间,来给用户存储数据。

当客户端访问服务器时,服务器会给当前用户的浏览器开辟一块内存空间,那么这个内存空间就和当前的这个浏览器做一个绑定,接下来,等后续浏览器再次访问该服务时,那么就利用这块内存空间来给浏览器来提供服务,可以用来存取数据。

每有一个浏览器访问,那么就会开辟一块内存空间。二者之间做了一个绑定。

在这里插入图片描述

实际上,上图只有一个问题,那么就是client和session对象(内存空间)如何关联在一起

实际上就是通过cookie来关联的。session底层依赖于cookie的

客户端访问服务时,服务器在某个场景下,会生成一个session对象(开辟一块内存空间),紧接着将该session对象的id值通过cookie返回给客户端(set-Cookie:JSESSIONID=xxxx),客户端下次访问时,就会将该id再次通过cookie请求头携带回来(Cookie:JSESSIONID=xxxx),服务器通过去取出cookie里面的JSESSIONID的值,就可以拿到对应的session对象,就可以进行数据的共享。


session的使用

1.创建session对象?

EE规范给我们提供了一个方法,叫做request.getSession();

HttpSession getSession()

Returns the current session associated with this request, or if the request does not have a session, creates one.

HttpSession getSession(boolean create)

Returns the current HttpSession associated with this request or, if there is no current session and create is true, returns a new session.

If create is false and the request has no valid HttpSession, this method returns null.

对于方式二,如果create是true,那么它和方式一是完全等价的。

对于方式二,如果create是false,那么它有则返回,没有则返回null,不会去创建。

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();
        System.out.println(session);
        System.out.println(session.getId());
    }
}

思考一个问题:

抓包关注什么?

第一次访问肯定会创建一个session对象,创建完session对象肯定会将session的id通过set-Cookie响应头发送给客户端。

第二次访问该servlet,还能不能看到创建过程?能或者不能其实取决于此时有没有session对象?如何判断有没有session对象呢?其实就是根据请求报文中请求头中有没有携带有效的JSESSIONID,如果携带了一个有效的JSESSIONID,那么就可以根据id找到session对象,找到的话,则不会创建新的session对象;如果没有携带一个有效的JSESSIONID,那么就找不到session对象,找不到的话,就会创建一个新的session对象。


2.session对象的使用

session其实也是一个域,session域。

方法和之前介绍的context域、request域的方法一模一样。


getSession()方法的执行逻辑

request.getSession()方法,可以创建或者返回一个session对象。如果当前请求有关联的session对象,那么就返回, 如果没有关联的session对象,则创建一个新的。什么叫关联呢?

就是看请求头中有没有携带有效的Cookie:JSESSIONID,如果携带了有效的id,那么就可以根据该id找到对应的session对象,则直接将该session对象返回,那么后续也就不会创建,也就不会有发送set-Cookie响应头的过程;如果没有携带有效的id,则会创建一个新的

登录案例

使用session来实现。

package com.cskaoyan.session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
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");
        //假设进行校验,用户名、密码校验通过   JDBC
        HttpSession session = request.getSession();
        session.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.*;
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 {
        HttpSession session = request.getSession();
        String username = (String) session.getAttribute("username");
        response.getWriter().println(username);
    }
}

三个域区别

context、request、session域

只要能够拿到同一个对象,那么就可以共享数据。

context域:整个应用中有且只有一个,无论哪个客户端访问,访问哪个servlet,拿到的都是同一个

request域:非常小。每次请求都会创建新的request对象,只有转发两个组件可以共享

session域:和用户具有强相关性。每个浏览器访问服务器,正常情况下来说,都会创建一个session对象,只要是同一个浏览器,那么访问不同servlet时,拿到的也都是同一个session对象。不同的浏览器拿到的肯定不是同一个session对象。

在这里插入图片描述


关闭浏览器,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();
        session.setAttribute("key", "zhangsan");
        System.out.println(session);
        System.out.println(session.getId());
    }
}
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("/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 {
        //创建一个session对象
        HttpSession session = request.getSession();
        Object key = session.getAttribute("key");
        System.out.println(key);
        System.out.println(session);
        System.out.println(session.getId());
    }
}
// 访问session2
org.apache.catalina.session.StandardSessionFacade@179d29a5
F92C51A91C2E856E8ABC163EB7B75670
//访问session3
zhangsan
org.apache.catalina.session.StandardSessionFacade@179d29a5
F92C51A91C2E856E8ABC163EB7B75670
///-------关闭浏览器,再次访问session3
null
org.apache.catalina.session.StandardSessionFacade@5b3f2b69
680287DC762299FDC7AE13A662AAB7CB

session没有被销毁,但是数据此时已经取不到了,丢失了。

既然没有销毁,为什么打印session的地址和id都变了呢?

关闭浏览器之后,cookie默认情况下存在于浏览器内存中,关闭浏览器,则把cookie给丢失了,再次访问时不会携带cookie:JSESSIONID=xxx,如果不携带,则会创建一个新的session对象。

原先的session对象,此时还没有被销毁,类似于一种不可达的状态。

你在瑞士银行有一顿黄金,但是你把密码忘了。



关闭服务器,session会销毁吗,数据会丢失吗?

session对象会销毁。

数据不会丢失。

验证:千万不要通过关闭idea tomcat的关闭按钮或者重新部署来验证,否则你得不出结论

可以使用tomcat提供的应用的后台管理系统来关闭应用

1.需要保障本地安装的tomcat里面有manger应用

2.需要在本地安装的tomcat conf/tomcat-users.xml文件中新增如下标签

<role rolename="manager-gui"/>
<user username="tomcat" password="tomcat" roles="manager-gui"/>

更改完毕,重启服务器,访问manager

在这里插入图片描述

说明了啥?

原先session在服务器即将被关闭之前,将session的id以及session里面存储的数据,全部序列化到本地硬盘上面了

重启服务器时,重新分配了一些新的session对象,将这些sessionid以及数据塞入到新的对象中。

在这里插入图片描述

此外还可以采用另外一种方式来验证,直接用本地安装的tomcat来部署idea里面的应用



session的生命周期

session对象的创建:

​ request.getSession() 来创建

使用:

​ session.setAttribute()

​ getAttribute

​ removeAttribute

session对象的销毁:

​ 应用被卸载、服务器被关闭,但是此时数据不会丢失

​ 数据会以序列化的形式持久化到本地硬盘上面

数据的销毁:

​ 1.session的有效期到达(默认有效期是30min,如果30min没人访问,那么session数据被销毁,只要30min有人访问了,那么重新开始倒计时)

< session-config>
< session-timeout>30< /session-timeout>
< /session-config>

​ 2.主动调用session.invalidate()方法(登录成功,session写入数据;注销按钮)


购物车案例

1.首页可以显示出几条商品信息(就用超链接显示商品信息)

2.当用户点击某个超链接时,进入到该商品的详情页,显示商品的信息(直接调用对象的toString),在该页面还有三个a标签。一个是返回首页,一个是加入购物车,一个是查看购物车

3.点击加入购物车按钮,会将当前商品加入到购物车中,回显加入购物车成功,返回首页

4.点击查看购物车,那么可以显示出当前购物车里面的商品信息(直接显示商品的toString)

在这里插入图片描述

关于商品详情页可以这么来设计,需要拿到商品的id,为什么呢?因为在商品详情页需要加载商品的详细信息,需要通过id拿到

response.getWriter().println("<a href='" + request.getContextPath() + "/goods?id=" + product.getId() + "'>" + product.getName() + "</a>");---如何获取通过request.getParameter来获取


response.getWriter().println("<a href='" + request.getContextPath() + "/goods/" + product.getId() + "'>" + product.getName() + "</a>");
设置一个servlet,url-pattern是 /goods /*

response.getWriter().println("<a href='" + request.getContextPath() + "/goods/" + product.getId() + ".html'>" + product.getName() + "</a>");
怎么处理呢?   /goods/*




http://localhost/cart/goods/1

http://localhost/cart/goods/1.html

说明:不要求考虑数量问题。

package com.cskaoyan.cart;

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","iphone12", 6999.0, "iphone"));
        products.add(new Product("2", "huawei mate40 pro", 5888.0, "huawei"));
        products.add(new Product("3","mi 11", 5699.0, "mi"));
        products.add(new Product("4", "onePlus 10",5888.0, "one plus"));
        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来封装几个对象
        ServletContext servletContext = getServletContext();
        List<Product> products = (List<Product>) servletContext.getAttribute("products");
        for (Product product : products) {
//            response.getWriter().println("<a href='" + request.getContextPath() + "/goods/" + product.getId() + "'>" + product.getName() + "</a>");
            response.getWriter().println("<a href='" + request.getContextPath() + "/goods/" + product.getId() + ".html'>" + product.getName() + "</a>");

        }
    }
}
package com.cskaoyan.cart;

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.List;

@WebServlet("/goods/*")
public class GoodsServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //拿到商品的id     /cart/goods/1.html
        response.setContentType("text/html;charset=utf-8");
        String requestURI = request.getRequestURI();
        String path = requestURI.replace(request.getContextPath() + "/goods/", "");
        String id = path.replace(".html", "");
        //为了稳妥起见,可以做一个判断
        if(id == null){
            response.getWriter().println("不合法的请求地址");
            return;
        }
        ServletContext servletContext = getServletContext();
        List<Product> products = (List<Product>) servletContext.getAttribute("products");
        for (Product product : products) {
            if(id.equals(product.getId())){
                response.getWriter().println(product);
            }
        }
        //纵向选择  按住alt + 鼠标键拖拉
        response.getWriter().println("<a href='" + request.getContextPath() + "/index" + "'>返回首页</a>");
        response.getWriter().println("<a href='" + request.getContextPath() + "/addCart/" + id + "'>加入购物车</a>");
        response.getWriter().println("<a href='" + request.getContextPath() + "/viewCart" + "'>查看购物车</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.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");
        //查看购物车的逻辑 id---- context Product
        HttpSession session = request.getSession();
        List<String> cart = (List<String>) session.getAttribute("cart");
        if(cart == null){
            response.getWriter().println("购物车为空,去选购吧");
            response.setHeader("refresh", "2;url=" + request.getContextPath() + "/index");
            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 (String id : cart) {
            for (Product product : products) {
                if(id.equals(product.getId())){
                    response.getWriter().println(product);
                }
            }
        }
        response.getWriter().println("<a href='" + request.getContextPath() + "/index" + "'>返回首页</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.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 {
        String requestURI = request.getRequestURI();
        String id = requestURI.replace(request.getContextPath() + "/addCart/", "");
        //需要将id关联的对象保存到购物车中
        //这个时候你可以选择将id保存到购物车,或者将对象保存到购物车
        //接下来的逻辑应该是啥?购物车是用户强相关性  session
        HttpSession session = request.getSession();
//        session.setAttribute("id", id);
        List<String> cart = (List<String>) session.getAttribute("cart");
        if(cart == null){
            cart = new ArrayList<>();
            session.setAttribute("cart", cart);
        }
        cart.add(id);

        response.getWriter().println("add success");
        //接下来跳转到首页即可
        response.setHeader("refresh", "2;url=" + request.getContextPath() + "/index");
    }
}

作业

关闭浏览器,依然可以访问到session域中的数据

关闭浏览器,为什么数据会丢失呢?cookie丢失了,JSESSIONID凭证丢失了,找不到原先的session,进而创建了一个新的session对象

只需要设置关闭浏览器,JSESSIONID的cookie不丢失即可。

package com.cskaoyan.session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
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();
        Cookie cookie = new Cookie("JSESSIONID", session.getId());
        cookie.setMaxAge(180);
        response.addCookie(cookie);
        session.setAttribute("key", "zhangsan");
        System.out.println(session);
        System.out.println(session.getId());
    }
}

禁用cookie之后的session策略

大家都知道,浏览器是有禁用cookie的功能的,如果禁用了cookie,session还能用吗?正常情况下来说就无法使用了,但是可以通过设置API,对地址进行重写,将jsessionid放置在地址栏中进行传递

response.encodeURL()

package com.cskaoyan.cart;

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","iphone12", 6999.0, "iphone"));
        products.add(new Product("2", "huawei mate40 pro", 5888.0, "huawei"));
        products.add(new Product("3","mi 11", 5699.0, "mi"));
        products.add(new Product("4", "onePlus 10",5888.0, "one plus"));
        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来封装几个对象
        ServletContext servletContext = getServletContext();
        List<Product> products = (List<Product>) servletContext.getAttribute("products");
        for (Product product : products) {
            String goods = response.encodeURL(request.getContextPath() + "/goods/" + product.getId() + ".html");
            response.getWriter().println("<a href='" + goods + "'>" + product.getName() + "</a>");

        }
    }
}
package com.cskaoyan.cart;

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.nio.IntBuffer;
import java.util.List;

@WebServlet("/goods/*")
public class GoodsServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //拿到商品的id     /cart/goods/1.html
        response.setContentType("text/html;charset=utf-8");
        String requestURI = request.getRequestURI();
        String path = requestURI.replace(request.getContextPath() + "/goods/", "");
        String id = path.replace(".html", "");
        int i = id.indexOf(";");
        if(i != -1){
            id  = id.substring(0, i);
        }
        //为了稳妥起见,可以做一个判断
        if(id == null){
            response.getWriter().println("不合法的请求地址");
            return;
        }
        ServletContext servletContext = getServletContext();
        List<Product> products = (List<Product>) servletContext.getAttribute("products");
        for (Product product : products) {
            if(id.equals(product.getId())){
                response.getWriter().println(product);
            }
        }
        //纵向选择  按住alt + 鼠标键拖拉
        String index = response.encodeURL(request.getContextPath() + "/index");
        String addCart = response.encodeURL(request.getContextPath() + "/addCart/" + id);
        String viewCart = response.encodeURL(request.getContextPath() + "/viewCart");
        response.getWriter().println("<a href='" + index + "'>返回首页</a>");
        response.getWriter().println("<a href='" + addCart + "'>加入购物车</a>");
        response.getWriter().println("<a href='" + viewCart + "'>查看购物车</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.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");
        //查看购物车的逻辑 id---- context Product
        HttpSession session = request.getSession();
        List<String> cart = (List<String>) session.getAttribute("cart");
        if(cart == null){
            response.getWriter().println("购物车为空,去选购吧");
            String index = response.encodeURL(request.getContextPath() + "/index");

            response.setHeader("refresh", "2;url=" + index);
            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 (String id : cart) {
            for (Product product : products) {
                if(id.equals(product.getId())){
                    response.getWriter().println(product);
                }
            }
        }
        String index = response.encodeURL(request.getContextPath() + "/index");
        response.getWriter().println("<a href='" + index + "'>返回首页</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.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 {
        String requestURI = request.getRequestURI();
        // addCart/2;xxxxxx
        String id = requestURI.replace(request.getContextPath() + "/addCart/", "");
        int i = id.indexOf(";");
        if(i != -1){
            id  = id.substring(0, i);
        }
        //需要将id关联的对象保存到购物车中
        //这个时候你可以选择将id保存到购物车,或者将对象保存到购物车
        //接下来的逻辑应该是啥?购物车是用户强相关性  session
        HttpSession session = request.getSession();
//        session.setAttribute("id", id);
        List<String> cart = (List<String>) session.getAttribute("cart");
        if(cart == null){
            cart = new ArrayList<>();
            session.setAttribute("cart", cart);
        }
        cart.add(id);

        response.getWriter().println("add success");
        //接下来跳转到首页即可
        String index = response.encodeURL(request.getContextPath() + "/index");

        response.setHeader("refresh", "2;url=" + index);
    }
}

jsp不去展开了。jsp的原理其实就是做了我们之前response.getWriter().println()输出html标签的工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值