Cookie和Session

本文深入解析HTTP协议中Cookie和Session的原理与应用,探讨其在客户端和服务端的使用方法,以及跨域访问解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Cookie

一、什么是Cookie,Cookie的弊端

解释:Cookie是一段小文本,伴随着用户请求和页面在服务器和客户端之间传递。用户每次访问某个页面的时候,都会带上含有日期和时间的Cookie。而Cookie是由服务器端生成,通过服务器返回的响应头中的Set-Cookie来设置Cookie,并保存在客户端上的一种会话跟踪技术。不同浏览器,不同用户,不同网页,之间的cookie互不影响。

 

为什么有cookie:因为http协议是无状态的,一旦请求数据交换完成,连接就会断开;为了保证下一次请求能跟踪上一次的会话,就比如执行某个操作后,再次执行,服务器并不能确定是哪个用户执行,所以需要引入一种机制来保证。

 

cookie的流程:

1.客户端发出http请求

2.服务器返回带有Set-cookie的响应

3.客户端发出http请求+cookie(path和domain会影响)

4.服务器响应

 

查看网站的cookie:

地址栏输入javascript:alert(document.cookie)

 

弊端:

1、cookie在部分浏览器下的数量限制(每个domain),以及大小限制

2、存在安全隐患

3、用户可以禁用

4、有些状态不可能保存在客户端

5、一般不可跨域

6、每次请求页面都需会发送cookie,无形增加流量

 

二、Cookie的后端使用

java代码:

Cookie cookie = new Cookie("name", "testcookie");
cookie.setMaxAge(1000); //有效时间
cookie.setDomain("wzy.com"); //有效域
cookie.setPath("/"); //有效路径

resp.addCookie(cookie); //设置响应头添加cookie

注意:name和value都不能包含 [ ] ( ) = , " / ? @ : ; 这几个特殊字符,中文需要使用java.net.URLEncoder.encoder(“中文”,”utf-8”); 加密处理,即不可以包含中文。

 

浏览器端(Response Headers):

Content-Length: 14
Date: Wed, 26 Sep 2018 02:31:19 GMT
Server: Apache-Coyote/1.1
Set-Cookie: name=testcookie; Domain=wzy.com; Expires=Wed, 26-Sep-2018 02:47:59 GMT; Path=/

 

javax.servlet.http.Cookie 核心方法: 

//设置cookie的注释
public String getComment();
public void setComment(String purpose);

//设置cookie的作用域
/**
 * cookie是不可跨域名的,比如同时访问www.baidu.com和www.jd.com,访问百度的时候是不会带上
 * 京东的cookie。
 * 即使有类似www.baidu.com 和 test.baidu.com ,因为域名不是完全吻合,也不会带上对方的
 * cookie。
 * 如果想一级域名baidu.com下的两个二级域名www.baidu.com 和 test.baidu.com 都能使用cookie,
 * 则需要设置domain。
 */
public void setDomain(String pattern);
public String getDomain();


//设置二级域名能够访问cookie
Cookie cookie = new Cookie("test","hello world");
cookie.setDomain(".baidu.com"); //设置域名


//设置cookie的最大生命周期(单位:秒),默认为-1,浏览器关闭则失效,这种会话一般保存在内存。
//如果为0,则删除该cookie;大于0,则保存到本地硬盘
public void setMaxAge(int expiry);
public int getMaxAge();

//设置 cookie 适用的路径。如果不指定路径,与当前页面相同目录下的(包括子目录下的)所有 URL 都会返回 cookie。
//比如 setPath("/aweb/abc"), 则客户端请求非/aweb/abc路径的url,都不会卸载该cookie
public void setPath(String uri);
public String getPath();

//设置cookie是否只在加密(SSL)的连接上发送
public void setSecure(boolean flag);
public boolean getSecure();

//设置cookie的name
public String getName();
public void setValue(String newValue);

//设置cookie的value
public String getValue();
public void setValue(String newValue);

 

 

三、Cookie的前端使用

//获取cookie
function getCookie(name){
    if(name == null || name == "")
        return "";
    var start = -1;
    var end = -1;
    if(document.cookie.length > 0){
        start = document.cookie.indexOf(name + "=");
        if(start != -1){
            start += name.length;
            start += 1;
            end = document.cookie.indexOf(";",start); //从start开始检索第一个";"
            if(end == -1)
                end = document.cookie.length;
            return unescape(document.cookie.substring(start,end));
        }
    }
    return "";
}

