在 Servlet 中,对于 Cookie 和 Session 有很好的支持!
所以,我们就可以基于 Servlet 里面提供的 Cookie 和 Session 的 API,来进行会话管理类似的操作。
Cookie 的几个问题
1.Cookie 是个啥?
浏览器提供的持久化存储数据的机制~~
2.Cookie 从哪里来?
Cookie 从服务器返回给浏览器的。服务器代码中由程序猿决定要把啥样的信息保存到客户端这边~~通过 HTTP 响应的 Set-Cookie 字段, 把键值对写回去即可~~
3.Cookie 到哪里去?
Cookie 会在后续浏览器访问服务器的时候带到请求的 header 中发给服务器~~
(为啥要这么折腾?? 服务器不是只给一个客户端提供服务,同一时刻要处理多个客户端的请求~~此时服务器就可以通过 cookie 中的值,来识别当前客户端是谁,当前客户端的服务提供到哪个环节了.(客户端借助 cookie 自报家门))
4.Cookie 存储在哪里?
存储在浏览器(客户端)所在主机的硬盘中
浏览器会根据 域名 来分别存储~~
回忆之前的例子:
但其实在实际情况中,使用的是一个 session 的方式来保存的。
毕竟一个人,他可能不止一张卡,而且卡可能会丢。
因此将身份信息全部存储到卡上并不安全,所以,卡上只需要存储一个会话窗口编号sessionId
通过这个会话窗口编号,就可以打开对应的窗口,调取对应的信息。
也就是说:
每一张卡 相当于 开启了 一个会话窗口,都可以通过这个会话窗口来获取自身的详细信息。
另外,这里我们补充一点:
Cookie 和 Session 的区别:
- Cookie 是客户端的存储机制. Session 是服务器端的存储机制.
- Cookie 里面可以存各种键值对(还可以存别的)Session 则专门用来保存用户的身份信息
- Cookie 完全可以单独使用,不搭配 session (实现非 登陆 场景下)Session 也可以不搭配 Cookie 使用.(手机 app 登陆服务器,服务器也需要 Session,此时就没有Cookie 的概念)
- Cookie 跟浏览器强相关的,Cookie 是属于 HTTP 协议中的一个部分。Session 则可以和 HTTP 无关(TCP, websocket ... 也可以用 session)
getSession 方法,既能用于获取到服务器上的会话,也能用于创建会话。
它的具体行为,取决于参数:
(拿着请求中 cookie 里的 sessionld 查一下哈希表)
这就好比:我们去医院看病。
但是这个医院,我们是第一次来。
肯定是要先挂个号,那么人家会问你有没有就诊卡?
如果你有,人家就不用给你办卡了。反之,人家就会根据你的身份信息,给你办一张就诊卡。
大家要明确:
在办卡之后,你拿到的不光是一张卡,还在该医院的服务器上存了一份档案。
这份档案就相当于是一个会话,卡上存储的就是 这个会话窗口的id号(sessionId)。
在实际情况中,调用 getSession 的时候具体做什么?
1、创建会话(买一张就诊卡)首先先获取到请求中 cookie 里面的 sessionId 字段。
sessionId 就相当于是会话的身份标识。
然后我们去判定这个sessionId 是否在当前服务器上存在。
如果不存在,则进入创建会话 逻辑 / 操作。
创建会话:会创建一个 HttpSession 对象,并且生成一个 sessionId。
sessionId 是一个很长的数字,通常是用 十六进制来表示的。
这个数字能够保证唯一性:它这里面有一些列相关的算法,能够生成一个唯一的sessionId,然后这个Id 就作为我们当前会话的身份标识。
接下来,就把 sessionId 作为 key,把这个 HttpSession 对象,作为 value。
把这个键值对,给保存到 服务器内存 的一个“哈希表” 这样的结构中。
这里的“哈希表”:只是表示一个类似 哈希表的数据结构。
也就是说,一定是一个键值对结构,但是 是不是 哈希表 就不能保证了。
再然后,服务器就会返回一个 HTTP 响应,把 sessionId 通过 Set-Cookie 字段 返回给浏览器。
然后,浏览器就可以保存这个 sessionId 到 cookie中了。2、获取会话
先获取到请求中的 cookie 里面的 sessionId 字段,也就是会话的身份标识。
判断这个 sessionId 是否在当前服务器上存在,也就是遍历“哈希表”中key,来判断这个 sessionId 是否包含在其中。
如果有,就直接调出 sessionId 对应的 HttpSession对象,并且通过返回值返回去。
会话,我们搞清楚了。
我们再来谈谈 HttpSession是什么?
HttpSession,其实就是 创建会话的时候,需要生成的一个关键对象。
这个对象本质上也是一个“键值对”的json结构。
注意! 由于 HttpSession 对象是一个键值对结构,所以允许程序员 对 HttpSession 对象进行套娃操作!
这和我前面讲的 表白墙 中获取所有信息记录的响应格式json是一样的。
一个 json 对象 可以让 另一个json 对象,作为自己keys中的一个value值。
HttpSession 也是如此,可以让另一个 HttpSession 对象作为 自己keys中的一个value值。
HttpSession键值对结构中,key必须是字符串类型,value 是一个 Object类型。
getCookies()方法会返回一个数组,包含客户端发送该请求的所有的 Cookie 对象.会自动把Cookie 中的格式解析成键值对
响应中就可以根据 addCookie 这个方法,来添加一个 Cookie 信息(键值对)到 响应报文中 ,
这里添加进来的键值对,就会作为 HTTP 响应中的 Set-Cookie 字段来表示。
换个说法:把一个cookie键值对 转换成 字符串 格式,通过 Set-Cookie 来返回到 客户端(浏览器)这里。
一个 HttpSession 对象里面包含多个键值对. 我们可以往 HttpSession 中存任何我们需要的信息
在编写案例代码之前,我们需要先约定前后端交互接口。
根据上面的逻辑图,一共有两组交互:
1、登录页面
2、主页面要明白:针对前后端交互接口,这里有很多种约定方式。这么多约定方式,使用哪种都可以!一定要确定出一种约定方式,然后前后端代码的编写,就需要围绕着这一种约定的方式进行编写。如果如果不按照这种方式去写,代码会出现很多问题!
比如:请求是GET,但是服务器中处理请求的方法是POST,那么就会触发405
首先我们来写一个简易风格的登录页面
接下来,就是编写一个服务器程序,用于处理登录页面发送的请求。
效果展示
先来看一下,登录成功的效果。
代码
loginServlet
@WebServlet("/login")
public class loginServlet extends HelloServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、处理用户请求
String username = req.getParameter("username");
String password = req.getParameter("password");
// 2、判断用户名 密码是否正确
// 正常来说是要放到数据库里进行存取的
// 注册的时候会把用户名和密码写入数据库
// 后续登录的时候,根据用户名看密码是否正确
// 这里为了简单就把密码写死
// 假设用户名为:zhangsan 密码为:123
if("zhangsan".equals(username) && "123".equals(password)){
//为了避免 username 和 password为空,而造成空指针异常,把写死的密码作
// 登录成功
//创建会话,并保存的身份信息
HttpSession httpSession = req.getSession(true);
// 往会话中存储键值对(关键信息),必要的身份信息
httpSession.setAttribute("username",username);
resp.sendRedirect("index");
}else {
resp.getWriter().write("login error!");
}
}
}
indexservlet
@WebServlet("/index")
public class indexServlet extends HelloServlet{
// doGet 要做的事情: 这回主页,主页就是简单的html
// 返回页面的前提,需要获得 用户名
// 也就是需要从 Httpsession 中拿到。
// 因为在上个服务器代码中,我们把用户名写作会话的属性,存入到会话中了。
// 此处的 getSession 的参数 必须为false!因为前面在登录过程中,已经创建过会话了
// 此处是要直接获取之前的会话。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession httpSession = req.getSession(false);
String username = (String)httpSession.getAttribute("username");
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("欢迎"+username+"");
}
}
login.html
登录页面