Cookie讲解

  无状态的请求响应式行为,无法关联来自同一客户端的多个请求,当然也无法确定这些请求之间的共享数据。从服务端的角度来说,当用户的Web浏览器打开第一个链接到服务器的套接字时请求就开始了,直到服务器返回最后一个数据包关闭连接时,请求结束。ip地址或许是一个好的想法,但是NAT并不可靠,以大学校园来说,学生们使用着相同的ip,而真实的ip隐藏在NAT路由之后,因此如何辨别请求是否来自同一客户端便有了难题。

  从HTTP1.1开始就出现了cookie技术。

下面为服务端的代码

Cookie cookie = new Cookie("lastAccess",currenttime);
response.addCookie(cookie);

在响应之中添加了Cookie(设置sert-cookie字段),之后会返回给客户端,客户端会把这个Cookie保存在客户端内存之中,以后发送请求就会在请求头中加入这个Cookie来起到识别客户端的作用。

下面简单展示一下。

package com.test;

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(urlPatterns = "/CookieServlet", asyncSupported = true)
public class CookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Cookie[] cookies = req.getCookies();
        Boolean existence = valueInCookie(cookies, "mycookie");
        if(!existence){
            Cookie cookie = new Cookie("mycookie","123");
            resp.addCookie(cookie);
        }
    }

    private Boolean valueInCookie(Cookie[] cookies,String key){
        if(cookies==null){
            return false;
        }
        for(Cookie cookie : cookies){
            String name = cookie.getName();
            String value = cookie.getValue();
            System.out.println("cookie is "+name+"  "+value);
            if(key.equals(name)){
                return true;
            }
        }
        return false;
    }
}

我们看到了请求之中返回了Set-Cookie

再次发送请求我们发现请求头中已经有了Cookie.

以下为Cookie的部分源代码。

public class Cookie implements Cloneable, Serializable {
    private static final long serialVersionUID = -6454587001725327448L;
    private static final String TSPECIALS;
    private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
    private String name;
    private String value;
    private String comment;
    private String domain;
    private int maxAge = -1;
    private String path;
    private boolean secure;
    private int version = 0;
    private boolean isHttpOnly = false;

    public Cookie(String name, String value) {
        if (name != null && name.length() != 0) {
            if (this.isToken(name) && !name.equalsIgnoreCase("Comment") && !name.equalsIgnoreCase("Discard") && !name.equalsIgnoreCase("Domain") && !name.equalsIgnoreCase("Expires") && !name.equalsIgnoreCase("Max-Age") && !name.equalsIgnoreCase("Path") && !name.equalsIgnoreCase("Secure") && !name.equalsIgnoreCase("Version") && !name.startsWith("$")) {
                this.name = name;
                this.value = value;
            } else {
                String errMsg = lStrings.getString("err.cookie_name_is_token");
                Object[] errArgs = new Object[]{name};
                errMsg = MessageFormat.format(errMsg, errArgs);
                throw new IllegalArgumentException(errMsg);
            }
        } else {
            throw new IllegalArgumentException(lStrings.getString("err.cookie_name_blank"));
        }
    }

domain确定了浏览器该将Cookie发送的域名。

path就是进一步的将cookie限制在相对于域的某个URL中。

Expires是过期时间。

Secure保证了cookie只会通过https来发送

httponly就会把cookie限制在直接的浏览器之中,这样js和flash将无法访问到cookie.

domain和path是为了不同的请求发送不同的cookie,不可能一个请求会把全部的cookie都扔过去。

而Secure和httponly是为了防止一些漏洞以免造成不安全的事件。

有一种形式的会话劫持,可以利用js读取会话cookie内容,攻击者可以利用这点来跨站脚本攻击,先把js注入到某个页面,然后document.cookie就能读取到cookie,然后模拟会话,来达到冒充的效果,因此httponly很有必要,它会禁止js,flash或其他插件来获取到cookie,并且cookie只可被用在浏览器创建的http/https请求中。

http请求是会发生中间人攻击的,我们往往使用ssl/tls技术,secure就限制了cookie只能通过https来发送,防止这种情况。

集群中遇到的问题和解决方法。

会话以对象的方式存在于内存中,并且只存在web容器的单个实例中,在具有负载平衡的环境下,同一个客户端的多次不同请求会被发送到不同的服务器,第一个web服务器一切和以前一样,第二个请求中会有cookie但是服务器没有,根据上面的例子就是

  Boolean existence = valueInCookie(cookies, "mycookie");

这一句就不对了。

通常的解决办法是粘滞会话,就是负载平衡累一点,把来自一个客户端的请求都扔到同一个服务器去。然而这样便不能使用https了,这会妨碍到负载平衡器的检测,这不是严重问题,因为现在很多的负载平衡器是支持https的,关键是把加密机制从服务器放到了负载平衡器上,那么负载平衡器要么获取到cookie进行记录,每次查记录去发送请求,要么添加自己的cookie,在后续请求中去识别,能用负载平衡的项目请求数量必定很大,如果在平衡器上就花费这些时间,集群的优势就被减弱了。同步每个tomcat的cookie自然也是很麻烦的,还要考虑请求阻塞不阻塞的问题。什么时候同步的问题,同步时处理不处理请求的问题,有些麻烦

tomcat已经给出了解决方法,还是粘滞会话,但是是有专门的服务器来做了,这个服务器卡在负载平衡器和web服务器之间,叫的是连接器(Apache HTTPD/mod_jk  IIS/isapi_redirect)。连接器用了一个tomcat中被称为会话ID jvmroute的概念来完成粘滞会话。基本的套路就是我配置一个tomcat服务器的标识,在conf/server/xml配置,<Connector>下添加一个jvmroute,然后jvmroute的值会被添加到会话ID的末端,既然cookie和服务器之间的关系比较难维护,我们就直接在cookie标志出所属服务器,服务器数量不会太多,这样就比较好的完成了粘滞会话。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值