//设置cookie,expireTime 单位毫秒
function addCookie(name,value,expireTime){
    if(name != null && name.length > 0){
        var date = new Date();
        date.setTime(date.setTime() + expireTime);
        document.cookie = name + "=" + escape(value) + (expireTime == null ? "": ";expires=" + date.toGMTString());
    }
}

//删除cookie,只需要设置生命周期为过去时间就可以
function delCookie(name){
    if(name != null && name.length > 0){
        var date = new Date();
        date.setTime(date.setTime() - 100000);
        //key,value 不允许出现逗号,分号,空白字符
        document.cookie = name + "=test;expires="+date.toGMTString();
    }
}

 

四、Cookie使用的限制和注意之处

cookie在不同浏览器,每个域下的cookie的数量是不一致的,有的为20,50或者不限制,如果超过该浏览器支持的数量,则会覆盖最开始的cookie,总大小一般为4k或者更多;

所以为了可扩展性,尽量保证cookie的数量不大于20(每个域),总大小不大于4095字节。

 

五、一级域名cookie跨域访问方案

比较常见的有如下几种方式:

1、JSONP

这种方式局限性比较大,只能使用GET方式,譬如其他POST,DELTE等都不能使用。

参考:https://blog.youkuaiyun.com/nainaiqiuwencun/article/details/82907210

需要设置:crossDomain:true

$.ajax({
    url:'',
    type:'get',
    dataType:'jsonp',
    crossDomain:true,
    success:function(resp){}
});

 

2、CORS

这种方式需要服务器配置,但是简单并且有错误机制,是w3c草案,对浏览器的具有一定的要求。

参考:https://blog.youkuaiyun.com/nainaiqiuwencun/article/details/83003168

需要设置:withCredentials为true

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;


//ajax
$ajax({
    url:'',
    type:'',
    xhrFields : {
        withCredentials : true
    }
});

服务器设置:

	    response2.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
	    response2.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
	    response2.setHeader("Access-Control-Allow-Headers", "Content-Type");
	    response2.setHeader("Access-Control-Allow-Credentials", "true");
	    response2.setHeader("Access-Control-Max-Age", "10");

设置Access-Control-Allow-Credentials 为true,并且Access-Control-Allow-Origins必须为具体的域名,而不能为*。

 

Session

 

一、什么是Session

因为http是无状态协议,所以每一次操作并不知道上一次具体做了什么;因此为了解决这个问题,出现了cookie技术和session技术。而session笼统来讲就是一个会话,从你第一次请求到最后一次请求,服务器会为你开辟一个独立的空间来保存你整个访问过程中需要保留的数据。

比如在web应用中,你第一次发起请求,服务器会去判断是否存在session,如果不存在则会产生一个session,并把该session对应的id在响应头中通过Set-cookie要求客户端保存下;然后在后面的每一次请求中都会要求携带上,服务器通过sessionid来寻找为该会话开辟的空间,操作对应的数据。

所以,session是在服务器端,在交互过程中依赖cookie;即使cookie被人为禁用,我们也可以使用URL重写技术等。

 

二、Session的使用

首先查看下javaEE提供的javax.servlet.http.HttpSession接口:

public interface HttpSession {

    /**
     * 获取创建的时间
     */
    public long getCreationTime();
    
    /**
     * 获取唯一标识id
     */
    public String getId();
       
    /**
     * 获取客户端最后一次请求的时间
     */
    public long getLastAccessedTime();
     
    /**
    * 获取此session所属的ServletContext
    */
    public ServletContext getServletContext();

    /**
     * 设置当前会话的有效时间,单位:s
     * 0 或者负数表示永不过期
     */
    public void setMaxInactiveInterval(int interval);

    public int getMaxInactiveInterval();
    
    /**
     * 在这个会话里,根据name获取value
     *
     */  
    public Object getAttribute(String name);
    
    public Enumeration getAttributeNames();
    
    /**
     * 设置name,value;如果name,重复,则替换value
     */
    public void setAttribute(String name, Object value);

    /**
     * 删除数据
     */
    public void removeAttribute(String name);

    /**
     * 让此session失效,解绑
     */
    public void invalidate();
    
    /**
     * 判断该session是否是新的
     */
    public boolean isNew();



}

