一.回顾Cookie
Cookie是浏览器持久化存储数据的一个机制。我们知道,为了保证安全性,网页无法访问主机的文件系统,所以要想存储数据,就要通过其他方式,也就是这里的Cookie。
下面我们来讨论一下用cookie存储用户信息的情况:
假设一个情景去医院看病。首先要挂号,前台医生就会给每一位患者一张就诊卡,后需要想看医生,只要刷一下就诊卡,医生电脑里就能够显示患者的全部情况(基本信息,既往病例,账户余额……)。
这张就诊卡就相当于是每个患者的cookie,刷卡这个过程就是会通过医院的系统去查询对应的身份标识,从而得到完整的身份信息。
二.什么是Session,理解会话机制
上面说的每个患者的身份信息,都会存在医院的系统中。这些数据必然要存在数据库中,但又不单单是数据库中。在服务器代码的逻辑的执行过程中,这些数据也会被从库中查询出来,先临时保存到某个内存结构中,后续要是需要修改信息,就先修改该内存结构中的信息,再更新数据库。
这个内存结构就是所谓的Session,即会话。
会话的合集本质上就相当于是一个hash表,其中的key就是sessionID,value就是对应用户的身份信息
三.Cookie和Session的配合
Cookie是客户端存储数据的机制(持久化),Session是服务器存储数据的机制(非持久化,因为是一种内存结构),两者往往是相互配合使用:
在cookie中存放了用户身份标识,也可以理解成sessionID,多个用户的id可能是在同一个cookie中存放(因为一个网站是多个用户所共享的)但是,每一个用户都有独自的Session,这个session就是一个键值对结构,键就是sessionid,值就是用户信息。
所以同一个服务器会存放多个session,通过类似于hash表的结构组织起来
四.与cookie和session有关的api
Cookie是浏览器/客户端的机制,Session是服务器的机制,所以Servlet已经提供好了相关api供我们去使用
1.HttpServletRequest中的方法
HttpSession getSession(boolean )这个方法是在服务器中获取会话,参数如果为true,则当session不存在时就会新建一个会话;参数如果为false,则当会话不存在时仅仅返回null。Servlet通过HttpSession来表示一个会话。服务器上存有多个会话(类似于HashMap<String,HttpSession>,String就是sessionid,HttpSession就是session对象)这个方法主要是给登录场景使用,它就是拿着sessionid去查表。
Cookie[] getCookies()每个cookie都是一个键值对,cookie对象的属性就是key和value,key就是cookie的名,value就是该cookie的值
2.HttpServletResponse中的方法
void addCookie(Cookie cookie)把指定的cookie添加到响应中。
3.HttpSession中的方法
Session对象本身,就是键值对结构,这些键值对就是通过下面的两个Attribute(属性)方法完成的
1.Object getAttribute(String name)该方法返回在该会话中具有指定名称的对象(相当于时返回该用户的其中一个指定的信息)
2.void setAttribute(String name,Object value)该方法是个这个会话添加一个新的属性)
3.boolean isNew(),判定当前会话是否是新创建的会话
4.Cookie类中的方法
每个cookie就是一个键值对
1.String getName()返回当前cookie的名称(这个名称是之前响应中的Set-Cooki字段给浏览器设置的)
2.String getValue()获取该cookie的值
3.void setValue(String newValue)设置该cookie的值
五.代码实例:实现用户登录
Cookie/Session机制,很重要的作用就是辅助用户完成登录功能
先创建好项目login,注意目录结构,并且引入依赖,这里主要使用servlet的依赖
1.先编写一个登录页面
静态页面是放在webapp中的。在webapp中创建新的文件login.html,右键->opin in->explorer,再用vscode打开
首先生成html的框架,然后把title改成“登录”。然后在body中用form表单来触发浏览器发送请求给服务器从而实现登录
注意,action中的路径是不加/的
编写好后,点击右上角的edit configurations,点加号,点SmartTomcat,配置一下然后运行,这样就能看到一个登录页面了:
然后输入张三 123,用fiddler抓包,看看能不能抓到一个POST请求:
注意content-Type就是application/x-www-form-urlencoded
此处的登录也可以使用json,但是使用了json就需要通过ajax方法来构造请求
2.服务器处理上述请求
在java目录中,创一个新的包login,再创建一个新的类LoginServlet
注意,创建会话时传入true,他就hi根据请求中传入的cookie中的sessionid去查询hash表,从而找到对应的session对象。若cookie中没有sessionid或者sessionid没有查到对应的对象,这就说明没有登陆呢,然后就可创建出session对象(前提是true)。创建的同时也会制定好sessionid,把对象保存到hash表中,而把sessionid设置到响应中(在header中加上Set-Cookie字段)再传回给浏览器,让浏览器使用cookie保存sessionid。
最后登陆成功后的重定向操作就会触发发送get请求(请求index页面)
3.服务器处理重定向请求
既然要跳转到index页面,那就要生成一个主页,创建一个新的类IndexServlet
注意getAttribute方法的返回值是Object类型,所以要进行向下转型
到此为止,简单的登录逻辑就写完了,在浏览器上验证一下,用fiddler抓包,首先抓到一个get请求,是获取login.html
可是我们发现,这个里面的header里有一个cookie,其中就有一个JSESSIONID,这就奇怪了,这还没登陆呢,怎么就有sessionid了呢?先往下看:这个请求下面对应一个响应:
状态码是304,HTTP 304,有时也称为“304 Not Modified”,是一种与浏览器通信的代码:“自上次访问以来,请求的资源未被修改。”本质上,服务器告诉您的浏览器,自您最近一次访问该页面以来,浏览器中存储(缓存)的资源没有被修改。这就是因为刚才我们在这个浏览器上测试代码时已经登陆了一次了,所以已经存了id。先不管这个,我们再登陆一下,看看能不能抓到重定向响应以及发送的get请求,如下就是获取到的post请求和重定向响应以及get请求,以及get请求的响应:
但发现,最后一个响应报文的内容竟然不认识,这是因为浏览器是使用的GBK字符集,然而idea使用的utf8字符集,要想正确解析请求内容,就要在解析前将请求内容换成utf8编码,如下:
这回咱们用个别的浏览器,用谷歌的浏览器访问一下:
首先是一个get请求,请求获取到login.html以及响应返回页面,这次,get请求里面没有cookie这一项了,因为之前在谷歌浏览器没有访问过此页面,响应也不是304了。
然后输入用户名和密码,点击登录,抓包抓到了一个post请求:
这个请求的content-Type就是form表单(也就是正文类型)。
然后有一个它对应的响应:
注意,这是王五第一次登录,所以服务器这边没有他的session,所以就创建了它的session并且设置了id,然后通过响应将id返回给浏览器,也就是这里的Set-Cookie字段!!!同时,登录也会触发重定向,所以状态码是302,并且location是index
一点击登录就会触发页面跳转,就会给index路径发送一个get请求,如下是get请求及其响应:
注意这个get请求的cookie字段,它和刚才的set-cookie就是一样的!!