Cookie和Session

目录

回顾Cookie

Cookie的典型应用

Cookie应用: 登录功能

Cookie和Session之间的关联和区别

代码示例: 模拟登录


回顾Cookie

前面介绍HTTP的时候已经有写到, 围绕Cookie的几个问题:

  1. Cookie是什么

Cookie是浏览器提供的持久化存储数据的机制.

像表白墙就是把数据存在服务器上, 但是有些时候也需要浏览器能够持续化存储数据. 那么"持久化"我们想到的就是存到文件上, 但是为了保证安全, 浏览器不允许一个网页直接访问文件系统, 毕竟这件事情太危险, 万一有恶意网站把电脑上的重要资料删除便得不偿失, 所以肯定不能这么办. 但是为了持久化存储数据, 浏览器就对持久化存储操作进行了封装, 不能让文件能够访问文件系统, 但是可以有限制的进行访问, 比如键值对这样的存储机制, 可以存储一些简单的数据, 但是想直接访问重要资料是不可能的.

  1. Cookie从哪里来

Cookie是从服务器返回给浏览器的.

服务器代码中由程序员决定要把什么样的信息保存到客户端, 然后通过HTTP响应的Set-Cookie字段, 把键值对写回去.

  1. Cookie到哪里去

Cookie是在客户端进行存储, 但是存储的目的是 下次请求服务器的时候自动带上, 所以Cookie会在后续浏览器访问服务器的时候带到请求的header中发给服务器.

注意: 服务器不是只给一个客户端提供服务. 同一时刻要处理多个客户端.那么此时就可以通过Cookie中的值来识别当前客户端是谁, 以及当前客户端的访问提供到哪个环节了...(客户端借助Cookie自报家门)

  1. Cookie存储在哪里

Cookie存储在浏览器(客户端)所在主机的硬盘上.

浏览器会根据 域名 来分别存储. 浏览器,每次访问一个网站, 都会根据这个网站的域名专门腾出一块空间来 就存储这个网站对应的Cookie.

Cookie的典型应用

Cookie的用途非常多, 其中最典型的应用就是 标识用户的身份信息.

Cookie应用: 登录功能

举一个例子, 很多网站都有一个登录功能.

以登录淘宝为例,

  1. 首先用户从浏览器发起请求获取到淘宝主页, 淘宝收到请求就把页面返回给浏览器.

这里要注意此时淘宝的服务器不知道用户的身份信息, 只是知道有一个客户端访问了它的主页,

  1. 接下来在主页上有一个登录按钮, 用户就可以通过这里输入用户名和密码进行登录.

用户发起登录请求, 淘宝收到登录请求之后, 返回登录成功的结果. 注意这里面淘宝就会针对登录操作查询数据库, 验证用户名和密码是否正确.

如果正确则登录成功, 然后淘宝会把当前用户的登录信息在内存中也保存一份, 同时给这个用户分配一个表示身份的序号(可能是个整数/字符串, 保证唯一性). 此时服务器会使用像hash表这样的结构, 把这个序号作为key, 身份信息作为value存储起来. 然后返回登录成功这个响应里面就会通过Set-Cookie把身份序号返回到客户端, 此时浏览器就会把身份序号保存到Cookie里了.

  1. 在后续的请求中, 服务器收到Cookie中的身份序号, 就会查询上述hash表, 判定用户是谁.

如果查到了, 就知道用户是谁了(可以避免重复输入账号密码); 如果没查到, 就会要求用户重新登录.

Cookie和Session之间的关联和区别

关联: 在网站登录功能中, 需要配合使用

区别:

  1. Cookie是客户端的存储机制. Session是服务器的存储机制.
  2. Cookie里面可以存各种键值对(还可以存别的). Session则专门用来保存用户的身份信息.

Cookie完全可以单独使用, 不搭配Session(实现非登录场景下)

Session也可以不搭配Cookie使用.(手机APP登录服务器, 服务器也需要Session, 此时就没有Cookie的概念) Cookie是跟浏览器强相关的.

Cookie是属于HTTP协议中的一个部分. Session则可以和HTTP无关(TCP, websocket... 也可以用Session)

代码示例: 模拟登录

在这个登录页中, 涉及到两个页面: 登录页面, 主页面.
登录页:
输入框输入用户名;
密码框输入密码;
登录按钮:点击按钮触发 登录请求->需要一个LoginServlet处理登录请求, 验证用户名密码是否 正确->登录成功, 跳转到 主页(另外一个页面, 可以是静态的html, 也可以是通过Servlet动态构建的页面)->IndexServlet, 在这个页面中显示用户的名字.

这个例子中涉及到两个页面:

  1. 登录页面
  2. 主页面

涉及到两个Servlet

  1. 处理登录的LoginServlet判定用户名密码
  2. 构造主页面的IndexServlet

我们在hello_servlet2中创建一个包login, 并创建LoginServlet类与IndexServlet类, 在webapp目录下创建前端页面.

  1. 编写登录页面
<form action="login" method="post">
    <input type="text" name="username">
    <br>
    <input type="password" name="password">
    <br>
    <input type="submit" value="提交">
</form>

启动服务器, 页面就可以通过浏览器访问了, 我们查看一下页面效果.