所以在具体的web应用中,使用非常简单。

 当用户第一次请求一个jsp(在tomcat容器中会编译成servlet)或者servlet的时候,容器会先去判断是否存在,不存在则创建,存在则更新最后一次访问时间。如果第一次创建,则会在响应头中添加Set-Cookie:JSESSIONID=XXXXXX;让浏览器保存该cookie,同时在后面的请求中都带上该cookie。

在firefox中发起get请求:http://localhost:8080/mybatis/home?name=8080

eclipse中debug看获取的session:

可以看出isNew=true,该session是第一次创建,有唯一标识id,以及session存储数据的接口是ConcurrentHashMap,以键值对的形式存储在服务器内存里。

浏览器中查看响应头:

第二次再次请求,服务器查看:

id等都不变,只有访问时间,以及isNew改变,说明在该次请求中,并未创建新的session。

再查看客户端请求:

可以看到,这次请求中带上了jsessionid。

 

httpsession的操作

HttpServletRequest request = null;
HttpSession session = request.getSession();

//session可以存取任何类型的数据,但不要滥用,尽量存储小数据,以防止服务器内存资源被占用
session.setAttribute("user",new User("wzy","test"));
session.setAttribute("isRead",true);

//获取的时候,需要做转化
User user = (User) session.getAttribute("user");

//删除某个属性
session.removeAttribute("isRead");

 

三、Session的弊端和缺陷

1、因为session是直接存储在服务器中的,如果过期时间设置的不合理或者session中保存的属性过多或者大量的请求等都会对服务器造成巨大的压力。

2、session劫持,比较严重的安全问题。

客户端只要通过一个具体的jsessionid,就可以在任何条件下,通过http伪造请求,因为服务器只知道session的id,但是并不知道该会话具体是在哪个客户端上。

比如:我在chrome浏览器上进行一次http请求:http://localhost:8080/mybatis/home?name=8080

获取到的jsessionid为:

然后打开firefox,在浏览器中 编辑请求:http://localhost:8080/mybatis/home?name=8080-firefox

并添加jsessionid=A6743BFFB03C14D39AB271B4CAAA86A6,发起请求,查看后台获取的session

 

服务器判断这两个请求都属于同一个会话;这里我们就可以通过劫持jsessionid,来伪造http请求,形成安全攻击。 

3、多台服务器会产生session同步问题,同步问题详见:

 

四、禁用Cookie

当客户端禁用cookie的时候,这时候每一次请求都会产生一个新的session,因为下一次请求都不会携带任何cookie信息,包括jsessionid,我们可以通过URL重写技术(HttpServletResponse.encodeURL("url"),最终请求服务器的时候,url会携带上jsessionid:http://ip:port/web/path;jesssionid=xxxxxxxx?name=1'),来继续使用session技术。

URL重写技术详见:

 

五、Session有效期

客户端在默认情况下,一旦退出浏览器,该jsessionid就失效,不会保存在客户端上;服务器上的session并不会客户端关闭,就立即失效。

在tomcat中,默认失效时间是30分钟,即最后一次请求后没有任何其他动作,则30分钟后该session失效,或者主动调用session.invalidate()方法。

有效期配置:

1、tomcat配置

    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>

tomcat目录下,conf/web.xml中修改,默认30分钟

 

2、工程设置

在web.xml中添加:

    <session-config>
		<session-timeout>30</session-timeout>
	</session-config>

单位:分钟 

在springboot中配置:

server.servlet.session.timeout = 1800

单位:秒 

  

3、java代码设置

session.setMaxInactiveInterval(1800);

单位:秒

这几种优先级:3 > 2 > 1

 

六、Session跨域的限制

详见cookie跨域。

 

Cookie和Session的比较

CompareCookieSessionComment
数据存放位置保存在客户端,以string格式保存在服务器,以object形式 
安全性不是很安全,可以分析本地的cookie,进行cookie欺骗相对安全,但是也存在session劫持

由于session中的数据保存在服务器,一定程度上,安全性比较高

限制保存的个数和大小有限制保存的个数和大小没有限制 
性能因为分布在不同的客户端,不会出现资源占用问题可能会出现资源占用问题

session保存在服务器,如果请求过大,session

失效时间设置不合理,会占用很大的服务器资源

时效性会失效会失效

一般情况下session会随着容器销毁一起失效,

但是cookie可以保存到客户端

所以, 一般对安全性要求较高的保存在session中,比如登录信息等;其他不重要的信息可以保存在cookie中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值