目录
前言
本篇博客主要回顾一下Cookie的功能和作用还有Session的概念及Servlet中Session的使用,以及二者之间的区别,最后会使用Session的机制来模拟实现一个简单的登录功能。还会通过Fiddler抓包来观察结果。
一、回顾Cookie
在前面的HTTP协议中,我们就见过Cookie了,Cookie是浏览器在本地存储数据的一种方式,理论上是可以存储任何数据的,前提是数据的类型必须是String类型。
关于Cookie的三个问题:Cookie要从哪来?Cookie要到哪去?Cookie有啥用?
首先就是Cookie从哪来:Cookie是从浏览器来的,服务器在响应时就会通过Set-Cookie字段将Cookie返回给浏览器,浏览器在下次访问服务器时,就会带上Cookie;
Cookie到哪去:Cookie还是到服务器去,在下一次的浏览器访问服务器的时候,浏览器会带上服务器返回的Cookie去访问服务器;
Cookie有啥用:Cookie是可以携带任何数据的,一般情况下我们是令Cookie来保存用户的登录信息,用来识别用户的身份,这样用户就不需要一直执行登录操作了。
二、Session
这里主要来介绍Session的作用和Servlet中的Session api、使用Session来模拟一下登录的场景。
2.1、引出Session及Session概念
上面的Cookie中,我们说到Cookie从服务器来,到服务器去,可以保存任何数据,一般是用来保存用户的登录信息。但是,服务器这边到底具体如何来区分用户的信息的呢?这里就需要用到Session(也称为会话)了,Session就是服务器这边用来实现用户身份区分的一种机制,一般和Cookie配合使用(也就是可以不和Cookie配合使用)。
Session的概念:
Session(会话)是指在客户端与服务器之间建立的一种交互状态。它代表了一段时间内的通信会话,通常用于跟踪用户在网站或应用程序中的活动。
当用户访问一个网站或应用程序时,服务器会为每个用户创建一个独立的会话。在会话期间,服务器会保存一些关于用户的信息,比如登录状态、购物车内容、用户偏好设置等。这些信息可以在用户与网站进行交互时进行共享和使用。
会话通常使用一个唯一的会话标识符来区分不同的用户会话。这个会话标识符可以通过Cookie、URL参数或者隐藏表单字段等方式传递到客户端,客户端在后续的请求中通过这个标识符来告诉服务器自己是哪个会话。
Session在Web开发中非常常见,它可以用于实现用户登录认证、购物车功能、用户跟踪等。一般来说,Session的数据是存储在服务器端的,这样可以保护用户数据的安全性。在某些情况下,也可以将Session数据存储在客户端(如通过Cookie实现),但这样会增加一定的安全风险。
需要注意的是,会话有一个有限的生命周期,可以通过设置超时时间或用户退出来终止会话。
2.2、理解Session
Session的工作流程:Session对象就是在我们第一次在浏览器中执行某个网站的登录操作时,服务器就会创建一个Session对象存储在一个全局的哈希表中,服务器会通过HTTP响应中的Set-Cookie字段将Session对象中的键SessionId返回,后续浏览器每次访问就都会带上这个SessionId,这样子就可以更加方便的让服务器为不同的人提供不同的页面了。
关于存储Session对象的哈希表:这个哈希表是一个键值对的形式,其中,以服务器分配的SessionId为键,以我们存进去的对象为值,这个对象可以包含任意数据。其中键的类型是一个String类型,值可以是Object对象,一般来说,Session对象中的值会存储在服务器端更加安全,浏览器一般只会有Session的键,也就是SessionId。
2.3、Cookie和Session的区别
1.Cookie是客户端的机制,Session是服务器端的机制
2.Cookie和Session一般都是配合使用的,但并不是必须配合使用的,表现在以下方面:
完全可以用 Cookie 来保存一些数据在客户端. 这些数据不一定是用户身份信息, 也不一定是 token / sessionId
Session 中的 token / sessionId 也不需要非得通过 Cookie / Set-Cookie 传
2.3、Servlet中Session的api
HttpServletRequest 类中的相关方法:
方法 | 描述 |
HttpSession getSession() | 在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果 为 false, 则当不存在会话时返回 null |
Cookie[] getCookies() | 返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把 Cookie 中的格式解析成键值对 |
关于HttpSession getSession(boolean)参数的解释及不同参数下getSession()方法的执行流程:
执行流程:
当参数为true时:
1.getSession()方法先读取请求中的SessionId;
2.根据SessionId去全局的哈希表中查找Session对象;
3.如果找到了对应的HttpSession对象,直接返回,没找到的话,就会创建HttpSession对象,并分配一个SessionId。
补充:后续我们可以使用setAttribute方法,将我们想要存储到Session的对象存进去。
当参数为false时:
1.getSession()方法先读取请求中的SessionId;
2.根据SessionId去全局的哈希表中查找Session对象;
3.如果找到了对应的HttpSession对象,直接返回,没找到的话,直接返回null。
补充:这里我们如果得到了Session对象,就可以通过getAttribute方法来拿到我们之前存进去的对象。
当参数不同时,区别到底在哪,以及什么时候该设置什么参数:
区别:当参数为true时,代表的是,允许创建Session对象,为false表示不允许创建Session对象;
我们该如何设置参数:一般我们在登录的时候,为了可以后续方便访问其他页面不用再重复登录,这时候就可以设置参数为true,表示允许创建对象,否则在其他情况下,参数就应该是false,就是不允许创建对象。
HttpResponse类中的相关方法:
方法 | 描述 |
void addCookie(Cookie cookie) | 把指定的 cookie 添加到响应中. |
HttpSession 类中的相关方法 :
一个 HttpSession对象里面包含多个键值对. 我们可以往 HttpSession 中存任何我们需要的信息.
方法 | 描述 |
Object getAttribute(String name) | 该方法返回在该 session 会话中具有指定名称的对象,如果没有 指定名称的对象,则返回 null. |
void setAttribute(String name, Object value) | 该方法使用指定的名称绑定一个对象到该 session 会话 |
boolean isNew() | 判定当前是否是新创建出的会话 |
Cookie 类中的相关方法:
每个Cookie 对象就是一个键值对
方法 | 描述 |
String getName() | 该方法返回 cookie 的名称。名称在创建后不能改变。(这个值是 Set-Cooke 字段设置给浏览器的) |
String getValue() | 该方法获取与 cookie 关联的值 |
void setValue(String newValue) | 该方法设置与 cookie 关联的值。 |
HTTP 的 Cookie 字段中存储的实际上是多组键值对. 每个键值对在 Servlet 中都对应了一个 Cookie 对象.
通过 HttpServletRequest.getCookies() 获取到请求中的一系列 Cookie 键值对.
通过 HttpServletResponse.addCookie() 可以向响应中添加新的 Cookie 键值对.
三、代码案例:模拟登录逻辑
这里的模拟登录场景实现的效果主要是:实现一个输入账号密码的框,使用form表单的格式来向后端提交数据,后端收到数据后对账号密码进行判断,如果正确的话,就显示欢迎+用户名,错误的话就直接显示用户名或密码错误即可。
这个过程中主要涉及到两个页面,一个前端登录的页面和两个后端的Servlet类来处理请求即可。
主要代码如下:
前端登录页的代码:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>登录页面</title>
</head>
<body><!--以下的路径使用的相对路径,相对路径就是前面的路径不变,将最近这一级路径改为login-->
<form action="login" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="登录">
</form>
</body>
</html>
处理登录逻辑的Servlet类代码:
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 SessionServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
resp.setContentType("text/html;charset=utf8");
//读取前端传来的密码和用户名
String username = req.getParameter("username");
String password = req.getParameter("password");
//验证用户名和密码的准确性
if(username == null || password == null || username.equals("") || password.equals("")){
resp.getWriter().write("用户名或密码不能为空");
}
if(!username.equals("zhangsan") && !username.equals("lisi")){
resp.getWriter().write("用户名或者密码错误");
return;
}
if(!(password.equals("123"))){
resp.getWriter().write("用户名或者密码错误");
return;
}
//到这一步说明用户名和密码都没错,接下来就可以先获取session对象再记录下sessionId了
HttpSession session = req.getSession(true);//这里设置为true说明允许创建session对象
session.setAttribute("username",username);//将用户名保存起来,后续通过get方法就可以直接获取了
//最后跳转到index页面即可
resp.sendRedirect("index");//使用相对路径
// if(username.equals("zhangsan") || username.equals("lisi")){
// if(password.equals("123")){
// //到这一步说明用户名和密码都没错,接下来就可以先获取session对象再记录下sessionId了
// HttpSession session = req.getSession(true);//这里设置为true说明允许创建session对象
// session.setAttribute("username",username);//将用户名保存起来,后续通过get方法就可以直接获取了
// //最后跳转到index页面即可
// resp.sendRedirect("index");//使用相对路径
// }else {
// resp.getWriter().write("用户名或密码错误");
// }
// }else {
// resp.getWriter().write("用户名或密码错误");
// }
}
}
处理跳转后的Servlet类代码:
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 {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf8");
HttpSession session = req.getSession(false);//获取session对象,不允许创建对象,因为没找到的话就是没登录
if(session == null){//如果没找到,说明用户还没登录,可以跳转到登录页面
resp.getWriter().write("用户未登录");
resp.sendRedirect("login.html");
return;
}
String username = (String) session.getAttribute("username");
resp.getWriter().write("欢迎"+username);
}
}
上述代码运行的效果图:
我们点击之后触发的post请求:
这里原本是不应该有Cookie的值的,但是由于我这里配置了smart Tomcat,这个Tomcat会在服务器关闭时,自动保存会话,在下次启动时,自动将会话加载到内存中,所以我这里就会有Cookie的值。
post请求对应的响应:
原本上述响应中应该是要带有Set-Cookie字段的,但是由于已经不是第一次访问了,请求中已经有Cookie字段了,所以就不会有Set-Cookie字段了。
跳转到index页面的请求:通过以下报文我们也可以看到Cookie字段中也包含有SessionID字段的
index页面对应的响应: