HTTP协议是一种无状态的协议
怎么理解无状态:认证的请求-响应 无法和 业务请求-响应 相对应
每次的的 请求-响应都是独立的
所以引入了Cookie
一、Cookie
狭义的讲就是:在认证阶段,返回给用户的可验证证明
通过这个过程,就可以使得HTTP协议具有状态了
上图是a和b分别认证并保存各自Cookie信息的过程,当a和b二次请求的时候,就带着各自的Cookie
通过读取Cookie就能知道新的请求是由谁发起的,解决了HTTP无状态特性
Cookie是由响应格式返回的:
- 响应:状态行+响应头+响应体
状态行:HTTP版本以及成功与否
响应头 —— 一般是给浏览器看的
响应体:正文部分,也就是用户想要的资源 —— 用户可以看到的
Cookie其实是放在了响应头里,通过 Set-Cookie:key = value 这个响应头形式
同样的:再次请求时
请求也是由请求行+请求头+请求体组成的
请求时带着的Cookie也是在请求头里,是哪个请求头呢? Cookie: key = value
1、先通过手动方式设置Cookie(已经提前配置好了该Servlet项目)
CookieDemo.java
package tomato;
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("/set-cookie")
public class CookieDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//手动设置响应头 resp.setHeader
resp.setHeader("Set-Cookie","name = Tomato");
}
}
Printcookie.java
package tomato;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/print-cookie")
public class PrintCookie extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain;charset=utf-8");
PrintWriter writer = resp.getWriter();
//手动从请求头中获取
String cookie = req.getHeader("Cookie");
if (cookie == null){
System.out.println("没有设置Cookie!");//这是打印在终端的
writer.println("没有设置Cookie");//打印在客户端的
}else{
System.out.println(cookie);
writer.println(cookie);
}
}
}
运行后结果:(注意url,是直接访问的print-cookie)
浏览器:
终端:
然后我们什么都不变,在浏览器调用一下 set-cookie:
然后回到刚刚的print-cookie页面刷新:
此时可以看到拿到了cookie
cookie信息保存后,当我们再次请求时,就可以直接拿着cookie认证了
总结
- 狭义cookie:身份证明
- 如何传递:
1)服务器设置cookie时(有时候也叫种cookie),通过Response Header : Set-Cookie : k = v
2)客户端带着Cookie时,通过Request Header : Cookie : k = v
注意:每次当我们的代码有变化的时候,都需要重启Tomcat或者重新部署
2、通过Cookie对象设置Cookie信息
就把Cookie看作是一份证明,其中包含
1、 有效内容: key = value
2、 适用范围:哪些Host中可以使用
3、 过期时间
4 、 其他属性
Cookie:k=v;domain=适用范围;expire=2020-04-17
实际上Sevlet将他封装成了Cookie对象
CookieDemo.java
package tomato;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/set-cookie")
public class CookieDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过Cookie对象设置Cookie信息
Cookie cookie = new Cookie("name","tomato");
resp.addCookie(cookie);
//设置过期时间
cookie.setMaxAge(10*60);//600s后过期
}
}
效果和1中的手动设置Cookie是一样的
print-cookie.java
@WebServlet("/print-cookie")
public class PrintCookie extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain;charset=utf-8");
PrintWriter writer = resp.getWriter();
//当然也可以打印过期时间等等
Cookie[] cookies = req.getCookies();
for (Cookie cookie : cookies){
System.out.println(cookie.getName() + "=" + cookie.getValue());
writer.println(cookie.getName() + "=" + cookie.getValue());
}
}
}
运行结果:
二、Session
光用cookie有什么问题吗?
当前的cookie中,所有的信息全部来自客户端,也就是完全信任客户端,客户端说它是谁,服务器就认为他是谁,实际上这个身份认证是没有证明的,缺少第三方的辅助,所以就引入了Session
Cookie + Session 配合工作
得到用户数据,认证后,将对象放在Session服务器中,然后返回给HTTP服务器的是Session-ID,HTTP服务器返回给客户端的是带着该Session-ID的Set-Cookie:Session-ID = ?,浏览器会保存该信息,当下一次用户发起请求的时候,会从Cookie中取出该Seesio-ID,然后去session服务器中取资源
Cookie + Session 配合使用,“安全的” 解决了HTTP的无状态问题
User.java
//用户对象
package tomato;
import java.util.ArrayList;
import java.util.List;
public class User {
String username;
String password;
private static List<User> userList = new ArrayList<>();
public User(String username, String password) {
this.username = username;
this.password = password;
}
static {
userList.add(new User("a1","a"));
userList.add(new User("b2","bb"));
userList.add(new User("c3","ccc"));
}
//登录
public static User login(String username,String password){
for (User user: userList){
if (user.username.equals(username) && user.password.equals(password)){
return user;
}
}
return null;
}
}
LoginServlet.java
//验证登录
package tomato;
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;
import java.io.PrintWriter;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
//得到Session信息
HttpSession session = req.getSession();
resp.setContentType("text/plain; charset=utf-8");
PrintWriter writer = resp.getWriter();
User user = User.login(username,password);
if (user != null){
//表示登陆成功,把User对象放进Session服务器里
session.setAttribute("user",user);
writer.println("登录成功,欢迎" + user.username);
}else{
writer.println("没有这个用户");
}
}
}
WhoAmI.java
//查看当前用户
package tomato;
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;
import java.io.PrintWriter;
@WebServlet("/who-am-i")
public class WhoAmI extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain; charset=utf-8");
PrintWriter writer = resp.getWriter();
HttpSession session = req.getSession();
User user = (User)session.getAttribute("user");
if (user == null) {
writer.println("我不知道你是谁");
}else {
writer.println("你是:" + user.username);
}
}
}
login.html
//简单登录页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/login" method="post">
<input type="text" name="username">
<input type="text" name="password">
<button type="submit">登录</button>
</form>
</body>
</html>
直接访问who-am-i:
登录:
这个时候再访问who-am-i:
结果解释:
Cookie和Session区别?
其实这个问题本身就有点问题,cookie和session是配合工作的,而不是两种方案的选择
硬要说的话:
- Cookie + Session一起“安全地”解决HTTP无状态的问题
- Cookie是主要用于 客户端和HTTP服务器之间,保持会话
Cookie主要保存在客户端的电脑上(浏览器的内存、文件中) - Session主要是服务器内部使用,目的是解决Cookie本身不太安全的问题(恶意用户,泄露篡改信息)
- Session一半保存在服务器电脑上(内存、文件、MySQL、专门的Session服务器)
- 在HTTP协议传输时,有两个Header:
1)Cookie——请求头:
2)Set-Cookie——响应头 - Cookie有适用范围(与主机——Host有关)
- 有过期时间