今天我们来认识一下Session
🧊1.回忆Cookie
🧊2.认识Session(会话)
🧊1.利用Session模拟实现登录功能
🧊2.登录逻辑的固定套路
🧊3.Cookie和Session区别
1.回忆Cookie
我们之前已经学习过了cookie机制
Cookie是浏览器在本地存储数据的一种机制
1.cookie从哪里来
cookie是从服务器来的
服务器在响应中会带有Set-Cookie字段,通过这个字段就可以把要把保存在浏览器本地的数据给返回回去
2.cookie到哪里去
后续浏览器访问服务器的时候,会把当前本地所有的cookie都通过http请求给带过去
3.cookie有啥用
最典型的应用就是用Cookie保存当前用户的登录状态
2.认识Session(会话)
而在Cookie保存用户身份标识这类的应用场景,身份标识如何分配,以及身份信息具体如何存储,都是需要服务器的支持的
那么这个时候用啥来实现用户区分呢?
用Session(会话)
Session既是服务器用来实现用户身份区分的一种机制,通常和Cookie配合使用
Session是给当前的用户分配一个sessionId,同时记录当前用户的身份信息
sessionId就会被返回浏览器的cookie中,后续浏览器访问服务器就会带着这个sessionId,从而让服务器识别当前用户的身份
会话的本质就是一个 “哈希表”, 存储了一些键值对结构. key 就是令牌ID(token/sessionId), value 就是用户信息(用户信息可以根据需求灵活设计).
Servlet 的 Session 默认是保存在内存中的. 如果重启服务器则 Session 数据就会丢失.
我们来画图理解一下吧
1.当用户登陆的时候, 服务器在 Session 中新增一个新记录, 并把 sessionId 返回给客户端. (例如通过 HTTP 响应中的 Set-Cookie 字段返回).
2.客户端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId (例如通过 HTTP 请求中的 Cookie 字段带上).
服务器收到请求之后, 根据请求中的 sessionId 在 Session 信息中获取到对应的用户信息,
再进行后续操作.
1.下面来认识一下Session的方法
方法 | 描述 |
HttpSessiongetSession() | 在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null |
Cookie[]getCookies() | 返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把Cookie 中的格式解析成键值对. |
getSession有一个参数,boolean类型的,
如果参数为false,getSseeion的行为是
1.读取请求中Cookie的sessionId
2.在服务器这边根据sessionId查询对应的Session对象
3.如果查到了,就会直接返回这个session对象,如果没查到,返回null
如果参数为true,getSession的行为是:
1.读取请求中Cookie的sessionId
2.在服务器这边根据sessionId查询对应的Session对象
3.如果查到了,就会直接返回这个session对象
4.如果没查到,会创建一个Session对象,同时生成一个sessionId,以sessionIdkey,Session对象为value,把这个键值对存储到服务器里的一个hash表中,同时把sessionId以Set-Cookie的方式返回给浏览器
2.下面我们来模拟实现登录功能
需要两个页面
1.登录页(包含两个输入框,用户名和密码,还要有一个登录按钮)
点击登录,发起http请求
服务器处理这个请求的时候验证用户名密码
如果都是对的,就跳转到主页
2.主页
主页单纯的显示当前的用户名,是一个动态页面(欢迎xxx)
登录页就是一个HTML
还需要一个Servlet,实现登录时候的用户名密码校验
还需要一个Servlet来生成主页(主页的内容是动态的,)
开写
1.先写登录页的前端页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta namen="viewport" content="width=device-width, initial-scale=1.0"> <title>登录页面</title> </head> <body> <form action="login" method="post"> <input type="text" name="username"> <input type="password" name="password"> <input type="submit" value="登录"> </form> </body> </html>
form表单会组织这里的数据以键值对的形式提交给服务器
key就是input的name属性,
value就是input用户输入的内容
最终构造成post请求以键值对的形式进行组织
服务器通过getParameter来获取指定key的value
2.编写login.HTML,处理登录请求
规定登录请求格式
POST/login
Content-Type:application/x-www-form-urlencoded
user=zhangsan&password=123
登录请求一般都用post
2.编写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; /** * Created with IntelliJ IDEA. * Description: * User: WHY * Date: 2023-06-10 * Time: 14:53 */ //这个Servlet用来实现登录时的校验 @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.从请求拿到用户名密码 //为了保证读出来的参数也支持中文,设置请求编码方式为utf8 req.setCharacterEncoding("utf8"); String username=req.getParameter("username"); String password=req.getParameter("password"); //2.验证用户名密码是否正确 if(username==null||password==null||username.equals("")||password.equals("")){ resp.setContentType("text/html; charset=utf8"); resp.getWriter().write("当前用户名或密码不能为空"); return; } //此处假设用户名只能是zhangsan 或者wangyibo,密码都是123 //正常的登录逻辑,验证用户名密码都是从数据库读取的,此处为了方便,就不用了 if(!username.equals("zhangsan")&&!username.equals("wangyibo")){ //用户名有问题 resp.setContentType("text/html; charset=utf8"); resp.getWriter().write("当前用户名或密码有误"); return; } if (!password.equals("123")){ resp.setContentType("text/html; charset=utf8"); resp.getWriter().write("当前用户名或密码有误"); return; } //3.用户名和密码验证成功,创建会话 //当前用户处于未登录状态,此时请求的cookie没有sessionId //此时的getSession无法从服务器的哈希表找到该session对象 //由于此处把参数设置为true,所以允许getSession创建Session对象和sessionId //然后会自动把sessionId和session对象存储到哈希表中, //同时返回这个session对象,并且在接下来的响应中把这个sessionId返回给客户端浏览器 HttpSession session=req.getSession(true); //让session存储自定义数据,存储用户信息 session.setAttribute("username",username); //4.登陆成功之后,自动跳转到主页 resp.sendRedirect("index"); } }
3.编写主页的代码
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; /** * Created with IntelliJ IDEA. * Description: * User: WHY * Date: 2023-06-10 * Time: 14:55 */ //这个类用来实现动态生成主页面 @WebServlet("/index") public class IndexServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //此处禁止创建会话,如果没找到,认为用户是未登录的状态 //如果找到了,才认为是登录状态 HttpSession session=req.getSession(false); if(session==null){ //当前未登录 resp.setContentType("text/html; charset=utf8"); resp.getWriter().write("当前用户未登录"); return; } String username=(String)session.getAttribute("username"); //虽然有会话对象,但是没有属性,也认为是登录状态异常 if(username==null){ resp.setContentType("text/html; charset=utf8"); resp.getWriter().write("当前用户未登录"); return; } //如果都没问题,生成动态页面 resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("欢迎你!"+username); } }
我们的代码就写完了,来看一下结果
抓包来看看代码执行流程
这里是没有cookie的,点击登录,触发一个登录请求
第一个请求的响应有set-cookie
这里的
这个sessonid来自创建的sesssion会话
第二个请求
这里就有cookie了
最后得到结果
总结:实现登录的逻辑
1.读取用户名和密码
2.验证用户名密码
3.创建会话,保存必要信息
4.重定向到主页
上述的sessionId不会一直存在下去,当服务器重新启动的时候.原来hash表中的内容就没了
这时再次访问可能sessionId无法查询到,那么就是未登录状态
因为session默认保存在内存中
不过如果是Smart Tomcat,为了方便我们调试,可能会把会话持久化,这个要看smart Tomcat的版本
如果是手动部署到Tomcat,那会话还是默认保存在内存,重启会丢失
这里说一个小问题,为什么smart Tomcat不用打包就可以执行
解答:
smart tomcat会自动配置。通过target目录 和webapp目录访问
3.cookie和session的区别
1.Cookie 是客户端的机制. Session 是服务器端的机制.
2.Cookie 和 Session 经常会在一起配合使用. 但是不是必须配合.
3.完全可以用 Cookie 来保存一些数据在客户端. 这些数据不一定是用户身份信息, 也不一定是token / sessionId
4.Session 中的 token / sessionId 也不需要非得通过 Cookie / Set-Cookie 传递
5.session主要是来存用户的相关数据,cookie存什么都可以
这就是今天的全部内容了,我们下期再见了~~~