关于 Cookie 的那些事儿

关于 Cookie 的那些事儿

带着问题去学习:

  • 什么是 Cookie?
  • Cookie的作用?
  • Cookie 的工作机制?
  • Cookie 的使用场景?
  • 后端处理 Cookie 实战

1、什么是Cookie?

  • 服务端向客服端写入的信息片段,用于保存客户端的状态,为 key-value 键值对的格式。因为 HTTP 是无状态协议,即服务器无法判断客户端的身份(是否为同一浏览器),所以需要 Cookie 来保存客户端的信息。
  • 比如:使用门禁卡进入公司的大门,卡就相当于 Cookie。这里就存在一个问题,门禁卡被别人捡到了,别人就可以冒充你,刷卡进入公司,所以存放在客户端的 Cookie 存在安全隐患,用户的重要信息不能明文放到Cookie中。

一些专业名词:

  • 种 Cookie:,将 Cookie 写到浏览器中,之后每次请求都会带上Cookie
  • 域名:域名由两组或两组以上的ASCII或各国语言字符构成,各组字符间由点号分隔开,最右边的字符组称为顶级域名或一级域名、倒数第二组称为二级域名、倒数第三组称为三级域名。
  • 跨域名:域名不同。

RFC 6265 制定了 HTTP Cookie 和 Set-Cookie 的属性头,HTTP 服务器能够利用 Set-Cookie 的属性头来存储 HTTP 客户端的状态(状态就是Cookie),虽然存在隐私泄露和安全问题,但是Cookie 和 Set-Cookie header 仍然在互联网广泛应用。

种 Cookie 应该遵守的规范:

  • 在多个 Set-Cookie 的字符串中,cookie 的名称不应该重复。
  • 一个 Set-Cookie 只能设置一个 Cookie,当你要想设置多个 cookie,需要添加同样多的set-Cookie字段。

Cookie的属性:

  • name:key-value 键值对
  • expire:失效日期
  • domain:域名,默认值为该网页所在的域名,必须包含两个点。
  • path:路径,默认值为该网页所在路径。domain + path = URL
  • security:设置cookie只在确保安全的请求中才会发送
  • http-only:来设置cookie是否能通过 js 去访问
  • max-age:设置过多久过期

其中 name=value 是必填项,其他是可选项。

Set-Cookie: "name=value;domain=.domain.com;path=/;expires=Sat, 11 Jun 2016 11:29:42 GMT;HttpOnly;secure"

3、Cookie 的工作机制

服务端使用 Set-Cookie header ,浏览器(User agent,客户端)接收到请求后,会根据 Set-Cookie header 的内容设置 Cookie,如下图所示:

  • step 1:浏览器对服务器发起HTTP请求
  • step 2: 服务器在响应中设置了 Set-Cookie header,以让浏览器设置Cookie
  • step3 : 浏览器根据 Set-Cookie header 中的内容设置 Cookie:SID=31d4d96e407aad42
  • step4 : 浏览器下次请求该服务器时会带上 Cookie(Cookie设置的域名要一致时才带上)
    浏览器和服务端交互

4、Cookie 的特点及使用场景

Cookie 的特点:

  • 生命周期:若设置了过期时间,以过期时间为准;若没有设置,则关闭浏览器就失效。
  • 大小:限制为4K
  • 数量限制:20个(部分浏览器不遵循这个规则)
  • 访问相同的域名路径时都会带上Cookie,占用带宽。
  • 作用范围:同一域名下。

一个网页所创建的 cookie 只能被与这个网页在同一目录或子目录下得所有网页访问,而不能被其他目录下得网页访问。

在域名 foo.example.com 可以设置 Domain 为 example.com 或者 foo.example.com,浏览器会拒绝设置 Domain 为 bar.example.com 或者 baz.foo.example.com 的 Cookie。出于安全考虑,浏览器会拒绝设置公共前缀(比如 com 等)的 Cookie。Cookie是不可以跨域名的,隐私安全机制禁止网站非法获取其他网站的 Cookie。

