08_会话技术

文章目录

  • 前置知识点
  • 会话技术Conversation
    • 客户端技术Cookie
      • Cookie的格式
      • Cookie的优缺点
      • 构造Cookie信息
        • 通过浏览器构造Cookie
        • 通过Postman构造Cookie
        • 通过服务器构造Cookie
      • 获取Cookie信息
      • Cookie中的信息
        • Path
        • Domain
        • MaxAge
      • 案例(cookie相关)
    • 服务器技术Session
      • 提供Session
      • 获取Session
      • 使用Session
      • Session的生命周期
    • 常见问题
    • 案例(重构登录案例,并且增加注销功能)
    • Cookie和Session的区别与联系
    • 数据共享

前置知识点

  • 响应头的设置
    • response.setHeader(String,String)
  • Postman中设置请求头
    • Header
  • URL编码
    • 浏览器能够完成编解码的工作
    • 比如我们发送一个Get请求(通过浏览器地址栏),其中的请求参数有中文,这个中文浏览器会自动进行URL编码 → Servlet中使用request获得请求参数的值,这个值是中文,说明request已经解码

eg:

public class URLEncodingDecodingExecution {public static void main(String[] args) throws UnsupportedEncodingException {// 提供编码的方法String encode = URLEncoder.encode("张三", "utf-8");System.out.println(encode); //%E5%BC%A0%E4%B8%89// 提供解码的方法String decode = URLDecoder.decode("%E5%BC%A0%E4%B8%89", "utf-8");System.out.println(decode); // 张三}
}

会话技术Conversation

  • 同一个客户端向服务器中发送的多个请求,需要信息共享
  • 在做服务器开发过程中,客户端和服务器之间,会有请求报文和响应报文
    • 客户端给服务器发送请求:请求报文
    • 服务器给客户端发送响应:响应报文
  • HTTP协议的无状态性,会导致服务器无法区分信息来自于哪个客户端
    • 无状态性:协议对于交互性场景没有记忆能力
    • eg:http://localhost:8080/demo1/user/list/user?userid=25;
    • 会产生弊端:
      • 用户信息不安全
      • 客户端和浏览器每一次发送请求的时候都需要携带请求参数比较繁琐
  • 引入会话技术,最重要的事情就是让服务器知道客户端是谁
  • 客户端直接携带确切的信息,这个就是客户端技术
    • 客户端技术:Cookie
    • eg:请求报文中携带对应的信息username=zs
  • 如果是通过客户端提供的编号,进而在服务器中进一步获得信息,那么这个就是服务器技术
    • 服务器技术:Session
    • eg:服务器中存储编号30012:zs
  • 服务器会话技术,是在客户端会话技术基础上的

客户端技术Cookie

  • 携带信息:客户端(浏览器)在向服务器发起请求的时候直接携带了信息,这些信息是通过请求头中一个特殊的请求头(特殊的请求头叫Cookie)携带的

Cookie的格式

  • Cookie:key1=value1;key2=value2
    • 携带的是键值对信息,携带的键值对信息都是字符串信息
    • 可以携带多组键值对信息,如果携带多组,中间使用分号分隔开

Cookie的优缺点

  • 优点:小巧、减轻了服务器压力、可以很轻松的实现多台主机、多个应用下的资源共享
  • 缺点:存储容量有限制 4kb、数据类型有限制只可以存储一些非敏感数据

构造Cookie信息

通过浏览器构造Cookie
  1. 打开开发者工具,快捷键F12
  2. 找应用程序(Application)
  3. 应用程序里找存储(Storage)里的Cookie
  4. 可以通过Fiddler获取Cookie值

通过Postman构造Cookie

eg:

  • 通过Postman进行创建
    在这里插入图片描述

  • 通过Fiddler进行抓取
    在这里插入图片描述


通过服务器构造Cookie

客户端 → 服务器,请求
服务器 → 客户端,响应(并且携带响应头set-cookie:key=value
客户端 → 服务器,请求(并且携带请求头cookie:key=value
客户端 → 服务器,请求(并且携带请求头cookie:key=value

eg:

/*** 发送请求* http://localhost:8080/demo1/cookie/set?username=zs*/
@WebServlet("/cookie/set")
public class CookieSetServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {String username = request.getParameter("username");response.setHeader("set-cookie","username=" + username);response.getWriter().println("cookie set finish, please check cookie");}
}

获取Cookie信息

  • Request中提供了直接获得Cookie的方法
    • Cookie[] cookies = request.getCookies();
  • 单个Cookie,我们先获得其键值对信息
    • 键:cookie.getName()
    • 值:cookie.getValue()

eg:

/*** 获取所有的cookie,然后进行打印*/@WebServlet("/cookie/fetch")
public class CookieFetchServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// String cookie = request.getHeader("cookie");Cookie[] cookies = request.getCookies();for (Cookie cookie : cookies) {String name = cookie.getName();String value = cookie.getValue();System.out.println(name + " : " + value);}/*** username : ls* Pycharm-89c7dd8b : 47f9fb69-6795-467d-875b-0a44c63e1e97* Idea-75c3c9a : b19ee743-c677-4bab-b7bd-673c42a25ce0*/}
}

Cookie中的信息

Cookie这个实例中的封装信息:

信息方法说明
name有参构造方法核心值
value有参构造方法核心值
PathsetPath(String)设置Cookie的有效URI
DomainsetDomain(String)域名,做Cookie的共享
MaxAgesetMaxAge(int)cookie的有效时间、设置过期时间,单位是秒,如果超过这个时间Cookie就会过期
Path
  • 客户端发起的请求,是某个path的话,客户端在发起请求的时候才会携带这个cookie信息
  • 这个Path就是设置cookie的执行路径
    • cookie.setPath(String path)

eg:

@WebServlet("/cookie/path")
public class CookiePathServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse response)throws ServletException, IOException {// response.setHeader("set-cookie","xxxx=" + xxxx);Cookie cookie = new Cookie("username", "ls");// 默认Path:URI去除掉最后一级 uri=demo1/cookie/path// ---> path=demo1/cookie// 手动构造Pathcookie.setPath("/demo1"); // 则/demo1目录下的cookie都有username=lsresponse.addCookie(cookie);}
}
Domain
  • 设置域名或ip,用来说不同域名下Cookie共享
    • 如果设置了Cookie的父域名,子域名下的请求可以共享父域名下的Cookie
    • eg:
      • 父域名:https://www.jd.com
      • 子域名:https://www.miaosha.jd.com
  • 不能设置和当前域名无关的domain,比如访问localhost时候设置一个ccc.com这样的domain是不可以的,否则浏览器会直接无视

eg:

@WebServlet("/cookie/domain")
public class CookieDomainServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse response)throws ServletException, IOException {Cookie cookie = new Cookie("username", "ww");cookie.setDomain("ccc.com");response.addCookie(cookie);}
}
MaxAge
  • 如果没有设置,则默认情况下存在于浏览器的内存中。关闭浏览器,则cookie信息失效
  • 设置一个maxAge=正数的时间,表示会在硬盘上面存活多少秒

eg:

@WebServlet("/cookie/maxage")
public class CookieMaxAgeServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse response)throws ServletException, IOException {Cookie cookie = new Cookie("userid", "251");cookie.setMaxAge(5); // 设置一个过期时间,如果没有设置,则浏览器关闭时会被清楚// 这个只有浏览器知道response.addCookie(cookie);}
}

案例(cookie相关)

在访问一个请求的时候,输出上一次访问的时间

思路:

  1. 访问该请求的时候,生成时间,放入到cookie中
  2. 访问该请求的时候,从cookie中取出这个时间,并且使用response做响应

eg:

@WebServlet("/last/access")
public class LastAccessServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 获取cookie中的上一次访问时间// 假设设定了cookie的name的lastCookie[] cookies = request.getCookies();String lastDateString = null;if (cookies != null & cookies.length != 0) {for (Cookie cookie : cookies) {if ("last".equals(cookie.getName())) {lastDateString = cookie.getValue();}}}if(lastDateString != null) {String lastDateStringDecode = URLDecoder.decode(lastDateString, "utf-8");response.getWriter().println(lastDateStringDecode);}// 向浏览器的cookie中做当前时间的设置SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String nowDate = simpleDateFormat.format(new Date());String nowDateEncode = URLEncoder.encode(nowDate, "utf-8");response.addCookie(new Cookie("last", nowDateEncode));}
}

服务器技术Session

  • Session相当于每个用户存在服务器的保险柜,保险柜里可以存一些数据,这些数据也可以是敏感数据
  • 要获得保险柜要带着钥匙,如果钥匙丢了,就打不开这个保险柜

提供Session

  • Session不需要专门去提供,当我们获取Session的时候,其实就提供了Session给客户端
  • 在服务端获取Session的时候,其实会提供一个响应头set-cookie
    • JSESSIONID这样的一个key,其实就是保险柜的钥匙

获取Session

  • 获得Session可以使用Request提供的getSession方法
    • getSession()
      • 如果还没有创建Session,那么就创建一个Session
      • 如果已经有了Session,那么就返回这个Session
      • 当前这个客户端或用户第一个调用getSession方法的时候创建了Session
    • getSession(boolean create)
      • 如果create的值为true则同上
      • 如果create的值为false,那么如果有了Session则返回Session对象,如果没有则返回null,就不会创建Session

实际上我们使用的无参方法更多一些

eg:

@WebServlet("/session/get")
public class SessionGetServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 根据JSESSIONID,不需要我们手动取获取,内部已经封装好了// 获取当前用户地session,如果当前用户没有session,则新建一个session// Set-Cookie: JSESSIONID=DA43DEBCD0DFF4BBFC4371558FE8C7B4HttpSession session = request.getSession();// 如果boolean类型参数为true,和无参的getSession方法是一样的// HttpSession session = request.getSession(true);// 如果为false,且当前用户有session,则返回,如果没有session,则返回空// HttpSession session = request.getSession(false);}
}

在这里插入图片描述

使用Session

  • Session其实就像一个保险柜 → key-value形式的保险柜
    • key:String
    • value:Object
    • 存储:setAttribute(String,Object)
    • 获取:getAttribute(String)

eg:

WebServlet("/session/save")
public class SessionSaveServlet extends HttpServlet {// localhost:8080/demo2/session/save?username=zs@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 传入一个用户名 -> 找到id,将id存在session里面String username = request.getParameter("username"); // 根据用户名可以找到该用户的id信息// 假定这个id是222Integer id = 222;HttpSession session = request.getSession();session.setAttribute("id",id);session.setAttribute("username", username);}
}
@WebServlet("/session/fetch")
public class SessionFetchServlet extends HttpServlet {// localhost:8080/demo2/session/fetch@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 从session中获取username和id信息HttpSession session = request.getSession();Integer id = (Integer) session.getAttribute("id");String username = (String) session.getAttribute("username");System.out.println(username + " : " + id); // zs : 222}
}

Session的生命周期

  • 对象生命周期:
    • 对象的创建:request.getSession()
    • 对象的销毁:关闭服务器、卸载应用
  • 数据的生命周期:
    • 数据的产生:session域
    • 数据的销毁:对象的销毁不会导致数据的销毁
      • 数据的销毁只有以下两种可能性:
        1. session有效期到达(默认是30min)
        2. 主动调用session.invalidate()方法-----用在注销的场景下

常见问题

  1. 关闭浏览器,Session是否被销毁?
    • 并没有
    • 只不过当前浏览器发起请求的时候,通常没有继续携带之前的JSESSIONID,但如果你仍然携带之前的JSESSIONID,那么仍然可以获取数据
  2. 服务器关闭,Session发生了什么?
    • Session对象会被销毁,并且会被序列化(序列化:把数据写到服务器里)
    • 当服务器重启时,会被反序列化(反序列化:把数据读出),重新加载到内容。
    • 当时要注意,重启前后的Session对象并不是同一个,但是JSESSIONID是同一个值
  3. Session失效,原因会是什么?
    • Session失效就是从Session中获取不到其存储的信息,则可以认为Session失效
      • 跨域请求Session不能共享
        • 跨域:多个应用之间的ip或端口号不同
          • 比如:前端地址localhost:9527与后端服务器地址localhost:8080
        • 每发起一次请求,就会新建一个新的Session
      • 过期或手动设置
      • 请求携带的JSESSIONID变了
  4. session底层是依赖于cookie的,但是如果浏览器禁用了cookie,那么session还可以使用吗?
    • 还可以。但是必须采用一种URL重写的方式。此时session的编号会附着在地址栏的后面,以;形式来进行分割
    • eg:localhost:8080/demo2/session/fetch;JSESSIONID=aaaabc2849ea

案例(重构登录案例,并且增加注销功能)

  • bean目录
@Data
public class Order {private Integer id;private String name;private Double price;private Integer userId;
}
@Data
public class User {private Integer id;private String username;private String password;private Integer age;private Date birthday;private Date createdate;private String mobile;}
  • mapper目录
public interface OrderMapper {List<Order> selectByUserId(Integer id);
}
public interface UserMapper {List<User> selectByUserNameAndPassword(@Param("username") String username, @Param("password") String password);User selectByPrimaryKey(Integer id);
}
<mapper namespace="com.coo1heisenberg.demo3.mapper.OrderMapper"><select id="selectByUserId" resultType="com.coo1heisenberg.demo3.bean.Order">select id, name, price, user_id as userId from test_product<where>user_id = #{user_id}</where></select>
</mapper>
<mapper namespace="com.coo1heisenberg.demo3.mapper.UserMapper"><select id="selectByUserNameAndPassword" resultType="com.coo1heisenberg.demo3.bean.User">select id, username, password, age, birthday, createDate, mobile from test_user<where>username = #{username} and password = #{password}</where></select><select id="selectByPrimaryKey" resultType="com.coo1heisenberg.demo3.bean.User">select id, username, password, age, birthday, createDate, mobile from test_user<where>id = #{id}</where></select>
</mapper>
  • servlet目录
