在之前的 HTTP 格式的介绍当中,有详细的介绍过 cookie 的相关用法。
简单回顾总结就是:
用户输入账号密码登录后,再次登录不需要再次输入账号密码就可以直接的登录成功就是依靠 cookie 来实现的
但是有关于用户的信息实际上是非常多的,比如访问网站的浏览记录,上次访问网站的时间等等,这么多的信息在服务器客户端之间传输来又去的非常浪费带宽。更何况 cookie 的存储容量又是有限的,一个站点最多保留20个 cookie,这么多的用户信息没有办法单纯的依靠 cookie 来保存在客户端,因此将数据保存到服务器端才是比较科学的做法。
因为要将信息存储在服务器端,就引入了会话机制 session
。
session是服务端存储的一个对象,主要用来存储所有访问过客户端网页的用户信息(也可以存储其他信息),从而实现保持用户会话状态。但是服务器重启时,内存会被销毁,存储的用户信息也就消失了。
面试题:详述 session 工作原理
- 当客户端登录完成后,就会在服务器端产生一个 session,,实际上是一个键值对,
key 是 sessionId
(随机唯一的字符串),value
保存的就是身份信息(HttpSession 对象
)。服务器端将这些键值对形式的会话通过hash 表
的形式管理起来- 此时服务器端就会将
sessionId
返回到客户端浏览器。- 客户端将 sessionId 存储在浏览器的
cookie
中- 当用户再次登录的时候,就会拥有对应的 sessionId ,就将该 sessionId 发送到服务器端请求登录
- 服务器端在内存中找到对应的 sessionId 就完成登录,如果找不到,说明还没有登录(每个用户登录都会生成一个会话),就返回登录页面让用户进行登录
session 工作原理和校园卡差不多,校园卡中实际上并没有保存太多的内容,但是有着它在学校中拥有的唯一的信息——学号。因此你(客户端)拎着这张卡,通过学号(相当于sessionId)就可以在系统中(服务器)搜索到有关于你的相关具体学生信息,就可以凭借卡进出宿舍大门,在图书馆借书,在食堂吃饭等等。
这是 HttpServletRequest 类中的方法,作用是在服务器中获取会话。
HttpSession session = req.getSession(true);
HttpSession session = req.getSession(false);
参数为true
- 查看请求中是否有 sessionId ,并判断其合法性
- 如果有 sessionId 且合法,就根据该 sessionId 找到对应的 HttpSession对象
- 如果没有 sessionId,就
生成一个唯一的 sessionId
,创建一个HttpSession 对象,把这一组键值对插入到服务器管理 session 的 hash 表中,把 sessionId 通过 Set-Cookie 在响应中返回给客户端
参数为false
- 查看请求中是否有 sessionId ,并判断其合法性
- 如果有 sessionId 且合法,就根据该 sessionId 找到对应的 HttpSession对象
- 如果没有 sessionId,就直接
返回 null
通常用法就是前者用于登录,后者用于查看登录的情况。
HttpSession 对象中就保存着我们需要保存的身份信息,这些身份信息的存储方式也是键值对的形式
**1. Object getAttribute(String name) **
该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返返回 null
int num = (Integer)session.getAttribute("number");
//通过number这个key来找对应的value,找的对象是int类型,需要进行强制类型转换
2. void setAttribute(String name, Object value)
该方法使用指定的名称绑定一个对象到该 session 会话
session.setAttribute("users",user);//将user是value; users是key
简单来说,就是 HttpSession 对象中的键值对,通过 key 来获取 value 的值的方法就是getAttribute,根据 key 来设置 value 的值的方法就是 setAttribute
可以看见Cookie,session,HttpSession 对象中的值都是键值对的形式,其中的关系需要理清。
<body>
<form action="login" method="post">
<input type="text" name="userName"><br>
<input type="text" name="passWord"><br>
<input type="submit" value="提交">
form>
body>
通过的 form 表单简单的构造一个登录页面
action=“login” 中,login是处理 post 请求的 Servlet 程序,用于判断用户登录是否成功
method="post"指的是请求的方法时POST
form 表单默认的body格式是
x-www-form-urlencoded
,因此构造的请求 body为userName=xxx&passWord=xxx
class User {
public String userName;
public String passWord;
}
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf8");//设置字符集
//1.获取到body中的数据
String userName = req.getParameter("userName");
String passWord = req.getParameter("passWord");
//2.验证登录数据的合法性,为null或者为空字符串都为不合法
if(userName == null||userName.equals("")||passWord == null||passWord.equals("")) {
resp.sendRedirect("login.html");
//不合法就重定向到login.html继续用户名及密码的输入
return;
}
//3.核对信息的正确性(在这里假定用户名字为Peter,用户的密码为123)
if(!userName.equals("Peter")||!passWord.equals("123")) {
resp.getWriter().write("用户名或者密码错误,登陆失败");
return;
}
//到这儿登录成功啦,就可以创建会话了
User user = new User();
user.userName = userName;
user.passWord = passWord;//构造一个 User 对象
HttpSession session = req.getSession(true);
//如果会话中不存在该用户就创建一个新的sessionId
session.setAttribute("user",user);//设置属性
session.setAttribute("index",0);//代表访问次数,最初为0
resp.sendRedirect("index");
//重定向到index路径,实现访问次数的计数
}
}
流程总结:
- 读取到用户提交的用户名和密码
- 对用户名及密码的合法性进行校验
- 判断是否登录成功
- 创建会话,在 HttpSession 对象中保存一些信息
- 重定向到指定页面
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset = utf8");
//1.首先需要通过session判断一下,是否已经登录成功了
HttpSession session = req.getSession(false);
//此时去找记录的时候,如果没有存档就返回null
if(session == null) {
//说明没有登录过,重定向到 login.html 页面进行登录
resp.sendRedirect("login.html");
return;
}
//到这里说明已经登录过了
User user = (User)session.getAttribute("user");
int visitCount = (Integer)session.getAttribute("index");
//获取用户 user 和 设置的访问次数 index,首次访问时为0
visitCount ++;//访问次数增加
session.setAttribute("index",visitCount); //将值设置回去
StringBuilder str = new StringBuilder();
str.append(user.userName);
str.append("访问次数: ");
str.append(visitCount);
resp.getWriter().write(str.toString());
}
}
首次登录请求:
请求:
可以查看到form表单中接收请求的对象以及 body 部分
响应:
状态码 302
,Location:index
,说明重定向到 index 路径进行访问次数的显示了
由于登录成功,服务器通过 Set-Cookie 的Header向客户端传送 sessionId,该sessionId 的 key 为JSESSIONID
,后面跟着的一长串字符串就是 sessionId 的值(唯一的)
此时服务器端就保存了 sessionId
第二次请求:
这次请求服务器就会带着 sessionId ,服务器根据这来找到 HttpSession 对象,取出里面 index 对应的值,自增,在写回去,同时页面显示访问次数结果
浏览器本地查看的 Cookie 内容:
第二次请求的请求:
第二次请求的响应:
页面结果:
之后的继续访问,原理同上
总体效果展示:
当服务器进行重启,内存中的 session 数据就会没有,此时客户端拿着之前的 sessionId 是没有办法在服务器端找到 HttpSession 对象的,就会返回 null ,进行重新登录。
完!