可以看到, 登录页面已经有了. 那么我们可以写一些数据, 就会触发登录请求.

但是由于这里的login还没有实现, 所以是404.

那么接下来我们继续编写后续代码.

登录页面我们已经处理好了, 那么这样一个登录 一点击提交, 就会触发一个POST请求, 我们可以查看这个请求是 什么样子的.

可以看到, 通过我们上述的form表单构造出了POST请求. 还看到Content-Type: application/x-www-form-urlencoded, 可见这个特定Content-Type是和form表单相关的. body部分为username=zhangsan&password=123, 就是通过键值对的方式进行表示的. 所以这一段请求是和前面的前端代码息息相关的.

注意下图的对应关系.

  1. 编写LoginServlet处理登录请求.
package login;

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("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        // 验证用户名密码是否正确.
        // 正常情况下, 用户名和密码会使用数据库来保存.
        // 此处直接约定, 用户名合法的是 zhangsan 和 lisi
        // 密码合法的都是 123

        if (!username.equals("zhangsan") && !username.equals("lisi")) {
            // 登录失败
            // 重定向到 登录页面
            System.out.println("登录失败, 用户名错误!");
            resp.sendRedirect("login.html");
            return;
        }
        if (!password.equals("123")) {
            // 登录失败
            System.out.println("登录失败, 密码错误!");
            resp.sendRedirect("login.html");
            return;
        }
        // 登录成功
        // 1. 创建一个会话
        HttpSession session = req.getSession(true);
        // 2. 把当前的用户名保存到会话中. 此处HttpSession又可以当成一个map使用
        session.setAttribute("username", username);
        // 3. 重定向到主页
        resp.sendRedirect("index");
    }
}

注意这里的代码也是要和上方请求形成对应关系.


理解创建会话的基本含义
HttpSession session = req.getSession(true);
会话实际上是一个键值对, key是sessionId, value是一个HttpSession对象.
每个客户端登录的时候都有一个这样的键值对.(会话)
服务器要管理多个这样的会话, 那么就可以搞一个哈希表, 把这些会话组织起来.
getSession(true)
判定当前请求是否已经有对应的会话: 拿着请求中Cookie里的sessionId查哈希表, 如果sessionId不存在, 或者没查到, 就 创建新会话, 并且插入到哈希表中. 如果查到了, 直接返回查到的结果.
创建过程:
1. 构造一个HttpSession对象
2. 构造唯一的sessionId
3. 把这个键值对插入哈希表
4. 把sessionId设置到响应报文Set-Cookie字段中.
注意HttpSession对象自己也是一个键值对. 可以通过setAttribute和getAttribute来存取键值对.

服务器是如何组织会话的

  1. 编写IndexServlet生成主页.
package login;

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("/index")
public class IndexServlet extends HttpServlet {
    // 通过重定向, 浏览器发起的是GET
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 先判定用户的登录状态
        // 如果用户未登录, 要求先登录; 已经登录, 根据会话中用户名来显示到页面上
        // 这里不会触发会话的创建
        HttpSession session = req.getSession(false);
        if (session == null) {
            // 未登录
            System.out.println("用户未登录!");
            resp.sendRedirect("login.html");
            return;
        }
        // 已经登录
        String username = (String) session.getAttribute("username");
        // HttpSession value的类型是Object, 需要手动强转成String(设定Object意为存各种类型的都可以)
        // 构造页面
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("欢迎 " + username + " 回来!");
    }
}

注意这里的对应关系,

在登录页面的代码中, 重定向指定了一个相对路径"index", 所以对应到IndexServlet中的WebServlet也要写index, 两者要匹配.

getSession(true): 会话存在就返回现成的, 不存在就创建.
getSession(false): 会话存在就返回现成的, 不存在就返回null(不创建).
String username = (String) session.getAttribute("username");
此处可以这样来取, 前提是前面的登录操作中已经存过.
即: session.setAttribute("username", username);
这两个session是根据同一个sessionId对应到的同一个HttpSession对象.

运行程序, 并使用fidder查看代码的基本效果.

输入用户名和密码, 点击提交, 这里就触发了Login请求和index请求.

可以看到, 此时已经提示: 欢迎 zhangsan 回来!

注意关注这里的请求交互过程.

第一次交互, 进行登录操作.

第一次交互的请求: 注意, 此时这次请求是没有Cookie的.

第一次交互的响应: 响应中的Set-Cookie的JSESSIONID就是前文所提到的sessionId, 注意它的值是一个唯一的数字.

Location: index 就是接下来要跳转到的index页面

第二次交互请求: 可以看到, 这是由浏览器的重定向功能所构造出的GET请求.

首先这个GET的路径 /index 和上面的Location: index 是相对应的.

然后这里要重点关注Cookie: 可以看到, 第二次请求就会带有Cookie, 可以知道Cookie中的JSESSIONID的值正好是上面通过Set-Cookie返回的值. 这个值就被交给服务器这边了.

这个请求到达服务器, Servlet就会在getSession方法中根据sessionId来查询HttpSession对象.也就是代码中的req.getSession(false);

第二次交互的响应:

只要完成登录之后, 后续请求多次服务器也都会带上刚才的Cookie值(sessionId)的.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值