/*** 提供一个list方法,登录成功才允许查看当前用户的订单信息,否则让其先登录*/
@WebServlet("/order/*")
public class OrderServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {process(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {process(request, response);}private void process(HttpServletRequest request, HttpServletResponse response) {String requestURI = request.getRequestURI();String operation = requestURI.substring(requestURI.lastIndexOf("/") + 1);switch (operation) {case "list":list(request, response);break;}}@SneakyThrowsprivate void list(HttpServletRequest request, HttpServletResponse response) {// 首先要保证当前用户已经登陆 ->  登录成功之后,session里才有用户信息// -> 可以判断session是否有用户信息HttpSession session = request.getSession();User user = (User) session.getAttribute("user");response.setContentType("text/html;charset=utf-8");if (user == null) {// 登录失败response.getWriter().println("登录失败,即将跳转登录页面");response.setHeader("refresh", "2;url=/demo3/login.html");} else {// 已经登录成功Integer userId = user.getId();OrderMapper orderMapper = MybatisUtil.getSqlSession().getMapper(OrderMapper.class);List<Order> orders = orderMapper.selectByUserId(userId);response.getWriter().println(orders);}}
}
@WebServlet("/user/*")
public class UserServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {process(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {process(req, resp);}private void process(HttpServletRequest request, HttpServletResponse response) {String requestURI = request.getRequestURI();String operation = null;operation = requestURI.substring(requestURI.lastIndexOf("/") + 1);switch (operation) {case "login":login(request, response);break;case "info":info(request, response);break;case "logout":logout(request,response);break;}}@SneakyThrowsprivate void logout(HttpServletRequest request, HttpServletResponse response) {// 做注销功能 localhost:8080/demo3/user/logoutHttpSession session = request.getSession();session.invalidate();response.setContentType("text/html;charset=utf-8");response.getWriter().println("已经注销");}@SneakyThrowsprivate void info(HttpServletRequest request, HttpServletResponse response) {HttpSession session = request.getSession();Object user = session.getAttribute("user");response.setContentType("text/html;charset=utf-8");if(user == null) {// 意味着之前未登录成功response.getWriter().println("还未登录,不能查看用户信息,请先登录,即将跳转");response.setHeader("refresh","2;url=/demo3/login.html");} else {// user != null// 意味着已经登录成功response.getWriter().println(user);}}@SneakyThrowsprivate void login(HttpServletRequest request, HttpServletResponse response) {// 1. 首先获得username和passwordString username = request.getParameter("username");String password = request.getParameter("password");// 2. 查询user记录UserMapper userMapper = MybatisUtil.getSqlSession().getMapper(UserMapper.class);List<User> users = userMapper.selectByUserNameAndPassword(username, password);// 3. 根据user记录判断登录状态response.setContentType("text/html;charset=utf-8");// 4. 判断登录状态 user的list是否为空if (users == null || users.size() == 0) {// 如果登陆失败(list == null || list.size() == 0)// 刷新到refresh --> 2;url=/demo2/login.html// 响应登录失败信息response.getWriter().println("登录失败,即将跳转登录页面");response.setHeader("refresh", "2;url=/demo3/login.html");} else {// 如果登录成功(list.size > 0)// 登录成功之后,将用户信息放在服务器Session里HttpSession session = request.getSession();session.setAttribute("user",users.get(0));response.getWriter().println("登录成功,可以访问其他请求");}}
}

Cookie和Session的区别与联系

  • 联系:Cookie和Session都是为了让服务端获取客户端提供的信息;提供的信息都是键值对形式的;Session技术是在Cookie技术的基础上进行的,都需要对请求头做处理
  • 区别:
    • 信息位置
      • Cookie是客户端技术,信息存储在客户端(浏览器),也意味着前端可以操作
      • Session是服务器技术,信息储存在服务器
    • 敏感性
      • Cookie共享的是常规信息,直接抓包获取的是对应的值
      • Session通常共享的是敏感信息,直接抓包获取的是id
    • 值类型
      • Cookie的值为String字符串
      • Session的值为Object
    • 跨应用
      • Cookie信息可以跨应用共享
      • Session信息局限于当前应用

数据共享

  • Request域:存在转发关系的Servlet之间的数据共享,比如Servlet和JSP数据共享
  • Context域:整个应用之中的数据共享,比如整个应用中的SqlSessionFactory、properties等
  • Session域:同一个用户(客户端)中的数据共享,比如登录之后的用户信息

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

<el-table>设置一列为固定字段,其他列为循环生成

<el-table :data"tableData" style"width: 100%"><el-table-columnprop"name"label"固定字段名":formatter"formatter"></el-table-column><el-table-columnv-for"(item, index) in wordsColumns…

Redis命令介绍

一、redis启动&#xff1a; 本地启动&#xff1a;redis-cli 远程启动&#xff1a;redis-cli -h host -p port -a password Redis 连接命令 1 AUTH password 验证密码是否正确 2 ECHO message 打印字符串 3 PING 查看服务是否运行 4 QUIT 关闭当前连接 5 SELECT index 切换…

Intellij IDEA安装配置Spark与运行

目录 Scala配置教程 配置Spark运行环境 编写Spark程序 1、包和导入 2、定义对象 3、主函数 4、创建Spark配置和上下文 5、定义输入文件路径 6、单词计数逻辑 7、输出结果 8、完整代码&#xff1a; Scala配置教程 IDEA配置Scala&#xff1a;教程 配置Spark运行环境 …

玫瑰图和雷达图(自备)

目录 玫瑰图 数据格式 绘图基础 绘图升级&#xff08;文本调整&#xff09; 玫瑰图 下载数据data/2020/2020-11-24 mirrors_rfordatascience/tidytuesday - 码云 - 开源中国 (gitee.com) R语言绘图—南丁格尔玫瑰图 - 知乎 (zhihu.com) 数据格式 rm(list ls()) libr…

【冥想X理工科思维】场景13:系统上线遭遇崩溃…

冥想音频合集&#xff1a;职场解压冥想音频 压力场景&#xff1a; 我搭建的系统刚刚在客户那边上线不到三天&#xff0c;系统就崩溃了&#xff0c;客户打电话来对我破口大骂&#xff0c;我该如何借助冥想调整面对客户时的压力&#xff1f; 点击看大图&#xff1a; 详细说明&…

深入Spark与LDA:大规模文本主题分析实战

使用LDA模型和Spark进行文本主题分析 本篇博客介绍了如何使用LDA&#xff08;潜在狄利克雷分配&#xff09;模型和Spark进行文本主题分析。我们的目标是从大量的用户评论中提取出主题。 1. 环境设置 首先&#xff0c;我们需要导入所需的库&#xff0c;包括jieba&#xff08;…

MySQL---触发器

一、介绍 触发器是与表有关的数据库对象&#xff0c;指在insert/update/delete之前(BEFORE)或之后(AFTER)&#xff0c;触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性, 日志记录 , 数据校验等操作 。 使用别名OLD和NEW来引用触…

OpenHarmony实战开发-从0到1实现购物应用页面

概述 OpenHarmony ArkUI框架提供了丰富的动画组件和接口&#xff0c;开发者可以根据实际场景和开发需求&#xff0c;选用丰富的动画组件和接口来实现不同的动画效果。 本Codelab中&#xff0c;我们会构建一个简易的购物应用。应用包含两级页面&#xff0c;分别是主页&#xf…

微信小程序被删除的文件一编译又回来了

一开始创建错了位置&#xff0c;就想着删除文件重新创建&#xff0c;但是没想到每次重新编译的时候&#xff0c;之前被删除的js、wsml文件就又回来了&#xff0c;后来发现是我在app.json中的pages里面的代码没有被删除。 因为我最开始创建错了&#xff0c;快捷创建了页面&#…

010——服务器开发环境搭建及开发方法(下)

目录 三、 第一个驱动程序 四、 buildroot 4.1 制作根文件系统 4.2 buildroot使用 五、 uboot 009——服务器开发环境搭建及开发方法&#xff08;上&#xff09;-CSDN博客 三、 第一个驱动程序 # 1. 使用不同的开发板内核时, 一定要修改KERN_DIR # 2. KERN_DIR中的内核要…

如何使用OpenHarmony实现视频暂停、播放、切换、倍速播放

介绍 本篇Codelab使用ArkTS语言实现视频播放器&#xff0c;主要包括主页面和视频播放页面&#xff0c;我们将一起完成以下功能&#xff1a; 获取本地视频和网络视频。通过AVPlayer进行视频播放。通过手势调节屏幕亮度和视频播放音量。 相关概念 AVPlayer&#xff1a;播放管理…

Kafka重要配置参数全面解读(重要)

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Kafka重要配置参数全面解读(重要 前言auto.create.topics.enableauto.leader.rebalance.enablelog.retention.{hour|minutes|ms}offsets.topic.num.partitions 和 offsets.topic.replication.factorlo…