目录
Cookie是HTTP协议中的一个字段,同时也是浏览器在客户端这边保存数据的一种方式。
登录一个网页,用抓包工具抓一下登录请求就可以看到:

令牌就是Cookie中的字段
Session叫做会话,他主要是用来检查请求数据中的Cookie字段是否在服务器存在和是否合法
因为Cookie是由服务器产生的,所以服务器中会存储Cookie中的一些信息,当浏览器再次请求数据时,服务器就会将Cookie中的一些字段和服务器存储的信息比对,如果比对成功就让这次请求通过,如果比对失败则不会通过这次请求。而Session所做的就是这样的工作,它就像检票员,如果发现你不是这趟车的就不会让你上车。
Session是在服务器这边存储的,可以简单的把Session想象成一个hash表,其中的key就是令牌的ID(token/sessionId),value就是程序员自己定义的数据(这里可以存储用户的身份信息等)。
sessionId是服务器生成的一个“唯一字符串”,每一个登录的用户都有自己唯一不同的ID,这就好比身份证一样,每个人都不相同。
总结上面的工作就是:
- HttpSession getSession()
- 功能有两个:
- 尝试根据当前请求中的sessionId来获取当前的session
- 1.如果session不存在,就创建(创建一个HttpSession对象,作为value。创建一个sessionId作为Key。把key和value插入到hash表中,同时把这个sessionId通过Set-Cookie字段返回给浏览器)
- 2.如果存在就返回
- void addCookie(Cookie cookie) 功能:把指定的cookie添加到响应中
- Object getAttribute(String name) 功能:返回session会话中具有指定名称的对象,如果没有指定名称的对象,则返回null
通过一个登录案例来探究这些方法和会话机制:
首先写一个html,里面包含用户名密码的输入框,以及登录按钮。然后要有一个LogServlet来处理登录请求。在搞一个IndexServlet,模拟登录完成以后,跳转到的主页面,在这个页面就可以获取到用户的身份信息
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <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>
- @WebServlet("/login")
- public class LoginSelvlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- resp.setContentType("text/html;charset=utf-8");
- //1.先从请求body中读取用户名和密码
- String username = req.getParameter("username");
- String password = req.getParameter("password");
- //2.判定一下用户名和密码是否正确(读固定的密码)
- if(!"zhangsan".equals(username)||!"123".equals(password)){
- //登录失败
- resp.getWriter().write("登陆失败");
- return;
- }
- //3.登陆成功,则创建出一个会话来,会话不存在就创建
- //会话是根据请求中的sessionId来查的,sessionId是在Cookie中的
- //但是此处是首次登录,此时请求中是没有Cookie(Cookie是在服务器返回)
- //此时就会触发"找不到就创建"这样的流程,同时这里进行的操作:先创建出一个HttpSession对象(作为value),再成一个随机的字符串,作为sessionId(作为key)
- //把这个key和value插入到hash表中,同时把这个生成的sessionId通过Set-Cookie字段返回给浏览器
- HttpSession httpSession = req.getSession(true);
- //还可以存入程序猿自定义的数据,可以存入身份信息(用户名和登录次数)
- httpSession.setAttribute("username","zhangsan");
- httpSession.setAttribute("loginCount",0);
- //4.让页面跳转到主页,使用重定向的方式实现即可
- resp.sendRedirect("index");
- }
- }
- @WebServlet("/index")
- public class indexServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //根据当前用户请求中的sessionId,获取到用户信息,并且显示到页面上
- resp.setContentType("text/html;charset=utf-8");
- //1.判定当前用户是否已经登录了,(请求中有没有sessionId,以及sessionId是否合法)
- //如果会话不存在,就不能创建了,只是查询,不是登录
- HttpSession httpSession = req.getSession(false);
- if(httpSession==null){
- //当前没有找到合法会话,当前用户尚未登录
- //返回并让用户重新登录
- System.out.println("登录失败");
- return;
- }
- //2.用户登录成功就可以从HttpSession获取到用户里面的信息
- String username = (String)httpSession.getAttribute("username");
- Integer loginCount = (Integer)httpSession.getAttribute("loginCount");
- loginCount = loginCount+1;
- httpSession.setAttribute("loginCount",loginCount);
- //3.返回一个HTML页面
- StringBuilder html = new StringBuilder();
- html.append("用户: "+username+"");
- html.append("访问次数: "+loginCount+"");
- resp.getWriter().write(html.toString());
-
- }
- }
启动服务器,在浏览器中输入相关URL后:

