Cookie

一、简介

  1. 某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。
  2. 通过 Cookie 和 Session 技术来实现记录访问者的一些基本信息 。
  3. Cookie 在计算机中是个存储在浏览器目录中的文本文件 。

二、特点

  • 在同一个页面中设置 Cookie,实际上是按从后往前的顺序进行的。如果要先删除一个 Cookie,再写入一个 Cookie,则必须先写写入语句,再写删除语句,否则会出现错误 。
  • Cookie是面向路径的。缺省路径 (path) 属性时,Web 服务器页会自动传递当前路径给浏览器,指定路径强制服务器使用设置的路径。在一个目录页面里设置的 Cookie 在另一个目录的页面里是看不到的 。
  • Cookie 必须在 HTML 文件的内容输出之前设置;不同的浏览器 (Netscape Navigator、Internet Explorer) 对 Cookie 的处理不一致,使用时一定要考虑;客户端用户如果设置禁止 Cookie,则 Cookie 不能建立。 并且在客户端,一个浏览器能创建的 Cookie 数量最多为 300 个,并且每个不能超过 4KB,每个 Web 站点能设置的 Cookie 总数不能超过 20 个 。

三、源码

package javax.servlet.http;

import java.text.MessageFormat;
import java.util.ResourceBundle;

public class Cookie implements Cloneable {
    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 static final String tspecials = ",; ";

    public Cookie(String name, String value) {
        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);
        }
    }

    public void setComment(String purpose) {
        this.comment = purpose;
    }

    public String getComment() {
        return this.comment;
    }

    public void setDomain(String pattern) {
        this.domain = pattern.toLowerCase();
    }

    public String getDomain() {
        return this.domain;
    }

    public void setMaxAge(int expiry) {
        this.maxAge = expiry;
    }

    public int getMaxAge() {
        return this.maxAge;
    }

    public void setPath(String uri) {
        this.path = uri;
    }

    public String getPath() {
        return this.path;
    }

    public void setSecure(boolean flag) {
        this.secure = flag;
    }

    public boolean getSecure() {
        return this.secure;
    }

    public String getName() {
        return this.name;
    }

    public void setValue(String newValue) {
        this.value = newValue;
    }

    public String getValue() {
        return this.value;
    }

    public int getVersion() {
        return this.version;
    }

    public void setVersion(int v) {
        this.version = v;
    }

    private boolean isToken(String value) {
        int len = value.length();

        for(int i = 0; i < len; ++i) {
            char c = value.charAt(i);
            if (c < ' ' || c >= 127 || ",; ".indexOf(c) != -1) {
                return false;
            }
        }

        return true;
    }

    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException var2) {
            throw new RuntimeException(var2.getMessage());
        }
    }
}

四、Cookie的使用

         Cookie cookie = new Cookie("username","password"); // 新建Cookie
        cookie.setMaxAge(Integer.MAX_VALUE); // 设置生命周期为MAX_VALUE
        response.addCookie(cookie); // 输出到客户端

五、修改

只有添加方法,无修改方法,若要修改, 则只能再次生成一次,用后来的数据覆盖前面的数据(前提是key值必须相同)。

六、删除

也没有删除方法, 若要想实现删除Cookie的操作,那么我们需要将Cookie的maxAge参数置为负数,比如说源码中的初始值就是-1,所以说我们也可以参考这个,如果要想将Cookie删除,我们也可以将其maxAge参数置为-1。

七、有效期

maxAge是Cookie的有效期,默认的情况下,Cookie的初始值是-1,其意思就是当浏览器退出时,清除Cookie。如果我们要想让Cookie保存一段时间,比如说是一周,其设置为72460*60(秒)。

八、注意:

修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。

九、域名

  • Cookie是不可跨域名的。
  • 正常情况下,同一个一级域名下的两个二级域名如www.helloweenvsfei.comimages.helloweenvsfei.com也不能交互使用Cookie,因为二者的域名并不严格相同。如果想所有helloweenvsfei.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数为“.helloweenvsfei.com”。

十、校验

根据密码校验

根据Cookie中的用户名在数据库中查找,一旦找到该信息,立即将其取出,然后再与Cookie中的密码进行比较,如果一致,则验证通过。

/**
     * 根据cookie中的密码进行校验
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/password")
    public String password(HttpServletRequest request, HttpServletResponse response){
        Cookie[] cookies = request.getCookies();
        if(cookies.length > 0){
            for(int i = 0;i < cookies.length;i ++){
                Cookie cookie = cookies[i];
                log.info("cookie的key为:{},value为:{}",cookie.getName(),cookie.getValue());
                //验证登录信息
                if(validParamPassword(cookie)){
                    return "success";
                }
            }
        }
        return "error";
    }

    /**
     * 根据cookie中的密码进行校验
     * @param cookie
     * @return
     */
    private Boolean validParamPassword(Cookie cookie){
        if(cookie.getName().equals("zhangsan") && cookie.getValue().equals(MD5Util.encrypt("zhangsan123"))){
            return true;
        }
        if(cookie.getName().equals("lisi") && cookie.getValue().equals(MD5Util.encrypt("lisi123"))){
            return true;
        }
        return false;
    }

根据最后一次Cookie时间进行校验

该方法的思路是每次服务器向用户颁发Cookie时都在数据库中更新一次Cookie颁发时间,这样如果下次访问,服务器可通过Cookie中的用户名在数据库中查出相应的信息,然后再与客户端的颁发时间进行比对,如果颁发时间一致,则验证通过,该方法的另一个优点是避免了用户密码存储在用户本地,因而更加的安全可靠。

/**
     * 根据cookie中的存储时间进行校验
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/cookiedTime")
    public String cookiedTime(HttpServletRequest request, HttpServletResponse response){
        Cookie[] cookies = request.getCookies();
        if(cookies.length > 0){
            for(int i = 0;i < cookies.length;i ++){
                Cookie cookie = cookies[i];
                log.info("cookie的key为:{},value为:{}。",cookie.getName(),cookie.getValue());
                //验证登录信息
                if(validParamTime(cookie)){
                    return "success";
                }
            }
        }
        return "error";
    }

    /**
     * 根据cookie存储时间进行校验
     * @param cookie
     * @return
     */
    private Boolean validParamTime(Cookie cookie){
        Test test = cookieService.selectByName(cookie.getName());
        if(test != null){
            if(String.valueOf(test.getUpdatetime().getTime()).equals(cookie.getValue())){
                return true;
            }
        }
        return false;
    }