深入理解Session与Cookie

    Session 与 Cookie 的作用都是为了保持访问用户与后端服务器的交互状态

1 理解cookie

    cookie的作用通俗地说就是当一个用户通过HTTP访问一个服务器时,这个服务器会将一些key/value键值对返回给客户端浏览器,并给这些数据加上一些限制条件,在条件符合时这个用户下次访问
这个服务器时,数据又被完整地带会给服务器。

    这个作用就像你去超市购物时,第一次会给你办张购物卡,在这个购物卡里存放了你的个人信息,下次你再来这个超市的时候,超市会识别你的购物卡,下次直接购物就好了。

    由于是同一个客户端发出的请求,每次发出的请求都会带有第一次访问时服务端设置的信息,这样服务端就可以根据Cookie值来划分访问的用户了。

1.1 Cookie属性项

  • NAME=VALUE : 键值对,可以设置要保存的Key/Value,注意这里的NAME不能和其它属性项的名字一样
  • Expires: 过期时间,在设置的某个时间点后该Cookie就会失效,如expires=Wednesday,09-Nov-99 23:12:40 GMT
  • Domain : 生成该Cookie的域名,如 domain="jd.com""
  • Path : 该Cookie是在当前那个路径下生成的,如path=/wp-admin/
  • Secure : 如果设置了这个属性,那么只会在SSH连接时才会回传该Cookie
  • Comment: 注释项,用户说明该Cookie有何用途
  • CommentURI: 服务器为此Cookie提供提供的URI注释
  • Discard: 是否在会话结束后丢弃该Cookie项,默认为false
  • Max-Age :最大失效时间,与Expires不同的是这里设置的是在多少秒后失效
  • Port: 该Cookie在什么端口下可以回传服务器,如果有多个端口,则以逗号隔开,如Port="80,81,8080"

    在实际应用中,在响应头中这样设置 :Set-Cookie:userName="junshan";Version="1";Domain=".wkq.pub";Max-Age=1000。

1.2 Cookie如何工作

当我们用如下方式创建Cookie时:


@RestController
@ApiIgnore
public class HelloController {
    String getCookie(Cookie[] cookies, String key) {
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(key)) {
                    return cookie.getValue();
                }
            }
        }
        return null;
    }

    @GetMapping("/get")
    public void doGet(HttpServletRequest req, HttpServletResponse res) {
        Cookie[] cookies = req.getCookies();
        String userName = getCookie(cookies, "userName");
        String userAge = getCookie(cookies, "userAge");
        if (userName == null) {
            res.addCookie(new Cookie("userName", "马瑾妍"));
        }
        if (userAge == null) {
            res.addCookie(new Cookie("userAge", "21"));
        }
        res.getHeaders("Set-Cookie");
    }
}

    Cookie是如何加到HTTP的Header中的呢?下图是Tomcat创建Set-Cookie响应头的时序图。


img.png

    从图中可以看出,真正构件Cookie是在org.apache.catalina.connector.Response类中完成的,调用generateCookieString方法将Cookie对象构造成一个
字符串,构造的字符串的格式如userName="wkq";Version="1";Domain=".jd.com";Max-Age=1000。然后将这个字符串命名为Set-Cookie添加到MimeHeaders中。

注意:

  1. 所创建Cookie的NAME不能和Set-Cookie或者Set-Cookie2的属性值一样,如果一样会抛出IllegalArgumentException。
  2. 所创建Cookie的NAME和VALUE的值不能设置成非ASCII字符,如果要使用中文,可以通过URLEncoder将其编码,否则会抛出IllegalArgumentException异常。
  3. 当NAME和VALUE的值出现一些TOKEN字符(如""、","等)时,构建返回头会将该Cookie的Version自动设置为1。
  4. 当在该Cookie的属性项中出现Version1的属性项时,构建HTTP响应头同样会将Version设置为1。

    当我们通过response.addCookie创建多个Cookie时,这些Cookie最终是以独立的Header项存在的,通俗的说就是每次创建Cookie时都是以创建一个以NAME为Set-Cookie的MimeHeaders
,每次调用addCookie时,最终都会创建一个Header
    找到Tomcat最终构造Http响应头的代码,这段代码位于org.apache.coyote.http11.Http11Processor类中的prepareResponse方法中,如下所示

keepAliveTimeout=headers.size();
for(int i=0;i<keepAliveTimeout; ++i){
    this.outputBuffer.sendHeader(headers.getName(i),headers.getValue(i));
}

    在构建HTTP返回字节流时是将Header中的所有的项顺序地写出,而没有进行任何修改。所以可以想象浏览器在接受HTTP返回的数据时是分别解析每一个Header项的。

    当我们请求某个URL路径时,浏览器会根据这个URL路径将符合条件的Cookie放在Request请求头中回传给服务端,服务端通过request.getCookies来取得所有的Cookie。

1.3 使用Cookie的限制

    Cookie是HTTP头里的一个字段,虽然HTTP本身对这个字段并没有多少限制,但是Cookie最终还是存储在浏览器里,所以不同的浏览器对Cookie的存储都有一些限制,比如在Chrome浏览器中每个域名下的Cookie数量不能超过50个,总大小不能大于80000

2 理解Session

    Cookie可以让服务器端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果Cookie很多,则无形地增加了客户端与服务器端的数据传数量,而Session的出现正是为了解决这个问题。

    同一个客户端每次和服务器交互时,不需要每次都传回所有的Cookie值,而是只要传回一个ID,这个ID是客户端第一次访问服务器时生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的ID,客户端只要传回这个ID就行了,这个ID通常是NAME为JSESSIONID的一个Cookie

2.1 Session与Cookie

    下面详细讲解了Session是如何基于Cookie来工作的。实际上有以下三种方式可以放Session正常工作。

  • 基于URL Path Parameter ,默认支持
  • 基于Cookie,如果没有修改Context容器的Cookie标识,则默认也是支持的。
  • 基于SSL,默认不支持,只有connector.getAttribute("SSLEnabled")为TRUE时才支持。

    在第一种情况下,当浏览器不支持Cookie功能时,浏览器会将用户的SessionCookieName重写到用户请求的URL参数中,它的传递格式如
/path/Servlet;name=value;name2=value2?Name3=value3,其中 “Servlet;”后面的K-V就是要传递的Path Parameters,服务器会从这个Path
Parameters中拿到用户配置的SessionCookieName。关于这个SessionCookieName,就是大家熟知的"JSESSONID".
与Session关联的Cookie与其它Cookie并没什么不同。接着Request根据这个SessionCookieName到Parameters中拿到SessionID并设置到request.setRequestedSessionId中。
    如果客户端支持Cookie,则Tomcat人会解析Cookie中的SessionId,并会覆盖URL中的SessionID
    如果是第三种情况,则会根据javax,servlet.request.ssl_session属性值设置Session ID。

2.2 Session 如何工作

    有了SessionID,服务端就可以创建HttpSession对象了,第一次触发通过request.getSession()
方法。如果当前的SessionID还没有对应的HttpSession对象,那么就创建一个新的,并将这个对象添加到
org.apache.catalina.Manager的Sessions容器中保存。Manager类将管理所有Session的生命周期,Session过期将被回收,服务器关闭,Session将被序列化到磁盘等,只要这个HttpSession
对象存在,用户就可以根据Session ID来获取这个对象,也就做到了对状态的保持。
一个SessionID 对应一个访问的客户端,所以一个HttpSession对象也就一个访问的客户端

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容