使用场景:

  • 保存用户的信息(购物车,登录信息,搜索记录,自动登录)

5、后端处理Cookie

利用过滤器获取 cookie,保存到会话中 AccountSession 中:随要随取。

/**
 * 账户会话管理
 */
public class AccountManager {
    // ThreadLocal 保证sessions只属于本次会话
	private static ThreadLocal<AccountSession> sessions = new ThreadLocal<AccountSession>();
	
	public static void put(AccountSession session) {
		sessions.set(session);
	}
	
    /* 获取session */
	public static AccountSession get() {
		return sessions.get();
	}
	/* 清空session */
	public static void clear() {
		sessions.remove();
	}
}

服务端设置Cookie

  • 设置新的cookie
    /**
     * 种 cookie
     * @param httpServletRequest  请求
     * @param httpServletResponse 响应
     * @param token               cookie内容
     */
    public void addCookie(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String value, String domain) {
     	// 创建一个cookie
        Cookie cookie = new Cookie("alibaba", value);
        // 设置域名
        cookie.setDomain(domain);
        // 设置路径
        cookie.setPath("/");
        // 设置超时时间,单位秒
        cookie.setMaxAge(10000);
        // 写入 cookie
        httpServletResponse.addCookie(cookie);
    }

Cookie 以键值对的形式放在请求中,服务端中 Cookie 以数组的形式存放在 HttpServletRequest 中:
注意,因为是数组,所以允许 Cookie 重复。

Cookie[] cookies = httpServletRequest.getCookies()

设置Cookie时,获取 Domain 域名或者路径:
方法一:new URL("www.baidu.com").getHost();
方法二:httpServletRequest.getServerName();
方法三:利用正则表达式,获取。

    /* 用正则表达式进行判断 */
    private boolean isIPAddressByRegex(String str) {
        String regex = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}";
        // 判断ip地址是否与正则表达式匹配
        if (str.matches(regex)) {
            String[] arr = str.split("\\.");
            for (int i = 0; i < 4; i++) {
                int temp = Integer.parseInt(arr[i]);
                //如果某个数字不是0到255之间的数 就返回false
                if (temp < 0 || temp > 255) {
                    return false;
                }
            }
            return true;
        } else {
            return false;
        }
    }
    /**
     * 获取cookies可访问的域名,注意第一个字符必须为“.”
     * @param httpServletRequest
     * @return
     */
    private String getCookieDomain(HttpServletRequest httpServletRequest) {
        String domain = httpServletRequest.getServerName();
        if (isIPAddressByRegex(domain)) {
            domain = httpServletRequest.getHeader("x-forwarded-host");
        }
        int pos = domain.indexOf(".");
        if (pos != -1) {
            // 获取第一个点之后的内容
            return domain.substring(pos + 1);
        } else {
            return domain;
        }
    }

注:在客户端(浏览器)中的控制台上,通过命令 document.cookie 可以查询 Cookie 和设置 Cookie

URL重写的技术

既然不同域名 Cookie 种不上,那么如何传递在不同域名之间传递用户Cookie信息呢?

  • 可以将 Cookie 放在地址栏上,从A域名重定向到B域名,这样B域名的服务就能获取到了用户信息。
  • 但这样做不太安全,用户在地址栏上就能拿到 Cookie 信息,当用户把地址分享给其他人的时候,相当于把自己家门的钥匙递给了其他人。

6、参考文献

RFC 6265 : https://datatracker.ietf.org/doc/rfc6265
浅谈Cookie跨域解决方案:https://blog.csdn.net/wangyucui123/article/details/81038840
不同域名下的Cookie共享:https://blog.csdn.net/qq_43392346/article/details/113430252
协议介绍:https://www.cnblogs.com/sunzhenchao/p/3897890.html
聊一聊Cookie:https://segmentfault.com/a/1190000004556040
深入浅出Cookie :https://www.cloudxns.net/Support/detail/id/1887.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值