• Cookie和Session的工作流程是什么样的?5分钟学懂:简易用户登录(前端+后端+数据库)


    目录

    前言

    一、Cookie

    1.1、Cookie从哪里来?

    1.2、Cookie到哪里去?

    二、Session

    2.1、什么是sessionId?

    三、Cookie和Session的区别

    1.作用的对象不同

    2.安全性不同

    3.存储的位置不同

    4.存储容量不同

    5.存储时间不同

    6.存储的数据类型不同

    7.使用习惯上

    8.存储习惯上

    四、Cookie和Session的具体工作流程

    五、代码实现用户登录

    5.1、核心方法

    5.2、代码


    前言

            想要了解Cookie和Session的工作流程,首先需要来了解一下什么是Cookie,什么是Session?之后我将会用一个用户登录(附加:前端+后端 代码)的栗子,让你通透整个工作流程;


    一、Cookie

            有时候需要让网页存储一些简单数据,但由于网页禁止js访问电脑硬盘(原因:安全性),所以就提供了特殊的api给网页用,Cookie就是最经典的一个方案,是浏览器在本地存储数据(存储到硬盘上)的一种机制;

     Cookie如何组织信息的?键值对的形式!(如下图)

    Cookie是用来存放什么数据的?

    • 上次访问网页的时间;
    • 当前网页的访问次数
    • 当前访问网页网页的身份标识(最典型的应用场景);

    特点:

    •         Cookie是按照域名维度来组织的,不同域名下有不同的Cookie;
    •         和query string一样,是程序员自定义的;
    •         每一个Cookie都是一个键值对;
    •         Cookie有个过期时间,到时自动清除;

            注意:Cookie不是缓存,是持久化数据的手段(保存在硬盘上),缓存的数据是用来提高访问速度的;

    1.1、Cookie从哪里来?

            Cookie存在于浏览器,来源于服务器;

            解释:在网页中我们所观察的Cookie都是浏览器访问了某一个服务器后,服务器返回一个响应报文,在响应header中包含 一个/多个 Set-Cookie这样的资源(程序员自己在服务器代码中写的),浏览器接收响应后,就见Set-Cookie这样的数据保存到浏览器本地;

    如下图Fiddler所捕捉到的Set-Cookie:

    1.2、Cookie到哪里去?

            来自服务器,存储到浏览器,最后返回到服务器;

            解释:当浏览器保存了cookie后,下次访问同一网站,就会把之前存在本地的Cookie作为身份标识在http请求的header给返回到服务器,服务器就知道,喔,又是你来了~就把上次加载好的数据作为响应返回给浏览器;


    二、Session

            服务器每一时刻接收到的请求是很多的;服务器为了区分这些请求分别是哪一个用户的,就需要记录用户和该用户信息之间的对应关系,这时Session会话机制就起到关键的作用!

            Session会话的本质就是一个哈希表,用来存放一些键值对;例如用户登录一个网站,Session的key就是用户名,value就是该用户的信息;

    注意:Servlet 的 Session 默认是保存在内存中的. 如果重启服务器则 Session 数据就会丢失.

    2.1、什么是sessionId?

            sessionId是由服务器生成的一个“唯一性字符串”,也可以理解为一个身份表示,通过这个,服务器就可以识别对应的用户;从session机制的角度来看,这个唯一性字符串称为 “sessionId”,但在整个登录流程来看,也可以把这个唯一字符称为 “token” ; 

    如下图:


    三、Cookie和Session的区别

    1.作用的对象不同

    Cookie是客户端机制,Session是服务器机制。

    2.安全性不同

    cookie存储在客户端,所以可以分析存放在本地的cookie并进行cookie欺骗,安全性较低。
    session存储在服务器上,不存在敏感信息泄漏的风险,安全性较高。

    3.存储的位置不同

    cookie的数据信息存放在本地硬盘。
    session的数据信息存放在服务器的内存中(一旦重启,数据就会丢失)。

    4.存储容量不同

    cookie存储的容量较小,一般<=4KB。
    session存储容量大小没有限制(但是为了服务器性能考虑,一般不能存放太多数据)。

    5.存储时间不同

    cookie可以长期存储,只要不超过设置的过期时间,可以一直存储。

    session在超过一定的时间(通常为30分钟)不使用会失效。

    6.存储的数据类型不同

    Cookie 只能保管ASCII字符串。

    session中能够存储任何类型的数据,包括且不限于string,integer,list,map等

    7.使用习惯上

    Cookie和Session经常一起配合使用,但不是必须配合使用。

    8.存储习惯上

    Cookie 主要用来存储上一次访问浏览器时间、访问次数、用户身份标识。

    Session 主要用来存储用户身份标识和对应的用户详细信息。


    四、Cookie和Session的具体工作流程

    如下图:


    五、代码实现用户登录

    5.1、核心方法

    把握一点:HttpSession就类似于HashMap~

    方法描述
    HttpSession getSession()

    当服务器获取会话:

    1.若该方法的参数为true,则判断当前会话是否存在,若不存在就创建一个新的键值对保存到哈希表中,并生成sessionId返回到浏览器,若存在则返回对应的HttpSession;

    2.若该方法的参数为false,则判断当前会话是否存在,若不存在就返回null,若存在就返回HttpSession;

    Object getAttribute(String name)

    类似于HashMap的get()方法:

    返回session会话中 name(key) 所对应的value;

    若没有指定名称的对象,就返回null;

    void setAttribute(String name, Object value)

    类似于HashMap的put方法:

    该方法使用指定的名称绑定一个对象到该 session 会话(绑定一对key,value);

    5.2、代码

    1. loginServlet实现登录界面的服务器,登录次数通过数据库持久化保存;(如下代码)

    1. import com.mysql.jdbc.Connection;
    2. import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
    3. import javax.servlet.ServletException;
    4. import javax.servlet.annotation.WebServlet;
    5. import javax.servlet.http.HttpServlet;
    6. import javax.servlet.http.HttpServletRequest;
    7. import javax.servlet.http.HttpServletResponse;
    8. import javax.servlet.http.HttpSession;
    9. import javax.sql.DataSource;
    10. import java.io.IOException;
    11. import java.sql.PreparedStatement;
    12. import java.sql.ResultSet;
    13. import java.sql.SQLException;
    14. @WebServlet("/login")
    15. public class LoginServlet extends HttpServlet {
    16. @Override
    17. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    18. resp.setContentType("text/html; charset=utf8");
    19. //从客户端请求中获取用户名和密码
    20. String username = req.getParameter("username");
    21. String password = req.getParameter("password");
    22. //判定账户密码是否正确
    23. //这里假设已经注册的账户为:
    24. //username:zhangsan password:123
    25. if (!"zhangsan".equals(username) || !"123".equals(password)) {
    26. resp.getWriter().write("很抱歉,登录失败,您的用户名或密码输入错误!");
    27. return;
    28. }
    29. //登录成功
    30. System.out.println("" + username + "成功登录");
    31. //从数据库中获取该用户的登录次数
    32. int loginCount = 0;
    33. try {
    34. loginCount = load(username);
    35. } catch (SQLException e) {
    36. e.printStackTrace();
    37. }
    38. //由于登录成功,所以登录次数加一,保存到数据库中
    39. try {
    40. save(username, String.valueOf(++loginCount));
    41. } catch (SQLException e) {
    42. e.printStackTrace();
    43. }
    44. //设置Session
    45. //getSession的参数true表示若查找不到HttpSession,就会新建立一个,并生成
    46. //一个sessionId插入哈希表,通过Set-Cookie返回给浏览器
    47. HttpSession session = req.getSession(true);
    48. //HttpSession对象自身相当于一个哈希表,可以根据需求设置里面的参数
    49. session.setAttribute("username", "zhangsan");
    50. session.setAttribute("loginCount", loginCount);
    51. //设置重定向到主界面index
    52. resp.sendRedirect("index");
    53. }
    54. //修改数据库中的数据(登录次数)
    55. private void save(String username, String loginCount) throws SQLException {
    56. //创建数据源
    57. DataSource dataSource = SingletonLazy.getDataSource();
    58. //建立连接
    59. Connection connection = (Connection) dataSource.getConnection();
    60. //构造sql
    61. String sql = "update message set loginCount = ? where name = ?";
    62. PreparedStatement statement = connection.prepareStatement(sql);
    63. statement.setString(1, loginCount);
    64. statement.setString(2, username);
    65. //执行sql
    66. int ret = statement.executeUpdate();
    67. //打印日志
    68. System.out.println("ret = " + ret);
    69. //关闭数据库
    70. statement.close();
    71. connection.close();
    72. }
    73. //从数据库中获取登录次数
    74. private int load(String username) throws SQLException {
    75. //创建数据源
    76. DataSource dataSource = SingletonLazy.getDataSource();
    77. //建立连接
    78. Connection connection = (Connection) dataSource.getConnection();
    79. //构造sql
    80. String sql = "select loginCount from message where name = ?";
    81. PreparedStatement statement = connection.prepareStatement(sql);
    82. statement.setString(1, username);
    83. //执行sql
    84. ResultSet resultSet = statement.executeQuery();
    85. int loginCount = 0;
    86. while(resultSet.next()) {//这里只对应一个结果
    87. loginCount = resultSet.getInt("loginCount");
    88. }
    89. //关闭数据库
    90. statement.close();
    91. connection.close();
    92. return loginCount;
    93. }
    94. }

    2.通过单例模式(懒汉)创建数据库的数据源(如下图)

    1. import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
    2. import javax.sql.DataSource;
    3. public class SingletonLazy {
    4. private volatile static DataSource dataSource = null;
    5. public static DataSource getDataSource() {
    6. if(dataSource == null) {
    7. synchronized(SingletonLazy.class) {
    8. if(dataSource == null) {
    9. dataSource = new MysqlDataSource();
    10. ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/login?characterEncoding=utf8&useSSL=false");
    11. ((MysqlDataSource)dataSource).setUser("root");
    12. ((MysqlDataSource)dataSource).setPassword("1111");
    13. }
    14. }
    15. }
    16. return dataSource;
    17. }
    18. }

    3. indexServlet实现登录后主页面的反馈;(如下图)

    1. import javax.servlet.ServletException;
    2. import javax.servlet.annotation.WebServlet;
    3. import javax.servlet.http.HttpServlet;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import javax.servlet.http.HttpSession;
    7. import java.io.IOException;
    8. @WebServlet("/index")
    9. public class indexServlet extends HttpServlet {
    10. @Override
    11. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    12. resp.setContentType("text/html; charset=utf8");
    13. //判断当前用户是否已经登录
    14. //getSession参数为false,表示若没有创建过HttpSession,则返回null;
    15. //若创建过,则返回该对象
    16. HttpSession session = req.getSession(false);
    17. if(session == null) {
    18. resp.getWriter().write("很抱歉,尚未登录");
    19. //重定向到index
    20. resp.sendRedirect("login");
    21. return;
    22. }
    23. //已登录过,就从Session中访问数据
    24. //这里由于getAttribute返回的是Object类型,所以这里需要强转成String
    25. String username = (String)session.getAttribute("username");
    26. int loginCount = (int)session.getAttribute("loginCount");
    27. resp.getWriter().write("欢迎回来:" + username + "~" + "
      "
    28. + "您今日已经登录了:" + loginCount + "次");
    29. }
    30. }

    4. login.html 一个简易登录界面,用来发送post请求登录

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>登录界面title>
    8. head>
    9. <body>
    10. <style>
    11. .container {
    12. width: 400px;
    13. margin: 0 auto;
    14. }
    15. h1 {
    16. padding: 10px;
    17. width: 200px;
    18. margin: 0 auto;
    19. text-align: center;
    20. }
    21. p {
    22. margin: 10px auto;
    23. color: gray;
    24. text-align: center;
    25. }
    26. .row {
    27. display: flex;
    28. justify-content: space-between;
    29. align-items: center;
    30. padding: 2px 0;
    31. width: 300px;
    32. }
    33. div input {
    34. height: 30px;
    35. width: 200px;
    36. }
    37. form {
    38. width: 300px;
    39. margin: 0 auto;
    40. }
    41. .button {
    42. margin: 0 auto;
    43. height: 40px;
    44. width: 300px;
    45. background-color: orange;
    46. border: none;
    47. color: white;
    48. }
    49. .button:active {
    50. background-color: rgb(251, 209, 130);
    51. }
    52. style>
    53. <div class="container">
    54. <h1>登录界面h1>
    55. <p>输入后点击登录,若信息正确自动跳转p>
    56. <form class action="login" method="post">
    57. <div class="row">
    58. <span>账户span>
    59. <input type="text" name="username">
    60. div>
    61. <div class="row">
    62. <span>密码span>
    63. <input type="password" name="password">
    64. div>
    65. <div class="row">
    66. <input class="button" type="submit" value="登录">
    67. div>
    68. form>
    69. div>
    70. body>
    71. html>

  • 相关阅读:
    js逆向算法
    php不重新编译,添加模块
    Mybatis:Mybatis的各种查询功能(5)
    MMKV(3)
    Java 线程池调度周期性任务“异常“探究
    C语言回顾(可变参数篇)
    【修复】centos定时任务python top不能输出
    算法练习——字符串
    喂饭级AI神器!免代码一键绘制图表,文本数据秒变惊艳视觉盛宴!
    【开发】安防监控/视频存储/视频汇聚平台EasyCVR优化播放体验的小tips
  • 原文地址:https://blog.csdn.net/CYK_byte/article/details/128004499