【问题标题】:How to parse a cookie string如何解析 cookie 字符串
【发布时间】:2011-07-31 06:41:51
【问题描述】:

我想获取一个 Cookie 字符串(因为它可能在 Set-Cookie 标头中返回)并且能够轻松修改其中的部分内容,特别是到期日期。

我看到有几个不同的 Cookie 类可用,例如 BasicClientCookie,但我没有看到任何简单的方法可以将字符串解析为其中一个对象。

我在 api 级别 9 中看到他们添加了 HttpCookie,它有一个 parse 方法,但我需要在以前的版本中工作。

有什么想法吗?

谢谢

【问题讨论】:

  • 您使用的是什么 HTTP 客户端? Android 附带 Apache Commons HttpClient。
  • 我正在使用 HttpClient 进行连接,但我一直在使用 CookieSyncManager 来保存 cookie。目前我没有使用 HttpClient 的 CookieStore。

标签: java android string cookies


【解决方案1】:

java.net.HttpCookie怎么样:

List<HttpCookie> cookies = HttpCookie.parse(header);

【讨论】:

  • 这就是 OP 所说的直到 api 9 才可用。
  • @MattWolfe 正确,但是在 2009 年发现这个问题的人中有 90% 不再担心 API 9。这就是为什么这个答案现在被赞成的原因。
  • 失败,例如:SSID PHPSESSID=d6cb3d4a5e6fd3bbe5078f37d616fac1; path=/, ci_session=a%3A0%3A%7B%7D; expires=Mon, 10-Nov-2014 12:37:06 GMT; Max-Age=-31500000; path=/, ci_session=a%3A5%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22ca963cd5e9b6d9631c5afc8f26eb7374%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A14%3A%22182.59.202.107%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A60%3A%22Dalvik%2F2.1.0+%28Linux%3B+U%3B+Android+5.1.1%3B+Nexus+5+Build%2FLMY48B%29%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1447123026%3Bs%3A9%3A%22user_data%22%3Bs
  • 此方法无法解析正确的cookies...不要使用!
  • @User3:您发布的 cookie 违反了RFC 2109 §4.1,它读取 token(非正式地)——一系列非特殊、非空白字符,所以@ 987654326@ 不应包含空格。 @atasoyh:很高兴看到一个具体的例子......
【解决方案2】:

我相信您必须手动解析它。试试这个:

BasicClientCookie parseRawCookie(String rawCookie) throws Exception {
    String[] rawCookieParams = rawCookie.split(";");

    String[] rawCookieNameAndValue = rawCookieParams[0].split("=");
    if (rawCookieNameAndValue.length != 2) {
        throw new Exception("Invalid cookie: missing name and value.");
    }

    String cookieName = rawCookieNameAndValue[0].trim();
    String cookieValue = rawCookieNameAndValue[1].trim();
    BasicClientCookie cookie = new BasicClientCookie(cookieName, cookieValue);
    for (int i = 1; i < rawCookieParams.length; i++) {
        String rawCookieParamNameAndValue[] = rawCookieParams[i].trim().split("=");

        String paramName = rawCookieParamNameAndValue[0].trim();

        if (paramName.equalsIgnoreCase("secure")) {
            cookie.setSecure(true);
        } else {
            if (rawCookieParamNameAndValue.length != 2) {
                throw new Exception("Invalid cookie: attribute not a flag or missing value.");
            }

            String paramValue = rawCookieParamNameAndValue[1].trim();

            if (paramName.equalsIgnoreCase("expires")) {
                Date expiryDate = DateFormat.getDateTimeInstance(DateFormat.FULL)
                        .parse(paramValue);
                cookie.setExpiryDate(expiryDate);
            } else if (paramName.equalsIgnoreCase("max-age")) {
                long maxAge = Long.parseLong(paramValue);
                Date expiryDate = new Date(System.getCurrentTimeMillis() + maxAge);
                cookie.setExpiryDate(expiryDate);
            } else if (paramName.equalsIgnoreCase("domain")) {
                cookie.setDomain(paramValue);
            } else if (paramName.equalsIgnoreCase("path")) {
                cookie.setPath(paramValue);
            } else if (paramName.equalsIgnoreCase("comment")) {
                cookie.setPath(paramValue);
            } else {
                throw new Exception("Invalid cookie: invalid attribute name.");
            }
        }
    }

    return cookie;
}

