> 文章列表 > Cookie 和 Session

Cookie 和 Session

Cookie 和 Session

前言

在 Servlet 中,对于 Cookie 和 Session 有很好的支持!
所以,我们就可以基于 Servlet 里面提供的 Cookie 和 Session 的 API,来进行会话管理类似的操作。

一、Cookie 和 Session 介绍

Cookie 的几个问题
1.Cookie 是个啥?
        浏览器提供的持久化存储数据的机制~~
2.Cookie 从哪里来?   
        Cookie 从服务器返回给浏览器的。服务器代码中由程序猿决定要把啥样的信息保存到客户端这边~~通过 HTTP 响应的 Set-Cookie 字段, 把键值对写回去即可~~
3.Cookie 到哪里去?
        Cookie 会在后续浏览器访问服务器的时候带到请求的 header 中发给服务器~~
        (为啥要这么折腾?? 服务器不是只给一个客户端提供服务,同一时刻要处理多个客户端的请求~~此时服务器就可以通过 cookie 中的值,来识别当前客户端是谁,当前客户端的服务提供到哪个环节了.(客户端借助 cookie 自报家门))
4.Cookie 存储在哪里?
        存储在浏览器(客户端)所在主机的硬盘中
        浏览器会根据 域名 来分别存储~~

回忆之前的例子:

  1. 到了医院先挂号. 挂号时候需要提供身份证, 同时得到了一张 “就诊卡”, 这个就诊卡就相当于患者的 “令牌”(相当于是 cookie,里面存储这用户的详细信息).
  2. 后续去各个科室进行检查, 诊断, 开药等操作, 都不必再出示身份证了, 只要凭就诊卡即可识别出当前患者的身份.
  3. 看完病了之后, 不想要就诊卡了, 就可以注销这个卡. 此时患者的身份和就诊卡的关联就销毁了. (类似于网站的注销操作)
  4. 又来看病, 可以办一张新的就诊卡, 此时就得到了一个新的 “令牌”

但其实在实际情况中,使用的是一个 session 的方式来保存的。

毕竟一个人,他可能不止一张卡,而且卡可能会丢。
因此将身份信息全部存储到卡上并不安全,所以,卡上只需要存储一个会话窗口编号sessionId

通过这个会话窗口编号,就可以打开对应的窗口,调取对应的信息。
也就是说:
每一张卡 相当于 开启了 一个会话窗口,都可以通过这个会话窗口来获取自身的详细信息。


1.2 Cookie 和 Session 的区别:

另外,这里我们补充一点:
Cookie 和 Session 的区别:

  • Cookie 是客户端的存储机制. Session 是服务器端的存储机制.
  • Cookie 里面可以存各种键值对(还可以存别的)Session 则专门用来保存用户的身份信息
  • Cookie 完全可以单独使用,不搭配 session (实现非 登陆 场景下)Session 也可以不搭配 Cookie 使用.(手机 app 登陆服务器,服务器也需要 Session,此时就没有Cookie 的概念)
  • Cookie 跟浏览器强相关的,Cookie 是属于 HTTP 协议中的一个部分。Session 则可以和 HTTP 无关(TCP, websocket ... 也可以用 session)

二、核心方法

2.1 HttpServletRequest 类中的相关方法

2.1.1 getSession 方法

getSession 方法,既能用于获取到服务器上的会话,也能用于创建会话。

它的具体行为,取决于参数:

  • 如果参数为 true,会话不存在,则创建一个会话
  • 如果参数为false,会话不存在,则返回一个null 

(拿着请求中 cookie 里的 sessionld 查一下哈希表)

这就好比:我们去医院看病。
但是这个医院,我们是第一次来。
肯定是要先挂个号,那么人家会问你有没有就诊卡?
如果你有,人家就不用给你办卡了。反之,人家就会根据你的身份信息,给你办一张就诊卡。

大家要明确:
在办卡之后,你拿到的不光是一张卡,还在该医院的服务器上存了一份档案。
这份档案就相当于是一个会话,卡上存储的就是 这个会话窗口的id号(sessionId)。

在实际情况中,调用 getSession 的时候具体做什么?
1、创建会话(买一张就诊卡)

首先先获取到请求中 cookie 里面的 sessionId 字段。
sessionId 就相当于是会话的身份标识。
然后我们去判定这个sessionId 是否在当前服务器上存在。
如果不存在,则进入创建会话 逻辑 / 操作。


创建会话:会创建一个 HttpSession 对象,并且生成一个 sessionId。
sessionId 是一个很长的数字,通常是用 十六进制来表示的。
这个数字能够保证唯一性:它这里面有一些列相关的算法,能够生成一个唯一的sessionId,然后这个Id 就作为我们当前会话的身份标识。

接下来,就把 sessionId 作为 key,把这个 HttpSession 对象,作为 value。
把这个键值对,给保存到 服务器内存 的一个“哈希表” 这样的结构中。
这里的“哈希表”:只是表示一个类似 哈希表的数据结构。
也就是说,一定是一个键值对结构,但是 是不是 哈希表 就不能保证了。