再通过抓包工具可以看出:
第一次请求中的数据里面是没有Cookie的
但是响应中就返回了一个Cookie,这个Cookie就是服务器生成的
上面的响应中还有一个Location的字段,他表示要跳转到那个URL,根据URL就会跳转到index路劲下面,于是就触发第二次请求:
可以看到第二次请求中就带有Cookie,而这个Cookie就是第一次响应中的Cookie,而里面的JSESSIONID就是sessionId,它=号后面的一串字符串就是十六进制的随机字符串。在这个请求头中,还有一个Referer字段,它表示从哪个URL跳过来的。
再来看第二次请求中的响应内容:
第二次响应中的body就是要输出在浏览器界面上的内容。
后续的请求中都带有相同的Cookie:
总结上面的代码:
为了展示效果明显,这里就不跳转到index路径下面的Servlet了,而是让他跳转到一个指定的html页面:
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>待办事项title>
-
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- }
-
- .container {
- /* background-color: transparent;*/
- width: 800px;
- height: 800px;
- margin: 0 auto;
- display: flex;
- }
-
- .todo,
- .done {
- width: 50%;
- height: 100%;
- }
-
- .container h3 {
- height: 50px;
- text-align: center;
- line-height: 50px;
- background-color: #FB7299;
- color: #fff;
- }
-
- .nav {
- width: 800px;
- height: 100px;
- margin: 0 auto;
- display: flex;
- align-items: center;
- }
-
- .nav input {
- padding-left: 5px;
- width: 600px;
- height: 50px;
- }
-
- .nav button {
- width: 200px;
- height: 50px;
- }
-
- .nav button {
- width: 200px;
- height: 50px;
- /*去除按钮周围的边框,button会自带2个像素的实线边框,因此要去掉*/
- border: none;
- background-color: #6CD083;
- color: #ffffff;
- }
-
- .row {
- height: 50px;
- display: flex;
- align-items: center;
- }
-
- .row input {
- /*设置上下外边距为0,左右外边距为10*/
- margin: 0 10px;
- }
-
- .row span {
- width: 300px;
- }
-
- .row button {
- width: 50px;
- height: 40px;
- }
- style>
- head>
- <body>
-
- <div class="nav">
- <input type="text">
- <button>新建任务button>
- div>
- <div class="container">
- <div class="todo">
- <h3>未完成h3>
-
- div>
- <div class="done">
- <h3>已完成h3>
- div>
- div>
-
-
- <script>
- //实现新增任务
- var addTaskButton = document.querySelector('.nav button');
- addTaskButton.onclick = function () {
- //1.获取到输入框
- var input = document.querySelector('.nav input');
- //2.获取到输入框内容
- var taskContent = input.value;
- if(taskContent==''){
- console.log('当前任务为空不能新增任务');
- return;
- }
- //3.根据内容新建一个元素节点
- var row = document.createElement('div');
- row.className = 'row';
- var checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- var span = document.createElement('span');
- span.innerHTML = taskContent;
- var button = document.createElement('button');
- button.innerHTML = '删除';
-
- row.appendChild(checkbox);
- row.appendChild(span);
- row.appendChild(button);
-
- //4.把新节点插入到TODO中
- var todo = document.querySelector('.todo');
- todo.appendChild(row);
-
- //5.把输入框里面的内容删除
- input.value = '';
-
- //点击复选框后将元素放到"已完成"
- //6.给checkbox注册点击事件
- checkbox.onclick = function () {
- /*操作row这个对象,看是吧row这个对象放在TODO里面还是done里面
- 注意,是先触发checked为true,然后再调用onclick函数*/
- if (this.checked) {
- var target = document.querySelector('.done');
- } else {
- var target = document.querySelector('.todo');
- }
- target.appendChild(row);
- }
-
- //点击删除按钮删除该任务
- //7.给删除按钮注册点击事件
- button.onclick = function (){
- /*这里row就是那个要删除的元素(child=>row)
- row的parent可能是todo,也可能是done,这里可以直接通过parentNode属性来获取row的父元素*/
- var parent = row.parentNode;
- parent.removeChild(row);
- }
- }
- script>
- body>
- html>
- @WebServlet("/login")
- public class LoginSelvlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- resp.setContentType("text/html;charset=utf-8");
- //1.先从请求body中读取用户名和密码
- String username = req.getParameter("username");
- String password = req.getParameter("password");
- //2.判定一下用户名和密码是否正确(读固定的密码)
- if(!"zhangsan".equals(username)||!"123".equals(password)){
- //登录失败
- resp.getWriter().write("登陆失败");
- return;
- }
- //3.登陆成功,则创建出一个会话来,会话不存在就创建
- //会话是根据请求中的sessionId来查的,sessionId是在Cookie中的
- //但是此处是首次登录,此时请求中是没有Cookie(Cookie是在服务器返回)
- //此时就会触发"找不到就创建"这样的流程,同时这里进行的操作:先创建出一个HttpSession对象(作为value),再成一个随机的字符串,作为sessionId(作为key)
- //把这个key和value插入到hash表中,同时把这个生成的sessionId通过Set-Cookie字段返回给浏览器
- HttpSession httpSession = req.getSession(true);
- //还可以存入程序猿自定义的数据,可以存入身份信息(用户名和登录次数)
- httpSession.setAttribute("username","zhangsan");
- httpSession.setAttribute("loginCount",0);
- //4.让页面跳转到主页,使用重定向的方式实现即可
- resp.sendRedirect("http://127.0.0.1:8080/messageWall/work.html");
- }
- }
启动服务器,输入URL,然后登录: