Cookie 和 Session

        HTTP 协议属于 "无状态" 协议:默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下次通信之间没有直接的联系. 但是大多数时候, 我们是需要知道请求之间的关联关系的。例如登陆网站成功后, 第二次访问的时候服务器就能知道该请求之前是不是登陆过了。

        图中的 "令牌" (sessionid)通常就存储在 Cookie 字段中(相当于给了一张超市会员卡)。此时在服务器这边就需要记录令牌信息, 以及令牌对应的用户信息, 这个就是 Session 机制所做的工作.

会话机制 (Session)

        服务器同一时刻收到的请求是很多的. 服务器需要清除的区分清楚每个请求是从属于哪个用户, 就需要在服务器这边记录每个用户令牌以及用户的信息的对应关系。为了能让“会员卡”能正常使用,服务器这边就得记录 卡和用户 之间的关联信息。

        会话(Session)的本质就是一个 "哈希表", 存储了一些键值对结构. key 就是令牌的ID(token/sessionId), value 就是用户信息(用户信息可以根据需求灵活设计)。sessionId 是由服务器生成的一个 "唯一性字符串", 从 session 机制的角度来看, 这个唯一性字符串称为 "sessionId". 但是站在整个登录流程中看待, 也可以把这个唯一性字符串称为 "token"。sessionId 和 token 就可以理解成是同一个东西的不同叫法(不同视角的叫法).

        当用户登陆的时候, 服务器在 Session 中新增一个新记录, 并把 sessionId / token 返回给客户端. (通过 HTTP 响应中的 Set-Cookie 字段返回).

        客户端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId/ token. (通过 HTTP 请求中的 Cookie 字段带上).

        服务器收到请求之后, 根据请求中的 sessionId / token 在 Session 信息中获取到对应的用户信息, 再进行后续操作.

Cookie Session 的区别

        Cookie 是客户端的机制. Session 是服务器端的机制.

        Cookie 里面可以存各种键值对(也可以存别的),Session 则专门用来保存用户的身份信息。

        Cookie 和 Session 经常会在一起配合使用. 但是不是必须配合.(当实现 非登录 场景下的时候只使用 Cookie 也是可以的;当手机 app 登录服务器的时候只使用 Session 也是可以的,因为 Cookie 和 浏览器 是强相关的)

核心方法

HttpServletRequest 类中的相关方法

HttpServletResponse 类中的相关方法

HttpSession 类中的相关方法

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

Cookie 类中的相关方法

每个 Cookie 对象就是一个键值对.

        HTTP 的 Cooke 字段中存储的实际上是多组键值对. 每个键值对在 Servlet 中都对应了一个 Cookie 对象.

        通过 HttpServletRequest.getCookies() 获取到请求中的一系列 Cookie 键值对.

        通过 HttpServletResponse.addCookie() 可以向响应中添加新的 Cookie 键值对.

        sessionid 相当于 是会员卡的卡号,然后根据这个卡号就可以获得购买过哪些商品:A 商品 A 元,B 商品 B 元。 

实现用户登陆  

通过实现一个用户登录功能网页来使用上述一些方法来理解 session 和 cookie。

1. 创建 IndexServlet

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 {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        // 1. 判定当前用户是否已经登陆
        HttpSession session = req.getSession(false);
        if (session == null) {
            // 用户没有登陆, 重定向到 login.html
            resp.sendRedirect("login.html");
            return;
       }
        // 2. 如果已经登陆, 则从 Session 中取出访问次数数据
        String userName = (String)session.getAttribute("username");
        String countString = (String)session.getAttribute("loginCount");
        int loginCount = Integer.parseInt(countString);
        loginCount += 1;
        session.setAttribute("loginCount", loginCount + "");
        // 3. 展示到页面上.
        StringBuilder html = new StringBuilder();
        html.append(String.format("<div>用户名: %s</div>", userName));
        html.append(String.format("<div>loginCount: %d</div>", loginCount));
        resp.getWriter().write(html.toString());
   }
}
        在这个代码中是看不到 " 哈希表 ", 也看不到 sessionId 这样的概念的 . getSession 操作内部提取到请求中的 Cookie 里的 sessionId, 然后查找哈希表 , 获取到对应的 HttpSession 对象 .
        其中getSession 参数为 false, 则获取不到 HttpSession 对象 , 不会创建新的 HttpSession, 而是返回 null. 此时说明用户没有登陆 .

2. 创建 login.html, 放到 webapp 目录中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录</title>
</head>
<body>
    <form action="login" method="post">
        <input type="text" name="username">
        <br>
        <input type="password" name="password">
        <br>
        <input type="submit" value="提交">
    </form>

</body>
</html>

3. 创建 LoginServlet

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");

        // 然后判断用户名密码是否正确,先假定一些固定的值(还没涉及数据库)
        if (!username.equals("zhangsan") && !username.equals("lisi")) {
            // 用户名错误
            resp.getWriter().write("用户名/密码错误!");
            resp.sendRedirect("login");
            return;
        }
        if (!password.equals("123456")) {
            resp.getWriter().write("用户名/密码错误!");
            resp.sendRedirect("login");
            // 密码错误
            return;
        }

        // 到这里说明用户名和密码都正确
        // 1.创建一个会话
        HttpSession session = req.getSession(true);
        // 2.把当前的用户名保存到会话中,此处的 HttpSession 又可以当作是一个 map
        session.setAttribute("username", username);
        // 重定向到主页
        resp.sendRedirect("index");

    }
}
        此处的 getSession 参数为 true, 表示查找不到 HttpSession 时会创建新的 HttpSession 对象 , 并 生成一个 sessionId, 插入到 哈希表 中 , 并且把 sessionId 通过 Set-Cookie 返回给浏览器 .
  • 29
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值