我还没有真正编译或运行这段代码,但它应该是一个很好的开始。您可能不得不稍微弄乱日期解析:我不确定 cookie 中使用的日期格式实际上是否与 DateFormat.FULL 相同。 (查看this 相关问题,该问题涉及处理cookie 中的日期格式。)另外,请注意BasicClientCookie 未处理一些cookie 属性,例如versionhttponly

最后,这段代码假定 cookie 的名称和值显示为第一个属性:我不确定这是否一定是真的,但这就是我见过的每个 cookie 的排序方式。

【讨论】:

  • if (paramName.equalsIgnoreCase("comment")) 之后,cookie.setPath(paramValue) 可能是错误的?
  • 最好按照@yanchenko 的建议去做,并使用本地库中的内容。使用CookieSpec 超类之一,例如BestMatchSpec
  • 如果 cookie 值中包含“=”字符,则会中断
  • 如何获取包含“=”字符的cookie值?
  • 有人有解决方案来处理带有“=”的cookie吗?
【解决方案3】:

您可以为此使用 Apache HttpClient 的工具。
这是CookieJar的摘录:

CookieSpec cookieSpec = new BrowserCompatSpec();

List<Cookie> parseCookies(URI uri, List<String> cookieHeaders) {
    ArrayList<Cookie> cookies = new ArrayList<Cookie>();
    int port = (uri.getPort() < 0) ? 80 : uri.getPort();
    boolean secure = "https".equals(uri.getScheme());
    CookieOrigin origin = new CookieOrigin(uri.getHost(), port,
            uri.getPath(), secure);
    for (String cookieHeader : cookieHeaders) {
        BasicHeader header = new BasicHeader(SM.SET_COOKIE, cookieHeader);
        try {
            cookies.addAll(cookieSpec.parse(header, origin));
        } catch (MalformedCookieException e) {
            L.d(e);
        }
    }
    return cookies;
}