再然后,服务器就会返回一个 HTTP 响应,把 sessionId 通过 Set-Cookie 字段 返回给浏览器。
然后,浏览器就可以保存这个 sessionId 到 cookie中了。

2、获取会话

先获取到请求中的 cookie 里面的 sessionId 字段,也就是会话的身份标识。
判断这个 sessionId 是否在当前服务器上存在,也就是遍历“哈希表”中key,来判断这个 sessionId 是否包含在其中。
如果有,就直接调出 sessionId 对应的 HttpSession对象,并且通过返回值返回去。
会话,我们搞清楚了。
我们再来谈谈 HttpSession是什么?
HttpSession,其实就是 创建会话的时候,需要生成的一个关键对象。
这个对象本质上也是一个“键值对”的json结构。
注意! 由于 HttpSession 对象是一个键值对结构,所以允许程序员 对 HttpSession 对象进行套娃操作!
这和我前面讲的 表白墙 中获取所有信息记录的响应格式json是一样的。
一个 json 对象 可以让 另一个json 对象,作为自己keys中的一个value值。
HttpSession 也是如此,可以让另一个 HttpSession 对象作为 自己keys中的一个value值。
HttpSession键值对结构中,key必须是字符串类型,value 是一个 Object类型。 

getCookies()方法

        getCookies()方法会返回一个数组,包含客户端发送该请求的所有的 Cookie 对象.会自动把Cookie 中的格式解析成键值对 

 2.2 Cookie 类中的相关方法 

2.3 HttpServletResponse 类中的相关方法 

响应中就可以根据 addCookie 这个方法,来添加一个 Cookie 信息(键值对)到 响应报文中 ,

这里添加进来的键值对,就会作为 HTTP 响应中的 Set-Cookie 字段来表示。

换个说法:把一个cookie键值对 转换成 字符串 格式,通过 Set-Cookie 来返回到 客户端(浏览器)这里。 

 2.4 HttpSession 类中的相关方法

一个 HttpSession 对象里面包含多个键值对. 我们可以往 HttpSession 中存任何我们需要的信息


 三、实战案例:网页登录 

3.1 理解例子逻辑

在编写案例代码之前,我们需要先约定前后端交互接口。
根据上面的逻辑图,一共有两组交互:
        1、登录页面
        2、主页面

要明白:针对前后端交互接口,这里有很多种约定方式。这么多约定方式,使用哪种都可以!一定要确定出一种约定方式,然后前后端代码的编写,就需要围绕着这一种约定的方式进行编写。如果如果不按照这种方式去写,代码会出现很多问题!
比如:请求是GET,但是服务器中处理请求的方法是POST,那么就会触发405 

 3.2 正式编写代码

 首先我们来写一个简易风格的登录页面

接下来,就是编写一个服务器程序,用于处理登录页面发送的请求。 

  编写服务器返回主页的逻辑 

 

效果展示

先来看一下,登录成功的效果。

 

登录失败的效果:

代码

loginServlet

@WebServlet("/login")
public class loginServlet extends HelloServlet{@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        1、处理用户请求String username = req.getParameter("username");String password = req.getParameter("password");
//        2、判断用户名 密码是否正确
//        正常来说是要放到数据库里进行存取的
//        注册的时候会把用户名和密码写入数据库
//        后续登录的时候,根据用户名看密码是否正确
//        这里为了简单就把密码写死
//        假设用户名为:zhangsan 密码为:123if("zhangsan".equals(username) && "123".equals(password)){//为了避免 username 和 password为空,而造成空指针异常,把写死的密码作// 登录成功//创建会话,并保存的身份信息HttpSession httpSession = req.getSession(true);
//            往会话中存储键值对(关键信息),必要的身份信息httpSession.setAttribute("username",username);resp.sendRedirect("index");}else {resp.getWriter().write("login error!");}}
}

indexservlet

@WebServlet("/index")
public class indexServlet extends HelloServlet{
//    doGet 要做的事情: 这回主页,主页就是简单的html
//    返回页面的前提,需要获得 用户名
//    也就是需要从 Httpsession 中拿到。
//    因为在上个服务器代码中,我们把用户名写作会话的属性,存入到会话中了。
//    此处的 getSession 的参数 必须为false!因为前面在登录过程中,已经创建过会话了
//    此处是要直接获取之前的会话。@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpSession httpSession = req.getSession(false);String username = (String)httpSession.getAttribute("username");resp.setContentType("text/html;charset=utf8");resp.getWriter().write("<H3>欢迎"+username+"<H3>");}
}

 login.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>登录页面</title>
</head>
<body><form action="login" method="post"><!-- name属性的值就是key,输入框中的数据就是value --><input type="text" name="username" ><input type="password" name="password" ><input type="submit" value="登录"></form>
</body>
</html>

服装搭配