04【Cookie、Session】

news2024/11/28 9:27:47

文章目录

  • 04【Cookie、Session】
  • 一、Cookie
    • 1.1 Cookie概述
      • 1.1.1 协议的状态
      • 1.1.2 Cookie的传递流程
    • 1.2 Cookie的操作
      • 1.2.1 创建Cookie
      • 1.2.2 Cookie的销毁
      • 1.2.3 Cookie的获取
      • 1.2.4 Cookie中使用特殊字符的情况
      • 1.2.5 Cookie的携带路径
      • 2.2.6 Cookie的删除
  • 二、Session
    • 2.1 Session概述
      • 2.1.1 Session介绍
      • 2.1.2 Session与Cookie的区别
    • 2.2 HttpSession接口的使用
    • 2.4 Session的原理分析:
      • 2.4.2 分析上述案例
      • 2.4.3 Session的分析
    • 2.5 持久化JSESSIONID
    • 2.6 会话的过期时间
      • 2.6.1 设置会话存活的时间
        • 1)代码设置
        • 2)设置xml
    • 2.7 Session的生命周期
    • 2.8 Session的持久化
  • 三、综合案例
    • 3.1 搭建项目
        • 1)执行SQL脚本:
        • 2)拷贝jar包:
        • 3)jdbc.properies:
        • 4)准备DataSourceUtils工具类:
        • 5)实体类:
        • 6)验证码工具类:
        • 7)静态页面:
    • 3.2 代码实现
      • 3.2.1 用户登录
      • 3.2.2 转账
      • 3.2.3 用户注销

04【Cookie、Session】

一、Cookie

1.1 Cookie概述

1.1.1 协议的状态

我们的协议按状态可划分为有状态无状态

  • 有状态:有状态简单来说就是有存储数据的功能,并且可以通过存储的数据来判断多次请求的关系,当请求端发送数据到服务端后,服务端能通过协议本身来判断该请求和上一个请求的关系;

在这里插入图片描述

  • 无状态:无状态就是没有数据存储的功能,当请求端发送数据到服务端后,服务端不能仅通过协议本身来判断该请求和上一个请求的关系;

我们之前学习过的HTTP协议就是无状态的;

无状态指的是:当一个客户端向服务器端发出请求,然后Web服务器返回响应,连接就被关闭了,在服务器端不保留连接的有关信息。可以看出,这种协议的结构是要比有状态的协议更简单的,一般来说实现起来也更简单,不需要使用状态机制。

1.1.2 Cookie的传递流程

HTTP协议是无状态的,无状态意味着,服务器无法给不同的客户端响应不同的信息,这样一些交互业务就无法支撑了。因此Cookie应运而生。

  • cookie的传递会经过下边这4步:
    • 1)Client发送HTTP请求给Server,Server判断本次请求是否有携带Cookie
    • 2)没有携带Cookie,则Server会在本次响应头中携带Set-Cookie响应头,Cookie信息就存在这个响应头中
    • 3)Client读取本次响应头中的Set-Cookie数据,创建一个Cookie并将Cookie的值存储到浏览器中,之后请求Server会将Cookie的数据读出来放在请求体头中(请求头的名称就叫Cookie)
    • 4)Server从请求头中获取Cookie信息,来判断是否是上一个人;

在这里插入图片描述

Cookie实际上是一小段的文本信息(key-val键值对格式)。客户端向服务器发起请求,如果服务器需要记录该用户状态,就用response向客户端发送一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再次向网站发送请求时,浏览器会把请求的网址和Cookie一起提交给服务器。服务器检查该Cookie,以此来辨别用户状态。

类似与我们去银行办理存蓄业务,第一次给你先办张银行卡,里面储存了身份证、密码、手机号码等个人信息。当你下次再来这个银行,银行及其能够直接识别出是你的卡,从而能够直接办理业务。

1.2 Cookie的操作

1.2.1 创建Cookie

  • Cookie相关方法:
Cookie类的方法作用
Cookie(String name,String value)通过构造方法创建一个Cookie 参数:键和值,都是String类型
String getName()得到Cookie的键
String getValue()得到Cookie的值
  • 在本次响应中添加Cookie
HttpServletResponse对象作用
addCookie(Cookie cookie)将服务器创建的Cookie通过响应发送给浏览器 参数:创建好的Cookie对象
  • 示例代码:
package com.dfbz.demo;

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.io.PrintWriter;

/**
 * @author lscl
 * @version 1.0
 * @intro: 创建一个Cookie
 */
@WebServlet("/demo01")
public class Demo01Servlet_CreateCookie extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //创建Cookie对象
        Cookie cookie = new Cookie("user", "zhangsan");

        //写到浏览器端
        response.addCookie(cookie);

        //在网页给个提示
        response.setContentType("text/html;charset=utf-8");

        PrintWriter out = response.getWriter();

        out.print("向浏览器写入了一个Cookie信息");
    }
}

访问:http://localhost:8080/demo01:

  • 查询响应头

在这里插入图片描述

  • 查看浏览器的Cookie信息:

在这里插入图片描述

1.2.2 Cookie的销毁

Cookie创建后,是存储在客户端磁盘中(每个浏览器的存储位置不一样),在默认情况下,浏览器关闭之后,Cookie就销毁了;

Cookie设置过期时间说明
void setMaxAge(int expiry)设置会话过期的时间,单位是秒 正数:设置秒数 负数:无效,浏览器关闭就失效 零:删除Cookie
  • 示例代码:
package com.dfbz.demo;

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.io.PrintWriter;

@WebServlet("/demo02")
public class Demo02 extends HttpServlet {

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

        //创建Cookie对象
        Cookie man = new Cookie("user", "zhangsan");