【讨论】:

    【解决方案4】:

    很有趣,但是 java.net.HttpCookie 类无法解析带有域和/或路径部分的 cookie 字符串,而这个确切的 java.net.HttpCookie 类已转换为字符串。

    例如:

    HttpCookie newCookie = new HttpCookie("cookieName", "cookieValue");
    newCookie.setDomain("cookieDomain.com");
    newCookie.setPath("/");
    

    由于这个类既不实现 Serializable 也不实现 Parcelable,因此将 cookie 存储为字符串是很诱人的。所以你写这样的东西:

    saveMyCookieAsString(newCookie.toString());
    

    此语句将 cookie 保存为以下格式:

    cookieName="cookieValue";$Path="/";$Domain="cookiedomain.com"
    

    然后你想恢复这个cookie,所以你得到了字符串:

    String cookieAsString = restoreMyCookieString();
    

    并尝试解析它:

    List<HttpCookie> cookiesList = HttpCookie.parse(cookieAsString);
    StringBuilder myCookieAsStringNow = new StringBuilder();
    for(HttpCookie httpCookie: cookiesList) {
        myCookieAsStringNow.append(httpCookie.toString());
    }
    

    现在myCookieAsStringNow.toString(); 产生

    cookieName=cookieValue
    

    域和路径部分刚刚消失了。原因:parse 方法对“domain”和“path”之类的词区分大小写。
    可能的解决方法:提供另一个 toString() 方法,例如:

    public static String httpCookieToString(HttpCookie httpCookie) {
        StringBuilder result = new StringBuilder()
                .append(httpCookie.getName())
                .append("=")
                .append("\"")
                .append(httpCookie.getValue())
                .append("\"");
    
        if(!TextUtils.isEmpty(httpCookie.getDomain())) {
            result.append("; domain=")
                    .append(httpCookie.getDomain());
        }
        if(!TextUtils.isEmpty(httpCookie.getPath())){
            result.append("; path=")
                    .append(httpCookie.getPath());
        }
    
        return result.toString();
    }
    

    我觉得它很有趣(尤其是像 java.net.HttpCookie 这样的目标是被很多人使用的类),我希望它对某人有用。

    【讨论】:

    • 似乎 HttpCookie 将 cookie 解析为服务器 cookie,但将它们字符串化为用户代理 cookie - 也许这就是 its JavaDoc 的意图:在服务器和用户之间携带状态信息代理。另请参阅examples section of RFC 2109
    【解决方案5】:

    使用正则表达式:

    ([^=]+)=([^\;]+);\s?
    

    您可以像这样解析 cookie:

    .COOKIEAUTH=5DEF0BF530F749AD46F652BDF31C372526A42FEB9D40162167CB39C4D43FC8AF1C4B6DF0C24ECB1945DFF7952C70FDA1E4AF12C1803F9D089E78348C4B41802279897807F85905D6B6D2D42896BA2A267E9F564814631B4B31EE41A483C886B14B5A1E76FD264FB230E87877CB9A4A2A7BDB0B0101BC2C1AF3A029CC54EE4FBC; 
    expires=Sat, 30-Jul-2011 01:22:34 GMT; 
    path=/; HttpOnly
    

    只需几行代码。

    【讨论】:

      【解决方案6】:

      如果你碰巧安装了 Netty HTTP 编解码器,你也可以使用io.netty.handler.codec.http.cookie.ServerCookieDecoder.LAX|STRICT。很方便。

      【讨论】:

        【解决方案7】:

        Yanchenko 使用 Apache Http 客户端的方法的优势在于,它可以根据来源验证与规范一致的 cookie。正则表达式方法不会这样做,但也许您不需要。

        public class CookieUtil {
        
            public List<Cookie> parseCookieString(String cookies) {
                List<Cookie> cookieList = new ArrayList<Cookie>();
                Pattern cookiePattern = Pattern.compile("([^=]+)=([^\\;]*);?\\s?");
                Matcher matcher = cookiePattern.matcher(cookies);
                while (matcher.find()) {
                    int groupCount = matcher.groupCount();
                    System.out.println("matched: " + matcher.group(0));
                    for (int groupIndex = 0; groupIndex <= groupCount; ++groupIndex) {
                        System.out.println("group[" + groupIndex + "]=" + matcher.group(groupIndex));
                    }
                    String cookieKey = matcher.group(1);
                    String cookieValue = matcher.group(2);
                    Cookie cookie = new BasicClientCookie(cookieKey, cookieValue);
                    cookieList.add(cookie);
                }
                return cookieList;
            }
        }
        

        我附上了一个使用 yanchenkos 正则表达式的小例子。它需要稍微调整一下。没有'?尾随 ';' 上的数量修饰符不会匹配任何 cookie 的尾随属性。之后,如果您关心其他属性,您可以使用 Doug 的代码(正确封装)来解析其他匹配组。

        编辑:另外,请注意 cookie 本身值的“*”限定符。值是可选的,您可以获得像“de=”这样的 cookie,即根本没有值。再次查看正则表达式,我认为它不会处理没有“=”的安全和丢弃 cookie 属性。

        【讨论】:

          【解决方案8】:
          CookieManager cookieManager = new CookieManager();
                  CookieHandler.setDefault(cookieManager);
                  HttpCookie cookie = new HttpCookie("lang", "en");
                  cookie.setDomain("Your URL");
                  cookie.setPath("/");
                  cookie.setVersion(0);
          
                  cookieManager.getCookieStore().add(new URI("https://Your URL/"), cookie);
                  List<HttpCookie> Cookies =  cookieManager.getCookieStore().get(new URI("https://Your URL/"));
                  String s = Cookies.get(0).getValue();
          

          【讨论】:

            【解决方案9】:
                    val headers = ..........
            
            
                    val headerBuilder = Headers.Builder()
            
                    headers?.forEach {
                        val values = it.split(";")
                        values.forEach { v ->
                            if (v.contains("=")) {
                                headerBuilder.add(v.replace("=", ":"))
                            }
                        }
                    }
            
                    val headers = headerBuilder.build()
            

            【讨论】:

              猜你喜欢
              • 2020-09-04
              • 2015-06-13
              • 2021-06-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-09-24
              相关资源
              最近更新 更多