目录
Cookie
Cookie的概念
Cookie是HTTP协议中的一个字段,同时也是浏览器在客户端这边保存数据的一种方式。
登录一个网页,用抓包工具抓一下登录请求就可以看到:
Cookie的来源、存储信息、最终的去处
- Cookie是服务器产生的,服务器可以通过响应中的Set-Cookie字段来设置,返回给浏览器
- Cookie是存储在浏览器这边的,浏览器会根据域名/地址来分别存储Cookie。Cookie里面存储的是键值对结构的字符串,这个字符串都是程序员自己定义的
- Cookie的作用就是用来存储用户的信息,然后当用户再次向服务器发出请求数据的时候,请求中就会带上Cookie,它就像一把钥匙,然后拿着它去开对应的门。
令牌就是Cookie中的字段
Session
Session的概念
Session叫做会话,他主要是用来检查请求数据中的Cookie字段是否在服务器存在和是否合法
因为Cookie是由服务器产生的,所以服务器中会存储Cookie中的一些信息,当浏览器再次请求数据时,服务器就会将Cookie中的一些字段和服务器存储的信息比对,如果比对成功就让这次请求通过,如果比对失败则不会通过这次请求。而Session所做的就是这样的工作,它就像检票员,如果发现你不是这趟车的就不会让你上车。
Session的本质
Session是在服务器这边存储的,可以简单的把Session想象成一个hash表,其中的key就是令牌的ID(token/sessionId),value就是程序员自己定义的数据(这里可以存储用户的身份信息等)。
sessionId是服务器生成的一个“唯一字符串”,每一个登录的用户都有自己唯一不同的ID,这就好比身份证一样,每个人都不相同。
Cookie和Session之间的工作
总结上面的工作就是:
- 当用户登录的时候,服务器在session中新增一个记录,并把sessionId/token返回给客户端
- 客户端后续再给服务器发送请求的时候,需要在请求中带上sessionId/token
- 服务器收到请求之后,根据请求中的sessionId/token在session信息中获取到对应的用户信息,再进行后续操作
Cookie和Session的区别
- Cookie是客户端机制,Session是服务器机制
- Cookie和Session经常在一起配合使用,但是不一定的必须配合使用。完全可以Cookie来保存一些数据在客户端,然后Session中的sessionId也不一定的通过Cookie/Set-Cookie来传递
Cookie和Session的方法
HttpSession getSession()
功能有两个:
尝试根据当前请求中的sessionId来获取当前的session
1.如果session不存在,就创建(创建一个HttpSession对象,作为value。创建一个sessionId作为Key。把key和value插入到hash表中,同时把这个sessionId通过Set-Cookie字段返回给浏览器)
2.如果存在就返回
void addCookie(Cookie cookie) 功能:把指定的cookie添加到响应中
Object getAttribute(String name) 功能:返回session会话中具有指定名称的对象,如果没有指定名称的对象,则返回null
通过一个登录案例来探究这些方法和会话机制:
首先写一个html,里面包含用户名密码的输入框,以及登录按钮。然后要有一个LogServlet来处理登录请求。在搞一个IndexServlet,模拟登录完成以后,跳转到的主页面,在这个页面就可以获取到用户的身份信息
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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>
@WebServlet("/login")
public class LoginSelvlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
//1.先从请求body中读取用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
//2.判定一下用户名和密码是否正确(读固定的密码)
if(!"zhangsan".equals(username)||!"123".equals(password)){
//登录失败
resp.getWriter().write("登陆失败");
return;
}
//3.登陆成功,则创建出一个会话来,会话不存在就创建
//会话是根据请求中的sessionId来查的,sessionId是在Cookie中的
//但是此处是首次登录,此时请求中是没有Cookie(Cookie是在服务器返回)
//此时就会触发"找不到就创建"这样的流程,同时这里进行的操作:先创建出一个HttpSession对象(作为value),再成一个随机的字符串,作为sessionId(作为key)
//把这个key和value插入到hash表中,同时把这个生成的sessionId通过Set-Cookie字段返回给浏览器
HttpSession httpSession = req.getSession(true);
//还可以存入程序猿自定义的数据,可以存入身份信息(用户名和登录次数)
httpSession.setAttribute("username","zhangsan");
httpSession.setAttribute("loginCount",0);
//4.让页面跳转到主页,使用重定向的方式实现即可
resp.sendRedirect("index");
}
}
@WebServlet("/index")
public class indexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//根据当前用户请求中的sessionId,获取到用户信息,并且显示到页面上
resp.setContentType("text/html;charset=utf-8");
//1.判定当前用户是否已经登录了,(请求中有没有sessionId,以及sessionId是否合法)
//如果会话不存在,就不能创建了,只是查询,不是登录
HttpSession httpSession = req.getSession(false);
if(httpSession==null){
//当前没有找到合法会话,当前用户尚未登录
//返回并让用户重新登录
System.out.println("登录失败");
return;
}
//2.用户登录成功就可以从HttpSession获取到用户里面的信息
String username = (String)httpSession.getAttribute("username");
Integer loginCount = (Integer)httpSession.getAttribute("loginCount");
loginCount = loginCount+1;
httpSession.setAttribute("loginCount",loginCount);
//3.返回一个HTML页面
StringBuilder html = new StringBuilder();
html.append("<div>用户: "+username+"</div>");
html.append("<div>访问次数: "+loginCount+"</div>");
resp.getWriter().write(html.toString());
}
}
启动服务器,在浏览器中输入相关URL后:
再通过抓包工具可以看出:
第一次请求中的数据里面是没有Cookie的
但是响应中就返回了一个Cookie,这个Cookie就是服务器生成的
上面的响应中还有一个Location的字段,他表示要跳转到那个URL,根据URL就会跳转到index路劲下面,于是就触发第二次请求:
可以看到第二次请求中就带有Cookie,而这个Cookie就是第一次响应中的Cookie,而里面的JSESSIONID就是sessionId,它=号后面的一串字符串就是十六进制的随机字符串。在这个请求头中,还有一个Referer字段,它表示从哪个URL跳过来的。
再来看第二次请求中的响应内容:
第二次响应中的body就是要输出在浏览器界面上的内容。
后续的请求中都带有相同的Cookie:
总结上面的代码:
为了展示效果明显,这里就不跳转到index路径下面的Servlet了,而是让他跳转到一个指定的html页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>待办事项</title>
<!--实现页面样式-->
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
/* background-color: transparent;*/
width: 800px;
height: 800px;
margin: 0 auto;
display: flex;
}
.todo,
.done {
width: 50%;
height: 100%;
}
.container h3 {
height: 50px;
text-align: center;
line-height: 50px;
background-color: #FB7299;
color: #fff;
}
.nav {
width: 800px;
height: 100px;
margin: 0 auto;
display: flex;
align-items: center;
}
.nav input {
padding-left: 5px;
width: 600px;
height: 50px;
}
.nav button {
width: 200px;
height: 50px;
}
.nav button {
width: 200px;
height: 50px;
/*去除按钮周围的边框,button会自带2个像素的实线边框,因此要去掉*/
border: none;
background-color: #6CD083;
color: #ffffff;
}
.row {
height: 50px;
display: flex;
align-items: center;
}
.row input {
/*设置上下外边距为0,左右外边距为10*/
margin: 0 10px;
}
.row span {
width: 300px;
}
.row button {
width: 50px;
height: 40px;
}
</style>
</head>
<body>
<!--创建页面布局-->
<div class="nav">
<input type="text">
<button>新建任务</button>
</div>
<div class="container">
<div class="todo">
<h3>未完成</h3>
<!--这里只是方便写代码才设置这样的操作,但实际开发中还是以操作为主-->
<!-- <div class="row">
<input type="checkbox">
<span>吃饭</span>
<button>删除</button>
</div>-->
</div>
<div class="done">
<h3>已完成</h3>
</div>
</div>
<!--实现页面行为-->
<script>
//实现新增任务
var addTaskButton = document.querySelector('.nav button');
addTaskButton.onclick = function () {
//1.获取到输入框
var input = document.querySelector('.nav input');
//2.获取到输入框内容
var taskContent = input.value;
if(taskContent==''){
console.log('当前任务为空不能新增任务');
return;
}
//3.根据内容新建一个元素节点
var row = document.createElement('div');
row.className = 'row';
var checkbox = document.createElement('input');
checkbox.type = 'checkbox';
var span = document.createElement('span');
span.innerHTML = taskContent;
var button = document.createElement('button');
button.innerHTML = '删除';
row.appendChild(checkbox);
row.appendChild(span);
row.appendChild(button);
//4.把新节点插入到TODO中
var todo = document.querySelector('.todo');
todo.appendChild(row);
//5.把输入框里面的内容删除
input.value = '';
//点击复选框后将元素放到"已完成"
//6.给checkbox注册点击事件
checkbox.onclick = function () {
/*操作row这个对象,看是吧row这个对象放在TODO里面还是done里面
注意,是先触发checked为true,然后再调用onclick函数*/
if (this.checked) {
var target = document.querySelector('.done');
} else {
var target = document.querySelector('.todo');
}
target.appendChild(row);
}
//点击删除按钮删除该任务
//7.给删除按钮注册点击事件
button.onclick = function (){
/*这里row就是那个要删除的元素(child=>row)
row的parent可能是todo,也可能是done,这里可以直接通过parentNode属性来获取row的父元素*/
var parent = row.parentNode;
parent.removeChild(row);
}
}
</script>
</body>
</html>
@WebServlet("/login")
public class LoginSelvlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
//1.先从请求body中读取用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
//2.判定一下用户名和密码是否正确(读固定的密码)
if(!"zhangsan".equals(username)||!"123".equals(password)){
//登录失败
resp.getWriter().write("登陆失败");
return;
}
//3.登陆成功,则创建出一个会话来,会话不存在就创建
//会话是根据请求中的sessionId来查的,sessionId是在Cookie中的
//但是此处是首次登录,此时请求中是没有Cookie(Cookie是在服务器返回)
//此时就会触发"找不到就创建"这样的流程,同时这里进行的操作:先创建出一个HttpSession对象(作为value),再成一个随机的字符串,作为sessionId(作为key)
//把这个key和value插入到hash表中,同时把这个生成的sessionId通过Set-Cookie字段返回给浏览器
HttpSession httpSession = req.getSession(true);
//还可以存入程序猿自定义的数据,可以存入身份信息(用户名和登录次数)
httpSession.setAttribute("username","zhangsan");
httpSession.setAttribute("loginCount",0);
//4.让页面跳转到主页,使用重定向的方式实现即可
resp.sendRedirect("http://127.0.0.1:8080/messageWall/work.html");
}
}
启动服务器,输入URL,然后登录: