什么是Cookie

Cookie收发机制

首先先对cookies浏览器端、服务端收发机制做一个简单的了解。

  1. 浏览器向服务端发送请求
  2. 服务端接收到请求并设置cookie值后返回请求资源
  3. 浏览器收到请求资源并将服务端设置的cookie值存储到浏览器cookies中
  4. 浏览器再次发起请求并附带上一次服务端设置的cookie值(附带cookie值的操作由浏览器自动执行无需开发者手动设置)
  5. 服务端根据cookie值做相应逻辑处理

Ok,废话少说马上进入实验。
首先搭建服务端。使用Go语言编写一个简单的http服务器。初始代码如下。

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/get", handle)
	http.ListenAndServe(":53055", nil)
}

func handle(w http.ResponseWriter, r *http.Request) {
	cookies := r.Cookies()
	fmt.Print("cookies:")
	fmt.Println(cookies)
	w.Write([]byte("success"))
	for _, temp := range cookies {
		w.Write([]byte("cookie:"+temp.Value))
	}
}

可以看到当前代码的主要功能是当请求到来时打印请求所携带的cookie值,并将cookie值以文本方式返回到前台。
现在打开浏览器访问该地址

可以看到请求访问成功了,并且无论怎么刷新cookie值都为空。后台打印结果

现在修改后台代码为请求附加cookie值,修改完成后的后台代码

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/get", handle)
	http.ListenAndServe(":53055", nil)
}

func handle(w http.ResponseWriter, r *http.Request) {
	cookies := r.Cookies()
	fmt.Print("cookies:")
	fmt.Println(cookies)
	cookie:=&http.Cookie{Name:"wxm",Value:"530",Path:"/",Domain:"localhost",Expires:time.Now().AddDate(1,1,1),RawExpires:"/",MaxAge:600000000,HttpOnly:false,Secure:false,}
	http.SetCookie(w,cookie)
	w.Write([]byte("success"))
	for _, temp := range cookies {
		w.Write([]byte("cookie:"+temp.Value))
	}
}

按照之前的解释来说浏览器的第一次请求不附带任何cookie信息。则返回结果应与上面的结果相同。现在开始测试(注意别请求多了哦)

虽然返回结果相同但注意右侧cookie信息已经正确的存储到了浏览器cookies中。
后台控制台

接下来发起第二次请求,理论上来说浏览器会自动将cookie信息附加到请求当中。则后台将会通过for _, temp := range cookies { w.Write([]byte("cookie:"+temp.Value)) }该段代码将cookie信息返回给前台。

可以看到符合预期(忘换行了,结果都在一行上(⊙﹏⊙))。前台成功打印了cookie信息

后台打印结果为

跨域Cookie

Ajax请求发生跨域时默认是不会携带cookie的,如果想要携带cookie需要手动为Ajax指定withCredentials属性。代码实现如下:

  1. 原生Ajax
var xmlHttpRequest = new XMLHttpRequest()
xmlHttpRequest.withCredentials = true //重点是这一行
xmlHttpRequest.open('get','http://127.0.0.1:53000/getPit')
xmlHttpRequest.onreadystatechange=function(){
if(xmlHttpRequest.readyState==4){
    if(xmlHttpRequest.status==200||xmlHttpRequest.status==304){
    window.console.log(JSON.parse(xmlHttpRequest.response))
    }
}
}
xmlHttpRequest.send()
  1. Jquery
$.get({
    type:'get',
    url:'http://127.0.0.1:53000/detect',
    dataType: "json",
    crossDomain:true,//指明需要跨域
    //xhrFiled告诉Jquery跨域时携带cookie
    xhrFields:{
        withCredentials:true
    },
    success:function(res){
        window.console.log(res)
    }
    })
  1. axios
//告诉axios跨域时携带cookie
this.axios.defaults.withCredentials = true
this.axios.get("http://127.0.0.1:53000/getPit").then(e=>{
window.console.log(e)
})

可以看到主要是为发出Ajax请求的XMLHttpRequest对象的withCredentials属性指定为true

当然只有前台指定了该参数是不行的,后台需要做对应的配置,后台根据开发语言的不同配置也有所出入,尤其是使用了一些Web开发框架后框架可能会有对应的配置方式。

但原理都是向http响应头中写入 Access-Control-Allow-Credentials:true属性这里我是用的是Golang net/http标准库代码为:

    //放行所有跨域请求
	ref:=strings.TrimSuffix(r.Referer(),"/")
	w.Header().Set("Access-Control-Allow-Origin", ref)
	w.Header().Set("Access-Control-Allow-Credentials", "true")

这里需要注意一旦声明跨域需要携带cookie放行跨域的Access-Control-Allow-Origin属性就不能使用*通配符了,而是需要指定具体域名且必须为你发起请求的域名。我这里代码中的r.Referer()函数的作用便是获取发情请求的域名。r是gohttp.Request指针。

这样配置完成后Ajax请求就能够正常携带cookie向后端发起跨域请求了。但现在还存在一些问题

  1. 前端不能使用document.cookie操作,原因是后台返回的cookie的domain属性与前台域名不相同,该cookie并不会存储在当前页面的cookie中。但是发起请求的url与该cookie的domain相同。这里做了个简单测试。
    前端Ajax请求参照之前的代码,后端部分代码为:
func GetPit(c *engine.Context) {
	fmt.Println(c.Req.Cookies())
	cookie := http.Cookie{
		Name:    "user",
		Value:   "wxm530",
		Path:    "/",
		Expires: time.Now().AddDate(1, 0, 0),
	}
	http.SetCookie(c.Writer, &cookie)
	c.Json(nil)
}

现在发起请求两次

从图中可以看到两次请求均发送成功且第一次没有携带cookie,合情合理,第二此请求浏览器成功将服务器设置的cookie返回给后台,但是浏览器cookie指示当前页面并没有存储cookie,这就是前面所说的这个cookie被存储在别的域名下,并且使用chrome可以很方便的看到那就是地址栏的起始位置有一个小叹号。

可以看到该cookie被存储在了127.0.0.1中了。

  1. 相对与http协议来说,未来Chrome可能不能使用现有方法传cookie,原因是我在实验时收到了Chrome的这样一条警告。如下图

意思就是说我服务器设置的cookie没有SameSite属性,未来Chrome将不会发送没有SameSite属性的跨域cookie,并且设置了SameSite属性的同时也要带Secure属性所以我修改了后台代码。

func GetPit(c *engine.Context) {
	fmt.Println(c.Req.Cookies())
	cookie := http.Cookie{
		Name:    "user",
		Value:   "wxm530",
		Path:    "/",
        SameSite: http.SameSiteNoneMode,
		Secure: true,
		Expires: time.Now().AddDate(1, 0, 0),
	}
	http.SetCookie(c.Writer, &cookie)
	c.Json(nil)
}

SameSite共有三个可选值,可参考阮一峰大佬的解释
但事情远远没有这么简单重启服务端跑一下

什么,Application中可以看到cookie了domain还不相同???但同样可以注意到这个cookie颜色好像不太一样。

把鼠标放在那个叹号上就能看到,这个cookie它有Secure属性但它不是用安全连接接收的,后来又去查了下Secure属性的作用:标识该cookie只能使用https协议发送。

Cookie 与 Session

我们都知道HTTP是无状态协议那么为了保存会话信息Session技术应运而生

Session是基于Cookie的技术,浏览器第一次发起请求,服务器会为该请求生成一个Session ID 通常为不重复的ID。

基于该ID 封装大量请求信息或服务器本身所需要的信息。可以理解为通过该ID可以定位到一个包含大量请求信息的封装对象。而该对象就是Session。

并且在封装该对象的同时会将生成的 Session ID 添加到Set Cookie 当中。那么根据前文浏览器下次发起请求时将会自动携带该Cookie内容。且改内容即时Session ID。

服务器通过Seesion ID 在内存中找到该Session,就实现了状态保存的功能


大幻梦森罗万象狂气断罪眼\ (•◡•) /

posted @ 2022-03-13 20:29  小艾咪  阅读(224)  评论(0)    收藏  举报