        // 设置cookie的有效时间  单位: 秒
        man.setMaxAge(60 * 10);         //10分钟

        //写到浏览器端
        response.addCookie(man);

        //在网页给个提示
        response.setContentType("text/html;charset=utf-8");

        PrintWriter out = response.getWriter();

        out.print("向浏览器写入了一个Cookie信息");
    }
}

访问:http://localhost:8008/demo02

  • 运行效果

在这里插入图片描述

  • 查看本次响应头信息:

在这里插入图片描述

1.2.3 Cookie的获取

HttpServletRequest对象作用
Cookie[] getCookies()服务器得到浏览器端发送过来的所有的Cookie信息,返回的是一个Cookie的对象数组
  • 写入Cookie:
package com.dfbz.demo;

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;

/**
 * @author lscl
 * @version 1.0
 * @intro: 创建Cookie
 */
@WebServlet("/demo03")
public class Demo03Servlet_WriteCookie extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 创建Cookie对象
        Cookie userCookie = new Cookie("user", "lisi");
//        Cookie userCookie = new Cookie("user", URLEncoder.encode("李四","UTF-8"));

        // 添加到本次响应中
        response.addCookie(userCookie);

        // 创建一个Cookie
        Cookie addressCookie = new Cookie("address", "qinghai");
//        Cookie addressCookie = new Cookie("address", URLEncoder.encode("青海","UTF-8"));

        // 添加到本次响应中
        response.addCookie(addressCookie);    //写入

        response.getWriter().print("ok!");
    }
}

访问:http://localhost:8080/demo03

  • 抓包查看响应报文:

在这里插入图片描述

  • 读取Cookie:
package com.dfbz.demo;

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.io.PrintWriter;

/**
 * @author lscl
 * @version 1.0
 * @intro: 读取Cookie
 */
@WebServlet("/demo04")
public class Demo04Servlet_ReadCookie extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 设置响应头以及编码
        response.setContentType("text/html;charset=utf-8");

        PrintWriter out = response.getWriter();

        //读取浏览器发送过来的所有Cookie信息
        Cookie[] cookies = request.getCookies();        //如果没有cookie,返回null

        //判断数组是否为空
        if (cookies != null) {
            //遍历数组
            for (Cookie cookie : cookies) {
                out.print("name:" + cookie.getName() + ",value:" + cookie.getValue() + "<hr>");
            }
        } else {
            out.print("没有Cookie");
        }
    }
}

访问:http://localhost:8080/demo04

  • 抓包查看请求报文:

在这里插入图片描述

1.2.4 Cookie中使用特殊字符的情况

在cookie值中使用分号(;)、逗号(,)、等号(=)、空格和中文等情况需要编码处理

类与方法说明
java.net.URLEncoder.encode(“字符串”,“utf-8”)把字符串使用utf-8进行编码
java.net.URLDecoder.decode(“字符串”,“utf-8”)把字符串使用utf-8进行解码
  • 使用步骤
  1. 在创建cookie之前将数据编码
  2. 将编码后的数据存入cookie
  3. 获取到cookie之后,解码数据,获取正常内容

测试将Demo03中的拼音改为汉字:

Cookie userCookie = new Cookie("user", "李四");

Cookie addressCookie = new Cookie("address", "青海");

再次访问:http://localhost:8080/demo03

在这里插入图片描述

如果存储的是特殊字符以及中文,那么我们需要进行编码:

Cookie userCookie = new Cookie("user", URLEncoder.encode("李四","UTF-8"));

Cookie addressCookie = new Cookie("address", URLEncoder.encode("青海","UTF-8"));

存值的时候进行了编码,那么取值的时候应该要解码:

for (Cookie cookie : cookies) {
    out.print("name:" + cookie.getName() + ",value:" + URLDecoder.decode(cookie.getValue(), "UTF-8") + "<hr>");
}

重启服务器,再次访问:http://localhost:8080/demo03

查看响应头:

在这里插入图片描述

访问:http://localhost:8080/demo04

查看请求头:

在这里插入图片描述

1.2.5 Cookie的携带路径

  • 设置路径的方法:
Cookie设置路径的方法功能
cookie.setPath(路径);用于设置Cookie访问的路径 访问这个路径或路径的子目录都可以访问Cookie 其它的路径无法访问

Tips:Cookie的默认的路径为当前Servlet的同级目录;

  • 创建一个Cookie工具类:
package com.dfbz.utils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
/**
 * @author lscl
 * @version 1.0
 * @intro: 根据CookieName获取在本次请求中获取Cookie的Value
 */
public class CookieUtils {
    /**
     * 根据cookieName获取Cookie的value
     * @param request
     * @param name
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String name) {

        // 得到所有的Cookie
        Cookie[] cookies = request.getCookies();
        // 判断数组是否为空
        if (cookies!=null) {
            for (Cookie cookie : cookies) {
                // 判断名字是否相等
                if (cookie.getName().equals(name)) {

                    // 返回此cookie的值
                    return cookie.getValue();
                }
            }
        }
        //数组为空或没有找到名字
        return null;
    }
}
  • 创建一个Cookie,使用默认携带路径:
package com.dfbz.demo;

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;

/**
 * @author lscl
 * @version 1.0
 * @intro: 使用默认Cookie携带路径
 */
@WebServlet("/user/findAll")
public class Demo05Servlet_CookiePath extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 创建一个Cookie,默认的携带路径为 当前Servlet的同级路径  (/user)
        Cookie cookie=new Cookie("province","JiangXi");

        // 等价于
//        cookie.setPath("/user");
        
        // Cookie的默认携带路径是当前的servlet的同级路径,如果将携带路径设置为"",那么该携带路径也为当前的servlet同级目录
//        cookie.setPath("");
        
        response.addCookie(cookie);

        response.getWriter().write("ok!");
    }
}

Tips:如果将携带路径设置为""(空字符串),那么该携带路径也为当前的servlet同级目录

  • 能够携带的路径:
访问地址能否访问到Cookie
http://localhost:8080/user/findById可以
http://localhost:8080/user/findById/abc可以
http://localhost:8080/user可以
http://localhost:8080/demo06不可以
http://localhost:8080/不可以
  • 读取Cookie测试:
package com.dfbz.demo;

import com.dfbz.utils.CookieUtils;

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;

/**
 * @author lscl
 * @version 1.0
 * @intro: 读取Cookie信息
 */
//@WebServlet("/user/findById")           // 可以读取到
//@WebServlet("/user/")                       // 可以读取到
//@WebServlet("/user/demo06")                       // 可以读取到
@WebServlet("/demo06")                       // 不可以读取到
public class Demo06Servlet_GetCookie extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 根据CookieName获取Cookie值
        String value = CookieUtils.getCookieValue(request, "province");

        response.getWriter().write("value: " + value);
    }
}

2.2.6 Cookie的删除

  • Cookie删除的相关方法:
Cookie的删除说明
setMaxAge(0)设置生命周期为0,表示删除Cookie的信息
  • 示例:删除指定的Cookie信息,注意Cookie的访问路径要和当初签发的相同,否则Cookie将不会被删除
package com.dfbz.demo;

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;

/**
 * @author lscl
 * @version 1.0
 * @intro: 删除Cookie
 */
@WebServlet("/demo07")
public class Demo07Servlet_DeleteCookie extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //创建Cookie对象,与要删除的键同名
        Cookie cookie = new Cookie("province", "");

        //设置路径(必须要和当初签发cookie的路径一致)
        cookie.setPath("/user");

        //设置过期时间
        cookie.setMaxAge(0);

        //写入浏览器
        response.addCookie(cookie);

        response.getWriter().write("ok!");
    }
}

二、Session

2.1 Session概述

2.1.1 Session介绍

如果把用户名、密码等重要隐私都存到客户端的Cookie中,还是有泄密风险。为了更安全,把机密信息保存到服务器上,这就是 Session;Session是服务器上维护的客户档案,可以理解为服务器端数据库中有一张user表,里面存放了客户端的用户信息。SessionID就是这张表的主键ID;

Session与Cookie不同,session是属于服务器端的会话技术,数据是保存在服务器的内存中。每个会话中保存它自己的数据,其它会话不能访问。不同的会话之间不能实现数据的共享。

2.1.2 Session与Cookie的区别

  • 1)Cookie属于客户端会话技术,数据保存在浏览器端文件中(磁盘),Cookie中键和值都是String类型

  • 2)Session属于服务器端的会话技术,数据保存服务器内存中,Session中键是String,值是Object类型

2.2 HttpSession接口的使用

  • 创建会话的方法:
创建session描述
HttpSession request.getSession()作用:通过请求对象创建一个会话对象 如果当前用户会话不存在,创建会话。如果会话已经存在,这个方法返回已经存在的会话对象。

Tips:用户第1次访问,使用request.getSession()时创建一个会话对象HttpSession。

  • HttpSession相关API:
HttpSession接口方法作用
String getId()得到会话的ID,在服务器上唯一的32位的十六进制数
long getCreationTime()表示会话创建的时间,返回long类型。表示1970-1-1到这个时间之间相差的毫秒数
long getLastAccessedTime()表示会话上次访问的时间
boolean isNew()判断当前是否是一个新的会话,是的返回true
ServletContext getServletContext()通过会话得到上下文对象
  • 示例代码:
package com.dfbz.demo;

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.io.PrintWriter;
import java.sql.Timestamp;

/**
 * @author lscl
 * @version 1.0
 * @intro: session 基本API
 */
@WebServlet("/demo01")
public class Demo01Servlet_SessionApi extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");
        
        PrintWriter out = response.getWriter();

        //得到会话对象
        HttpSession session = request.getSession();

        //得到会话的ID,在服务器上唯一的32位的十六进制数
        out.print("会话ID:" + session.getId() + "<hr>");

        //表示会话创建的时间,返回long类型。
        out.print("会话创建时间:" + new Timestamp(session.getCreationTime()) + "<hr>");

        //表示会话上次访问的时间
        out.print("上次访问的时间:" + new Timestamp(session.getLastAccessedTime()) + "<hr>");

        //判断当前是否是一个新的会话,是的返回true
        out.print("是否新的会话:" + session.isNew() + "<hr>");

        //通过会话得到上下文对象
        out.print("上下文对象:" + session.getServletContext() + "<hr>");
    }
}

访问:http://localhost:8080/demo01

在这里插入图片描述

  • 会话域对象的方法:

session也是一个域对象,也有域对象的几个方法,如下:

HttpSession的方法作用
Object getAttribute(“名字”)从会话域中得到一个值
void setAttribute(“名字”,Object数据)向会话域中添加一对键和值
void removeAttribute(“名字”)从会话域中删除键和值

小案例:

1)在一个SetServlet中,向Session中添加一个用户:张三,另一个GetServlet中,从Session中取出用户并输出在网页上。

2)使用一个浏览器存,另一个浏览器取,看能不能取出来。

  • SetServlet
package com.dfbz.demo;

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;

/**
 * @author lscl
 * @version 1.0
 * @intro: 向会话域中存值
 */
@WebServlet("/set")
public class Demo02SetServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //创建会话对象
        HttpSession session = request.getSession();

        //向会话域中添加键和值
        session.setAttribute("country", "China");

        response.getWriter().write("ok!");
    }
}
  • GetServlet
package com.dfbz.demo;

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.io.PrintWriter;


/**
 * @author lscl
 * @version 1.0
 * @intro: 从会话域中取出值
 */
@WebServlet("/get")
public class Demo03GetServlet extends HttpServlet {

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

        PrintWriter out = response.getWriter();

        // 得到会话对象
        HttpSession session = request.getSession();

        // 取出来
        out.print(session.getAttribute("country"));
    }
}

2.4 Session的原理分析:

在这里插入图片描述

1)第1次访问用户没有会话ID,调用getSession方法,服务器创建一个会话对象

2)每个会话都有一个唯一的ID,通过Cookie发送给浏览器。

3)浏览器得到会话ID,下次访问再通过Cookie发送给服务器,相当于带了密码条给服务器。

4)服务器通过Cookie中的ID,与服务器中会话ID进行比较,访问同一个会话域中数据

2.4.2 分析上述案例

1)访问:http://localhost:8080/set

客户端第一次访问,服务器创建一个session会话,之后将此次会话的ID以Cookie的方式写到客户端,此Cookie的携带路径默认为整个项目都携带(不是当前servlet的访问路径的同级目录);

我们通过浏览器抓包工具查看响应头信息:

在这里插入图片描述

2)访问:http://localhost:8080/get

在上一步的操作中,服务端已经将JSESSIONID以Cookie的形式写回客户端了,下次客户端访问服务端的任意请求都会携带此Cookie,服务端根据sessionid查询原来的会话,查询到了则返回该会话,如果查询不到则创建一个新的会话,并将此次新的会话的sessionid通过cookie的形式写回客户端

在这里插入图片描述

2.4.3 Session的分析

  • 问:浏览器关闭以后,还能不能得到之前会话域中的信息?
  • 答:不能,因为会话ID已经丢失,再次访问会话ID不同的。得到不之前会话中的数据。

  • 问:如果浏览器关闭,服务器上的会话信息是否还存在?
  • 答:还是存在,直接会话过期之前都是存在的。

  • 如何让浏览器关闭还可以访问之前session中的数据?
  • 答:将Cookie中的Sessionid持久化;

2.5 持久化JSESSIONID

我们通过刚刚分析的session原理就能够明白,要想找到服务器的会话,必须要携带对应的会话ID,如果会话ID没有携带,那么服务器会创建一个新的会话;而**Cookie在默认情况下,浏览器关闭就失效了;**那么上一次会话的信息也就找不到了,再次获取session时,服务器会创建一个新的会话给客户端(上一次的会话并没有销毁,只是找不到了)

  • 修改Demo02SetServlet代码:
package com.dfbz.demo;


import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

/**
 * @author lscl
 * @version 1.0
 * @intro: sessionID持久化
 */
@WebServlet("/demo04")
public class Demo04Servlet_Persistent extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //创建会话对象
        HttpSession session = request.getSession();

        //向会话域中添加键和值
        session.setAttribute("country","China");

        // 获取此次会话的id
        String id = session.getId();

        Cookie cookie=new Cookie("JSESSIONID",id);

        // 设置过期时间10分钟
        cookie.setMaxAge(600);

        // 写回到前端
        response.addCookie(cookie);
    }
}

查看JSESSIONID的过期时间:

在这里插入图片描述

可以关闭浏览器,再次访问:http://localhost:8080/get

发现依然可以获取到数据;

2.6 会话的过期时间

默认情况下,关闭浏览器后,上一次会话就找不到了,原因是因为cookie销毁了,JSESSIONID没有了,所以才导致服务器端创建了一个新的会话,其实上一次的会话并没有销毁,那么问题来了,Session在服务器上默认的销毁时间是多久?如何查看?

session中的方法说明
int getMaxInactiveInterval()得到服务器上会话最大的非活动时间间隔,默认是1800秒(30分钟)
  • 时间间隔的含义:如果你在这段时间内再次发送请求给服务器,服务器将会重新计时。

2.6.1 设置会话存活的时间

1)代码设置

HttpSession的方法功能描述
void setMaxInactiveInterval(int 秒)设置会话最大非活动时间时隔,单位是秒
  • 示例代码:
package com.dfbz.demo;


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.io.PrintWriter;

/**
 * @author lscl
 * @version 1.0
 * @intro: 会话存在的时间
 */
@WebServlet("/demo05")
public class Demo05Servlet_SessionExpire extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");

        PrintWriter out = response.getWriter();

        // 创建会话
        HttpSession session = request.getSession();

        // 设置过期时间,10秒
        session.setMaxInactiveInterval(10);

        // 当前会话存活的时间是
        out.print("会话存在的时间:" + session.getMaxInactiveInterval() + "<hr>");
        out.print("会话ID: " + session.getId());
    }
}

先访问:http://localhost:8080/set 存入数据到session

在访问:http://localhost:8080/demo04 将session的过期时间设置为10s

最后访问:http://localhost:8080/get 查看10s后,session的值就不存在了

2)设置xml

除了通过代码来设置session的过期时间之外,我们还可以通过配置web.xml文件来设置项目会话过期的时间

  • 添加web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 配置会话过期的时间;单位: 分钟 如果设置为零或负数,则表示会话将永远不会超时。 -->
    <session-config>
        <session-timeout>5</session-timeout>
    </session-config>
</web-app>

疑问:设置web.xml的会话配置,并且在代码中设置会话过期的时间,以哪个为准?

  • 就近原则,以代码为准,代码会覆盖前面配置

3)立刻失效

HttpSession方法功能描述
invalidate()会话立刻失效,一般用于用户退出,注销

修改demo04:

package com.dfbz.demo;


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.io.PrintWriter;

/**
 * 会话存在的时间
 */
@WebServlet("/demo04")
public class Demo04 extends HttpServlet {

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

        response.setContentType("text/html;charset=utf-8");

        PrintWriter out = response.getWriter();

        //创建会话
        HttpSession session = request.getSession();

        // 让session立即失效
        session.invalidate();

        out.print("会话ID: " + session.getId()+"已经失效!");
    }
}

访问:http://localhost:8080/set 存入数据到session

访问:http://localhost:8080/get 可以正常查询数据

访问:http://localhost:8080/demo04 session失效

访问:http://localhost:8080/get 查询不到数据

2.7 Session的生命周期

  • 何时创建?
    • request.getSession()判断是否要创建session
      • 请求头中的Cookie是否有携带会话ID
        • 有携带:
          • 根据会话ID去服务器里面查询对应的Session
            • 查询到了:返回对应的session
            • 查询不到:创建一个新的会话,并且在本次的响应头中添加Set-Cookie(内容就是本次的会话ID)
        • 没有携带:创建一个新的会话,并且在本次的响应头中添加Set-Cookie(内容就是本次的会话ID)
  • 何时销毁?
    • 1)默认30分钟后销毁(在30分钟期间,如果获取了对应的会话,那么会过期时间会重新续期为30分钟),可以通过session.setMaxInactiveInterval()设置过期时间
    • 2)调用session.invalidate()方法时立即销毁
    • 3)服务器关闭时session销毁
  • 为什么浏览器关闭session就"销毁"了呢?
    • 浏览器关闭是存储sessionid的cookie被销毁了,导致下次来到服务器端找不到上一次的会话,因此服务器会创建一个新的session,并将新session的id以cookie的形式写回到客户端;

2.8 Session的持久化

我们都知道session是存储在服务器的内存中的,当服务器关闭之后,session就销毁了,session的持久化指的是将session的内容持久化到磁盘上,进行永久保存,想要session的内容能够持久化必须保证对象实现Serializable接口;

session的持久化也叫钝化与活化

  • 钝化:从内存到磁盘
  • 活化:从磁盘到内存

在这里插入图片描述

在web目录下创建META-INF目录,然后创建Context.xml文件:

<Context>
    <!-- maxIdleSwap:session中的对象多长时间不使用就钝化(单位为分钟) -->
    <!-- directory:钝化后的对象的文件写到磁盘的哪个目录下  配置钝化的对象文件默认在work/catalina/localhost/钝化文件 -->
    <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
        <Store className="org.apache.catalina.session.FileStore" directory="d:/aaa" />
    </Manager>
</Context>

在这里插入图片描述

创建Person对象(必须要实现Serializable接口):

/**
 * 实现Serializable接口
 */
public class Person implements Serializable {

    private String id;
    private String name;
}
  • CreateServlet:
package com.dfbz.demo;

import com.dfbz.entity.Person;

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;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet("/create")
public class Demo07_CreateServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("text/html;charset=utf8");

        Person person = new Person("1", "张三");

        // 将对象存入session
        request.getSession().setAttribute("person", person);

        response.getWriter().println("create访问成功...会话ID" + request.getSession().getId() + "<hr>");
        response.getWriter().println("person: " + person);
    }
}
  • QueryServlet:
package com.dfbz.demo;

import com.dfbz.entity.Person;

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.io.PrintWriter;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet("/query")
public class Demo08_QueryServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf8");

        Person person = (Person) request.getSession().getAttribute("person");

        PrintWriter writer = response.getWriter();

        // 和之前钝化的id是同一个
        writer.println("query访问成功...会话ID" + request.getSession().getId() + "<hr>");

        writer.println("person: " + person + "<hr>");
    }
}
  • DeleteServlet:
package com.dfbz.demo;

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.io.PrintWriter;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet("/delete")
public class Demo09_DeleteServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("text/html;charset=utf8");

        PrintWriter writer = response.getWriter();

        writer.println("delete访问成功..." + request.getSession().getId());

        // 销毁session(钝化的文件也会被删除)
        request.getSession().invalidate();
    }
}

访问:http://localhost:8080/create

等待一分钟,观察:D:/aaa目录下是否有xxx.session文件产生,等待文件生成后关闭服务器(session销毁)

再次访问:http://localhost:8080/query 发现session中保存的person依旧存在

Tips:开启session持久化后,如果有创建session,那么默认情况下服务器关闭会将session都钝化到文件中;

三、综合案例

3.1 搭建项目

1)执行SQL脚本:

CREATE TABLE `account`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `money` double(255, 0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO account VALUES (1, 'zhangsan', 'admin', 1000);
INSERT INTO account VALUES (2, 'lisi', '123456', 1500);
INSERT INTO account VALUES (3, 'wangwu', '111', 5000);

2)拷贝jar包:

在这里插入图片描述

3)jdbc.properies:

jdbc.username=root
jdbc.password=admin
jdbc.url=jdbc:mysql://localhost:3306/xb
jdbc.driverClassName=com.mysql.jdbc.Driver

4)准备DataSourceUtils工具类:

package com.dfbz.utils;

import com.alibaba.druid.pool.DruidDataSource;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 数据源的工具类
 */
public class DataSourceUtils {

    private static DataSource ds;

    /**
     * 在静态代码块中创建数据源对象
     */
    static {
        // 创建druid数据源
        DruidDataSource dataSource = new DruidDataSource();

        Properties prop = new Properties();
        try {
            // 加载配置文件
            prop.load(DataSourceUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        dataSource.setUsername(prop.getProperty("jdbc.username"));
        dataSource.setPassword(prop.getProperty("jdbc.password"));
        dataSource.setUrl(prop.getProperty("jdbc.url"));
        dataSource.setDriverClassName(prop.getProperty("jdbc.driverClassName"));

        ds=dataSource;
    }

    /**
     * 得到数据源
     */
    public static DataSource getDataSource() {
        return ds;
    }


    /**
     * 从连接池中得到连接对象
     */
    public static Connection getConnection() {
        try {
            return ds.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }


    /**
     * 释放资源
     */
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        //关闭结果集
        if (rs!=null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //关闭语句对象
        if (stmt!=null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //关闭连接对象
        if (conn!=null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭连接
     */
    public static void close(Connection conn, Statement stmt) {
        close(conn, stmt, null);
    }
}

5)实体类:

package com.dfbz.entity;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Account {
    private Integer id;
    private String username;
    private String password;
    private Double money;

    // 省略get/set/toString...
}

6)验证码工具类:

package com.dfbz.util;

import javax.imageio.ImageIO;
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.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Date;
import java.util.Random;

/**
 * @author lscl
 * @version 1.0
 * @intro: 生成验证码
 */
@WebServlet("/generateCode")
public class GenerateImageCodeServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private static final char[] CH = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray();
    private static final int IMAGE_WIDTH = 73;
    private static final int IMAGE_HEIGHT = 28;
    private static final int LINE_NUM = 30;
    private static final int RANDOM_NUM = 4;
    Random random = new Random();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("image/jpg");//设置相应类型,告诉浏览器输出的内容为图片
        response.setHeader("Pragma", "No-cache");//设置响应头信息,告诉浏览器不要缓存此内容
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expire", new Date().getTime());

        BufferedImage bi = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_BGR);
        Graphics g = bi.getGraphics();
        g.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
        g.setColor(getRandomColor(110, 133));
        g.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18));
        // 绘制干扰线
        for (int i = 1; i <= LINE_NUM; i++) {
            int x = random.nextInt(IMAGE_WIDTH);
            int y = random.nextInt(IMAGE_HEIGHT);
            int xl = random.nextInt(13);
            int yl = random.nextInt(15);
            g.drawLine(x, y, x + xl, y + yl);
        }

        // 绘制随机字符
        StringBuilder sb = new StringBuilder();
        String str = null;
        for (int i = 0; i < RANDOM_NUM; i++) {
            g.setFont(new Font("Fixedsys", Font.CENTER_BASELINE, 18));
            g.setColor(new Color(random.nextInt(101), random.nextInt(111), random.nextInt(121)));
            str = CH[random.nextInt(CH.length)] + "";
            g.drawString(str, 13 * i, 16);
            g.translate(random.nextInt(3), random.nextInt(3));
            sb.append(str);
        }
        g.dispose();
        request.getSession().setAttribute("safeCode", sb.toString());
        ImageIO.write(bi, "JPG", response.getOutputStream());
    }

    /**
     * 获得颜色
     */
    private Color getRandomColor(int fc, int bc) {
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc - 16);
        int g = fc + random.nextInt(bc - fc - 14);
        int b = fc + random.nextInt(bc - fc - 18);
        return new Color(r, g, b);
    }
}

7)静态页面:

  • 登录页面:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>

<form action="/login" method="post">
    用户名:<input type="text" name="username">
    <hr>
    密码:<input type="password" name="password">
    <hr>
    验证码:<input type="text" name="checkCode">
        <a href="javascript:location.reload()">
            <img src="generateCode" alt="">
        </a>
    <hr>
    <input type="submit" value="登录">
</form>
</body>
</html>

在这里插入图片描述

  • 转账页面:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>转账页面</title>
</head>
<body>
<!--这个页面必须要登录才可以访问-->
<form action="/transfer" method="post">
    请输入收款方账号: <input type="text" placeholder="请输入收款方账号" name="username">
    <hr>
    请输入转账金额: <input type="text" placeholder="请输入转账金额" name="money">
    <hr>
    <input type="submit" value="转账">
</form>
</body>
</html>

在这里插入图片描述

3.2 代码实现

3.2.1 用户登录

  • LoginServlet:
package com.dfbz.servlet;

import com.dfbz.entity.Account;
import com.dfbz.util.DataSourceUtils;

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.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author lscl
 * @version 1.0
 * @intro: 用户登录
 */
@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 收集前端的参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String checkCode = request.getParameter("checkCode");

        // 获取session对象
        HttpSession session = request.getSession();

        response.setContentType("text/html;charset=utf8");
        PrintWriter writer = response.getWriter();

        // 判断验证码是否正确
        if (!session.getAttribute("safeCode").equals(checkCode)) {
            writer.println("<h1>验证错误!登录失败!</h1>");
            writer.println("<a href='/login.html'>重新登录</a>");
            return;
        }


        // 代码走到这里说明验证码正确

        // 根据用户名查询用户
        Account account = findByUsername(username);

        if(account == null || !account.getPassword().equals(password)){
            writer.println("<h1>用户名或密码错误!</h1>");
            writer.println("<a href='/login.html'>重新登录</a>");
            return;
        }

        // 代码走到这里说明验证码/用户名/密码都正确

        // 存入登录成功的用户信息存入session
        session.setAttribute("loginAccount", account);

        // 重定向到转账页面
        response.sendRedirect(request.getContextPath()+"/transfer.html");
    }

    /**
     * 根据账户名查询账户
     *
     * @param username
     * @return
     */
    public Account findByUsername(String username) {

        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            // 1. 从连接池中获取一个连接
            connection = DataSourceUtils.getConnection();

            // 2. 获取一个数据库连接
            ps = connection.prepareStatement("select * from account where username=?");
            ps.setString(1, username);

            // 3. 执行查询获取结果集
            rs = ps.executeQuery();

            if (rs.next()) {

                // 能查询到记录说明传递进来的username肯定等于查询行中的username
                Integer id = rs.getInt("id");
                String password = rs.getString("password");
                Double money = rs.getDouble("money");

                return new Account(id, username, password, money);
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 释放连接
            DataSourceUtils.close(connection,ps,rs);
        }

        return null;
    }
}

3.2.2 转账

  • TransferServlet
package com.dfbz.servlet;

import com.dfbz.entity.Account;
import com.dfbz.util.DataSourceUtils;

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.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet("/transfer")
public class TransferServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        resp.setContentType("text/html;charset=utf8");
        PrintWriter out = resp.getWriter();


        // 1. 获取session
        HttpSession session = req.getSession();

        // 2. 获取session中的account信息
        Account loginAccount = (Account) session.getAttribute("loginAccount");


        if (loginAccount == null) {
            // 说明用户没有登录
            out.println("<h1>请先登录</h1>");
            out.println("<a href='/login.html'>去登录</a>");
            return;
        }

        // 获取前端传递过来的收款账号
        String receiveName = req.getParameter("username");

        // 获取前端传递过来的转账金额
        Double money = Double.parseDouble(req.getParameter("money"));

        // 进行转账
        Boolean flag = transfer(loginAccount.getUsername(), receiveName, money);

        if (flag) {
            resp.getWriter().write("<h1>转账成功!</h1>");

        } else {
            resp.getWriter().write("转账失败!");
        }

        resp.getWriter().write("<a href='/transfer.html'>去转账</a>");
    }

    /**
     * 转账
     *
     * @param transferName 转账方
     * @param receiveName  收款方
     * @param money        转账的金额
     */
    public Boolean transfer(String transferName, String receiveName, Double money) {

        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet rs = null;


        try {
            connection = DataSourceUtils.getConnection();
            connection.setAutoCommit(false);                // 开启手动提交

            // 转账SQL
            ps = connection.prepareStatement("update account set money=money+? where username=?");

            ps.setDouble(1, money);
            ps.setString(2, receiveName);

            // 执行加钱操作
            ps.executeUpdate();


            ps = connection.prepareStatement("update account set money=money-? where username=?");
            ps.setDouble(1, money);
            ps.setString(2, transferName);

            // 执行扣款操作
            ps.executeUpdate();

            connection.commit();                // 提交事务

            return true;           // 转账成功
        } catch (SQLException e) {
            e.printStackTrace();

            try {
                if (connection != null) {
                    connection.rollback();      // 出现异常回滚事务
                }
            } catch (SQLException e2) {
                e2.printStackTrace();
            }

            return false;                       // 转账失败
        } finally {
            // 释放连接
            DataSourceUtils.close(connection, ps, rs);
        }

    }
}

3.2.3 用户注销

  • LogoutServlet
package com.dfbz.servlet;

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;


/**
 * @author lscl
 * @version 1.0
 * @intro: 用户注销
 */
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        HttpSession session = request.getSession();

        // 销毁session
        session.invalidate();

        // 重定向到登录页面
        response.sendRedirect(request.getContextPath() + "/login.html");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/153144.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Deep tabular data learning

ContentsWhy do tree-based models still outperform deep learning on tabular data?Tabular Data (表格数据)NN 处理表格数据的挑战模型的归纳偏置有何不同&#xff1f;模型本质有何不同&#xff1f;做个小结[CIKM 2019] AutoInt: Automatic Feature Interaction Learning v…

【前端】Vue项目:旅游App-(13)home:热门数据的网络请求、store和显示

文章目录目标过程与代码页面html与css获取数据网络请求store展示数据效果总代码修改或添加的文件service的home.jsservice的index.jsstore的home.jshome.vue本项目博客总结&#xff1a;【前端】Vue项目&#xff1a;旅游App-博客总结 目标 天河区、番禺区…等数据是动态的&…

智能物流仓储机器人|海格里斯HEGERLS货物夹抱式防倾倒装置四向穿梭车

在电商以及物流服务行业迅猛发展的当下&#xff0c;自动化立体仓库表面了更加强大的生命力与适应能力。谋求人力节省以及长远发展的电商也需要自动化立体仓库来帮助他们实现这一目标。在现有的技术中会利用穿梭车进行货物转运&#xff0c;但是在转运过程中&#xff0c;由于货物…

浅谈Web渗透

中华人民共和国网络安全法 第二十七条 任何个人和组织不得从事非法侵入他人网络、干扰他人网络正常功能、窃取网络数据等危害网络安全的活动:不得提供专门用于从事侵入网络、干扰网络正常功能及防护措施、窃取网络数据等危害网络安全活动的程序、工具&#xff0c;明知他人从事危…

“一秒”读懂串扰对信号传输时延的影响

作者&#xff1a;一博科技高速先生成员 刘春在前几期的文章“为什么DDR走线要走同组同层&#xff1f;”中&#xff0c;我们了解了信号在传输线上的传输速度以及微带线与带状线传输的时延差异。同时也有很多热情的网友对影响传输线时延情况给出了各自的见解&#xff0c;比如串扰…

经营性房产的管理范围

经营性房屋是指商业用房或生产用房&#xff0c;是学校出租给外单位使用的房屋&#xff0c;数图互通经营性房屋管理功能范围包括&#xff1a; 定义经营性房屋&#xff08;范围&#xff09;&#xff1a;定义那些房产是可以租赁。 租赁房屋基本信息&#xff1a;建立商铺&#xff…

【ArcGIS Pro微课1000例】0025:大型商场选择典型案例(缓冲区分析+叠置分析)

文章目录 一、实验分析二、实验数据三、实验过程一、实验分析 合理的商场位置有利于商家营业,方便人们购物。商场选址问题需要考虑很多因素,如地理位置、居民点分布、停车场分布、交通情况等。综合把握这些影响因素对商场的选址显得尤为重要。 二、实验数据 加载专栏实验数…

赞赞赞!融云收获行业媒体「组团打 Call」

近期&#xff0c;融云又收获了来自行业和媒体的一波集中“点赞”&#xff0c;分别是—— 产品方面来自掘金的年度创新产品奖&#xff1b; 技术方面来自思否的年度技术团队、掘金的人气技术团队荣誉&#xff1b; 出海方面入选爱分析出海通信厂商全景报告。 打 Call 组团来袭…

revit绘制扶手楼梯生成,转角断开怎么解决?

一、Revit中楼梯扶手连接技巧 在用revit绘制楼梯时扶手可自动生成&#xff0c;但是生成的楼梯扶手在转角处却是断开的。 通常有几种情况&#xff0c;如图1所示情况 碰到这种情况时&#xff0c;可以选择楼梯扶手然后点编辑命令&#xff0c;将原有的楼梯扶手线向后移动一定的距离…

手把手教你如何通过Java给图片添加文字和图片水印

前言 最近工作上有个需求&#xff0c;动态生成一张图片&#xff0c;具体来说就是基于模版图片动态添加文字和图片&#xff08;文字内容不同&#xff0c;图片数目不同&#xff09;&#xff0c;其中文字大小不全一样&#xff0c;且对位置有所要求。 本文将剖析多个技术方案来实…

ElementUI

一、axios拦截器1、axios模块的作用&#xff1a;是对基于http请求的封装。在浏览器对异步请求对象XMLHttpRequest进行封装2、拦截器&#xff1a;&#xff08;1&#xff09;请求拦截器&#xff1a;对客户端发起的请求进行统一的前期处理&#xff08;token、时间戳、cookie等&…

【Maven自动化构建工具】 | 项目管理工具

目录 第1章&#xff1a;Maven简介 1. 传统项目开发存在的问题 2. Maven 概述 3. Maven核心概念 4. 安装 Maven 环境 第2 章 Maven 的核心概念 1. Maven 工程约定目录结构 2. 仓库概念 3. POM文件 4. 坐标 5. 依赖 6. Maven的生命周期、命令和插件 第 3 章 Maven…

[JavaEE初阶] 内存可见性问题----volatile与wait(),notify()的使用

读书要趁黑发早,白首不悔少当时 文章目录1. 什么是内存可见性问题2. 避免内存可见性问题-----volatile(易变的)3. 需要注意的点4. wait()与notify()的使用4.1 控制两个线程执行顺序4.2 控制多个线程执行顺序4.3 wait()与sleep()的区别总结1. 什么是内存可见性问题 在线程A在读…

后端人眼中的Vue(四)

七、Vue生命周期 ​ Vue的生命周期指的是Vue实例在页面中创建到销毁整个过程。Vue提供了在各个生命周期的钩子&#xff0c;钩子也叫Vue生命周期函数。这些生命周期函数是伴随着Vue实例创建、销毁的过程中自动触发的&#xff08;不需要人为手动触发&#xff09;。Vue实例生命周期…

Leetcode:106. 从中序与后序遍历序列构造二叉树、105. 从前序与中序遍历序列构造二叉树(C++)

目录 106. 从中序与后序遍历序列构造二叉树&#xff1a; 问题描述&#xff1a; 实现代码与解析&#xff1a; 切割法&#xff08;递归&#xff09;&#xff1a; 原理思路&#xff1a; 索引版本&#xff1a; 105. 从前序与中序遍历序列构造二叉树&#xff1a; 问题描述&am…

zookeeper单节点部署

kafkazookeeper单节点部署及注意事项 事前准备&#xff1a; 1、一台Linux服务器或者是一台虚拟机 2、准备好JDK环境 3、安装好wget&#xff08;当然也可以不用这个&#xff0c;只是用于下载安装包的一个工具&#xff0c;所以能下载好包就是没问题的&#xff09; 4、需要了解vim…

Java面试题每日10问(4)

Core Java - OOPs Concepts: Inheritance Interview Questions 1. Why use inheritance in java? For Method Overriding (so runtime polymorphism can be achieved).For Code Reusability. Terms used in Inheritance Class: –A class is a group of objects which have c…

皕杰报表点击导出按钮后网页变空白问题

有人反映使用皕杰报表导出时&#xff0c;点击导出按钮后网页变成了空白&#xff0c;然后就没有反应了。看tomcat控制台也没有错误信息&#xff0c;似乎遇到了一个很难缠的问题&#xff0c;没有错误信息却卡滞了&#xff0c;这个问题怎么解决呢&#xff1f; 还是要从tomcat的日志…

盘点微服务架构下的诸多身份验证方式

联合作者&#xff1a;罗泽轩&#xff0c;API7.ai 技术专家、Apache APISIX PMC 成员 联合作者&#xff1a;赵士瑞&#xff0c;API7.ai 技术工程师&#xff0c;Apache APISIX Committer 身份认证是授予用户访问系统并授予使用系统的必要权限的过程。而提供了这一功能的服务&…

指针详解——高级指针的解析及应用

目录 &#x1f411;指针的初步了解 &#x1f402;指针的深入认识 &#x1f99b;1.指针数组 &#x1f400;指针数组的介绍 &#x1f400;指针数组的用法介绍 &#x1f42b;2.数组指针 &#x1f98c;数组指针的介绍以及使用 &#x1f9ae;3.函数指针 &#x1f408;函数指针的介绍…