为什么会有cookie??
cookie是浏览器在本地存储数据的一种机制首先我们的数据很多都是存放在服务器上的,但是服务端也想这能不能在客户端也存放一些数据。但是又要保证本地的安全,不能让浏览器直接控制本地的存储设备,所以就做了一个中立的做法,也就是说让浏览器开一个口子,使用某些特殊的键值对,来限定数据的存储保证安全,同时又可以让浏览器往本地存储数据。
cookie从哪里来的??
cookie是从服务器来的。服务器在响应中会带有Set-Cookie字段,通过这个字段就可以把要保存在浏览器本地的数据给返回回去。
cookie到哪里去??
后续浏览器访问服务器的时候,就会把当前本地的所有cookie都通过http带给服务端
cookie有啥用??
最典型的场景就是使用cookie保存当前用户的登录状态。就例如我们经常访问b站的时候,只登录了一次,后面关闭页面再次打开,任然保持这上次的登录状态。
在cookie保存用户身份标识,这样的应用场景之中,此时身份标识该如何分配,以及身份信息该如何存储,都是需要服务器的支持的。这个就要利用一个session会话机制。
session
session给当前用户分配一个sessionId。同时记录下当前用户的身份信息(可以自定义的),这个id就会被返回到浏览器的cookie中,后续浏览器访问服务器都会带着这个。从而能让服务器识别到当前用户的身份。
下面我们结合代码,来了解一下cookie的机制。
HttpServletRequest类中的相关方法
方法 | 描述 |
HttpSession getSession() | 在服务器中获取会话,如果参数为true,则当前不存在会话时会创建新会话,如果参数为false,则当不存在的时候会返回null |
Cookie[ ] getCookies() | 返回一个数组,包含客户端发送该请求的所有cookie对象,会自动把cookie中的格式解析成为键值对 |
下面我们来解析一下这个getSession的用法,首先getSession的参数如果为false,那么就会触发以下流程:
- 读取cookie中的sessionid
- 然后再服务器这边根据服务器存储的数据和sessionid一起,查询Session对象
- 如果查到了就会直接返回这个session对象
- 否则返回null
如果为true的话,与false唯一不同就只有第四步不一样,如下:
- 读取cookie中的sessionid
- 然后再服务器这边根据服务器存储的数据和sessionid一起,查询Session对象
- 如果查到了就会直接返回这个session对象
- 否则返回创建一个新的session对象,同时生成一个sessionid,同时以sessionid为key ,以session对象为value构成一个键值对,然后把这个键值对存储到服务器的hash表里面
- 同时把sessionid以setcookie 的方式返回给浏览器
这个对我们实现登录功能非常实用。
简单的实现cookie登录功能
接下来我们基于httpServlet来实现一个简单的登录功能。
首先需要提供两个页面:
- 登录页(输入用户id,输入密码,然后还有一个登录按钮,点击登录就会发起一个http请求,服务器就会处理这个http请求,服务器处理这个请求的时候就会验证用户名和密码,如果用户名和密码都ok,就会跳转主页)
- 主页(不存在任何业务逻辑,仅仅只是展示一个欢迎页面)
其中的登陆页面就只是单纯的html页面,还需要写一个servlet,实现登录的时候用户名密码校验,还要写一个servlet来生成主页
实现登录页面
首先先来回忆一下我们传输键值对的方式,一说到传输键值对,那么肯定就要提到html里面的form表单
<form action="login" method="post"> <input type="text" name="username"> <!--用户输入的用户名--> <br> <input type="password" name="password"> <!--用户输入的密码--> <br> <input type="submit" value="登录"> </form>
form会组织这里的数据,以键值对的形式提交给服务器,其中key就是input的name属性,value就是input中用户输入的内容,最终会构造post请求,在body中以键值对的形式,进行组织。服务器通过getParameter方法来获取指定key的value,具体getParameter的方法可以参看我前面的servelet的内容。
完整的登录页面的代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="login" method="post"> <input type="text" name="username"> <!--用户输入的用户名--> <br> <input type="password" name="password"> <!--用户输入的密码--> <br> <input type="submit" value="登录"> </form> </body> </html>
实现servlet逻辑
新建一个类,为loginServlet类来处理上述的登录请求。
登录的请求形式如下:
- POST/login
- Content-Type:application/x-www-form-urlencoded
- username=zhangshan&password=1233
一般像登录这样的请求都是post
构造好的登录的处理逻辑如下:
package Login; import javax.jws.WebService; 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 java.io.IOException; @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf8"); resp.setContentType("text/html;charset=utf8"); // 首先通过getParameter方法来获取对应key的value值 String username = req.getParameter("name"); String password = req.getParameter(("password")); // 然后来验证这个用户和密码是否正确 if (username == null || password == null || username.equals("") || password.equals("")){ resp.getWriter().write("当前用户名和密码不能为空"); return; } // 假设此处的密码和用户名只能是zhangsan和1234 // 逻辑上此处应该是在服务器的数据库里面去查找验证用户名和密码,此处简化步骤 if (username.equals("zhangsan") || username.equals("lisi")) { if (password.equals("1234")) { // 此时用户输入密码正确 } else { // 用户密码有误 resp.getWriter().write(); } } else { // 用户名错误 } } }
这是简单的登录逻辑。
接下来,如果登录的用户名和密码验证正确的话,就会触发session会话,如下:
// 此时用户属于未登录的状态,所以请求的cookie中没有sessionid,所以无法从服务器的hash表中获取到对应的session对象 // 所以此处吧getSession中的参数设置为true,(在查询不到的时候创建新的sesion会话和session对象并存储到hash表中) // 同时会返回这个session对象,并且接下来会将新的sessionid通过响应http返回给客户端浏览器。 HttpSession session = req.getSession(true); // 然后让刚刚创建好的session对象存储我们自定义的数据,就可以在这个对象中存储用户的身份信息 session.setAttribute("username",username);
session对象本身就可以看作一个hash表,通过setAttribute方法来存储键值对(setAttribute方法里面有两个参数,一个是String(key),一个是Object(value)),后续就可以通过getAttribute根据key来获取value。
最后登录成功之后跳转到主页:
// 登录成功之后 需要自动跳转到 主页欢迎页 resp.sendRedirect("index");
下面是loginServlet的代码:
package Login; import javax.jws.WebService; 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 LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf8"); resp.setContentType("text/html;charset=utf8"); // 首先通过getParameter方法来获取对应key的value值 String username = req.getParameter("name"); String password = req.getParameter(("password")); // 然后来验证这个用户和密码是否正确 if (username == null || password == null || username.equals("") || password.equals("")){ resp.getWriter().write("当前用户名和密码不能为空"); return; } // 假设此处的密码和用户名只能是zhangsan和1234 // 逻辑上此处应该是在服务器的数据库里面去查找验证用户名和密码,此处简化步骤 if (username.equals("zhangsan") || username.equals("lisi")) { if (password.equals("1234")) { // 此时用户输入密码正确 } else { // 用户密码有误 resp.getWriter().write(); } } else { // 用户名错误 } // 此时用户属于未登录的状态,所以请求的cookie中没有sessionid,所以无法从服务器的hash表中获取到对应的session对象 // 所以此处吧getSession中的参数设置为true,(在查询不到的时候创建新的sesion会话和session对象并存储到hash表中) // 同时会返回这个session对象,并且接下来会将新的sessionid通过响应http返回给客户端浏览器。 HttpSession session = req.getSession(true); // 然后让刚刚创建好的session对象存储我们自定义的数据,就可以在这个对象中存储用户的身份信息 session.setAttribute("username",username); // 登录成功之后 需要自动跳转到 主页欢迎页 resp.sendRedirect("index"); } }
实现生成主页
首先需要验证身份信息:
HttpSession session = req.getSession(false); if (session == null ) { // 当前是未登录状态 resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("当前用户未登录"); return; }
此处的查询到的sessiom对象应该是和刚才登录的session对象是同一个对象,因为是同一个sessionid。刚才登录成功,sessionid就会通过Set-Cookie返回给浏览器,浏览器下次访问IndexServlet的时候,就会带上这个同一个sessionid。
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 { // 此处禁止创建新的会话,如果没有找到,那么就认为此用户为未登录状态 // 如果找到了才认为是登录状态 HttpSession session = req.getSession(false); if (session == null ) { // 当前是未登录状态 resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("当前用户未登录"); return; } String username = (String)session.getAttribute("username"); // 在服务器这边维护了一个全局的hash表,key就是sessionid,value就是session对象 if (username == null) { resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("当前用户未登录"); return; } // 如果都ok就生成动态主页 resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("欢迎你!!"+ username); } }
部署tomcat服务器
选择edit configuration:
添加smart tomcat选项:
设置端口8080(默认),重新命名,然后启动:
访问登录页面
访问: http//127.0.0.1:8080/login/login.html
输入“zhangsan”,1234
就会收到浏览器请求保存cookie的请求。点击明白即可,我们点击编辑可以查看到我们的用户名和密码都已经保存到本地了。
下面使用费德勒来抓包看看我们信息传递的过程,如下:
输入用户名和密码之后就会构造一个http请求访问login的页面,随后访问index页面
抓包的结果可以看出来,第一次访问的时候是没有cookie的,后面setCookie之后就有了。
第二次请求index的请求如下:
可以看到最后的存在一个Cookie字段
下次无论你怎么刷新,都是显示“欢迎你!!zhangsan” 。
问题
上面的sessionid也不一定会一直存在,比如说服务器重新启动,session的hash表就会清除,此时如果再访问就可能出现sessionid无法查询到,此时还是未登录状态,需要重新登录。
服务器默认保存会话是再内存中的,一但重启服务器会话服务就会销毁,但是smart tomcat为了方便程序员方便调试程序,会在停止服务器的时候,把会话持久化保存,下次启动的时候自动恢复到内存中,这个时候会话还是不会丢失的(这个时候真的有效,跟tomcat版本有关)。