什么是Cookie
Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。定义于 RFC2109 和 2965 中的都已废弃,最新取代的规范是 RFC6265 [1] 。(可以叫做浏览器缓存)来自百度百科
Cookie的类型
cookie是当前识别用户,实现持久化会话的最好方式。
cookie分为两类:会话cookie和持久cookie。
会话cookie是一种临时cookie,它记录了用户访问站点时的设置和偏好。用户退出浏览器时,会话cookie就被删除了。持久cookie是生存时间更长一些;它们存储在硬盘上,浏览器退出,计算机重启时它们仍然存在。通常会用持久cookie维护某个用户会周期性访问的配置文件或登录名
会话cookie和持久cookie之间唯一的区别就是它们的过期时间
Cookie是如何工作的
cookie就像服务器给用户贴的标签,用户访问一个web站点时,这个web站点就可以读取那个服务器贴在用户身上的所有标签
用户首次访问web站点时,web服务器对用户一无所知(如下图)。web服务器希望这个用户再次回来,所以想给这个用户“拍上”一个独有的cookie,这样服务器以后它就可以识别出这个用户了。cookie中包含了一个由名=值(name=value)这样的信息构成的任意列表,并通过Set-Cookie或Set-Cookie2 http响应(扩展)首部将其贴到用户身上去
cookie中可以包含任意信息,但它们通常都只包含一个服务器为了进行跟踪而产生的独特的识别码。比如上图中,服务器会将一个表示“id=34294”的cookie贴到用户上去。服务器可以用这个数字来查找服务器为期访问者积累的数据库信息(购物历史、地址信息等)
但是cookie并不限于ID号,很多web服务器都会将信息直接保存在cookie中,比如:
cookie: name="Brian Totty"; phone="123123123"
浏览器会记住从服务器返回的Set-Cookie或Set-Cookie2首部中的cookie内容,并将cookie集存储在浏览器的cookie数据库中(把它当做一个贴有不同国家贴纸的旅行箱)。将来用户返回同一站点,浏览器会挑中那个服务器贴到用户上的那些cookie,并在一个cookie的请求首部中将其传回去。
Cookie的组成
现在使用的 cookie 规范有两个不同的版本:cookies 版本 0(有时被称为 Netscape cookies) 和 cookies 版 本 1(RFC 2965) 。 cookies 版 本 1 是 对 cookies 版 本 0 的 扩 展,应用不如后者广泛
cookie版本0,看起来如下
Set-Cookie: name=value [; expires=date] [; path=path] [; domain=domain] [; secure]
Cookie: name1=value1 [; name2=value2] ...
Set-Cookie 首部
Set-Cookie 首部有一个强制性的cookie名和cookie值。后面跟着可选的cookie 属性,中间由分号分隔
cookie版本1的Set-Cookie2首部
版本1的cookie标准比网景告诉标准的可用属性多
版本1的cookie首部
版本1的cookie会带回与传送的每个cookie相关的附加信息,用来描述每个cookie途径的过滤器。每个匹配的cookie都必须包含来自相应Set-Cookie2首部的所有Domain、Port或Path属性
Cookie: $Version="1";
ID="29046"; $Domain=".joes-hardware.com";
color="blue";
Coupon="hammer027"; $Path="/tools";
Coupon="handvac103"; $Path="/tools/cordless"
不同站点使用不同的Cookie
浏览器内部的cookie可以有成百上千个cookie,但浏览器不会将每个cookie都发送给所有的站点。实际上,它们通常只向每个站点发送2-3个cookie。原因如下:
- 对所有这些cookie字节进行传输会严重降低性能。浏览器实际传输的cookie字节数要比实际的内容字节数多
- cookie中包含的是服务器特有的名值对,所以对大部分站点来说,大多数cookie都只是无法识别的无用数据
- 将所有的cookie发送给所有站点会引发潜在的隐私问题,那些你并不信任的站点也会获得你只想发给其他站点的信息
总之,浏览器只会向服务器发送服务器产生的那些cookie。joes-hardware.com产生的cookie会被发送给joes-hardware.com,不会发送给bobs-books.com.
Cookie的作用
主要用在以下三个方面:
- 会话状态管理(如用户登录状态、购物车)
- 个性化设置(如用户自定义设置)
- 浏览器行为跟踪(如跟踪分析用户行为)
iOS中的Cookie
HTTPCookieStorage
NSHTTPCookieStorage implements a singleton object (shared instance) which manages the shared cookie store. It has methods to allow clients to set and remove cookies, and get the current set of cookies. It also has convenience methods to parse and generate cookie-related HTTP header fields.
NSHTTPCookieStorage的实现是一个单例对象,管理着HTTPCookie对象。它有很多方法允许客户端设置、获取和删除cookies。
Sharing Cookie Storage
The persistent cookie storage returned by
shared
may be available to app extensions or other apps, subject to the following guidelines:
- iOS — Each app and app extension has a unique data container, meaning that they have separate cookie stores. You can obtain a common cookie storage by using the
sharedCookieStorage(forGroupContainerIdentifier:)
method.
UIWebView
—UIWebView
instances within an app inherit the parent app's shared cookie storage.
WKWebView
— EachWKWebView
instance has its own cookie storage. See theWKHTTPCookieStore
class for more information.
Session cookies (where the cookie object’s
isSessionOnly
property istrue
) are local to a single process and are not shared.
会话cookie的 sessionOnly方法返回YES就不可在进程间共享
注意
In cases where a cookie storage is shared between processes, changes made to the cookie accept policy affect all currently running apps using the cookie storage.
NSHTTPCookieStorage可以管理cookie的接受策略,在一个app中改变cookie的接受策略将会影响其他正在运行的app的cookie接受策略。
- 获取Cookie Storage对象
class var shared: HTTPCookieStorage //The shared cookie storage instance.
class func sharedCookieStorageGroupContainerIdentifier: String) -> HTTPCookieStorage //Returns the cookie storage instance for the container associated with the specified app group identifier.
- 添加和去除Cookie
func removeCookies(since: Date) //Removes cookies that were stored after a given date.
func deleteCookie(HTTPCookie) //Deletes the specified cookie from the cookie storage.
func setCookie(HTTPCookie) //Stores a specified cookie in the cookie storage if the cookie accept policy permits.
func setCookies([HTTPCookie], for: URL?, mainDocumentURL: URL?) //Adds an array of cookies to the cookie storage if the storage’s cookie acceptance policy permits.
func storeCookies([HTTPCookie], for: URLSessionTask) //Stores an array of cookies in the cookie storage, on behalf of the provided task, if the cookie accept policy permits.
- 获取Cookie
var cookies: [HTTPCookie]? //The cookie storage’s cookies.
func getCookiesFor(URLSessionTask, completionHandler: ([HTTPCookie]?) -> Void) //Fetches cookies relevant to the specified task and passes them to the completion handler.
func cookies(for: URL) -> [HTTPCookie]? //Returns all the cookie storage’s cookies that are sent to a specified URL.
func sortedCookies(using: [NSSortDescriptor]) -> [HTTPCookie] //Returns all of the cookie storage’s cookies, sorted according to a given set of sort descriptors.
- Cookie接受策略
extension HTTPCookie {
/*!
@enum NSHTTPCookieAcceptPolicy
@abstract Values for the different cookie accept policies
@constant NSHTTPCookieAcceptPolicyAlways Accept all cookies
@constant NSHTTPCookieAcceptPolicyNever Reject all cookies
@constant NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain Accept cookies
only from the main document domain
*/
public enum AcceptPolicy : UInt {
case always //默认策略,接受所有的cookies
case never //拒绝所有的cookies
case onlyFromMainDocumentDomain //只接收main document domain中的cookie.
}
}
HTTPCookie
A NSHTTPCookie instance represents a single http cookie. It is an immutable object initialized from a dictionary that contains the various cookie attributes. It has accessors to get the various attributes of a cookie.
NSHTTPCookie对象中包含了HTTP 的cookie对象,从包含cookie字段的字典初始化创建该对象。
- 创建cookies
// Creates an array HTTP cookies corresponding to the provided response header fields for the provided URL.
open class func cookies(withResponseHeaderFields
headerFields: [String : String], for URL: URL) -> [HTTPCookie]
public init?(properties: [HTTPCookiePropertyKey : Any]) // 根据给定的属性值创建ccokie
- 转换cookies到请求头
//Converts an array of cookies to a dictionary of header fields.
open class func requestHeaderFields(with cookies: [HTTPCookie]) -> [String : String]
- 获取cookie的端口属性
var domain: String //The domain of the cookie.
var path: String //The cookie’s path.
var portList: [NSNumber]? //The cookie’s port list.
- 获取cookie元数据
var name: String //The cookie’s name.
var value: String //The cookie‘s string value.
var version: Int //The cookie’s version.
- 确定cookie的声明周期
var expiresDate: Date? //The cookie’s expiration date.
var isSessionOnly: Bool //A Boolean value that indicates whether the cookie should be discarded
// at the end of the session (regardless of expiration date).
- 安全的cookie
var isHTTPOnly: Bool // A Boolean value that indicates whether the cookie should only be sent to HTTP servers.
var isSecure: Bool //A Boolean value that indicates whether this cookie should only be sent over secure channels.
- 获取cookie的属性
var properties: [HTTPCookiePropertyKey : Any]? //The cookie’s properties.
extension HTTPCookiePropertyKey {
/*!
@const NSHTTPCookieName
@discussion Key for cookie name
*/
public static let name: HTTPCookiePropertyKey
/*!
@const NSHTTPCookieValue
@discussion Key for cookie value
*/
public static let value: HTTPCookiePropertyKey
/*!
@const NSHTTPCookieOriginURL
@discussion Key for cookie origin URL
*/
public static let originURL: HTTPCookiePropertyKey
/*!
@const NSHTTPCookieVersion
@discussion Key for cookie version
*/
public static let version: HTTPCookiePropertyKey
/*!
@const NSHTTPCookieDomain
@discussion Key for cookie domain
*/
public static let domain: HTTPCookiePropertyKey
/*!
@const NSHTTPCookiePath
@discussion Key for cookie path
*/
public static let path: HTTPCookiePropertyKey
/*!
@const NSHTTPCookieSecure
@discussion Key for cookie secure flag
*/
public static let secure: HTTPCookiePropertyKey
/*!
@const NSHTTPCookieExpires
@discussion Key for cookie expiration date
*/
public static let expires: HTTPCookiePropertyKey
/*!
@const NSHTTPCookieComment
@discussion Key for cookie comment text
*/
public static let comment: HTTPCookiePropertyKey
/*!
@const NSHTTPCookieCommentURL
@discussion Key for cookie comment URL
*/
public static let commentURL: HTTPCookiePropertyKey
/*!
@const NSHTTPCookieDiscard
@discussion Key for cookie discard (session-only) flag
*/
public static let discard: HTTPCookiePropertyKey
/*!
@const NSHTTPCookieMaximumAge
@discussion Key for cookie maximum age (an alternate way of specifying the expiration)
*/
public static let maximumAge: HTTPCookiePropertyKey
/*!
@const NSHTTPCookiePort
@discussion Key for cookie ports
*/
public static let port: HTTPCookiePropertyKey
}
网络请求Cookie的读取与写入
当访问一个网站时,URLRequest都会帮你主动记录下来你访问的站点设置的Cookie,如果 Cookie 存在的话,会把这些信息放在 NSHTTPCookieStorage 容器中共享,当你下次再访问这个站点时,URLRequest会拿着上次保存下来了的Cookie继续去请求
- 查看返回的cookie
func inquireCookies() {
let cookieStorge = HTTPCookieStorage.shared
printCookies(cookieStorge.cookies ?? [])
// 创建URLRequest并发起请求
let urlRequest = URLRequest(url: URL(string: "https://www.jianshu.com/")!)
URLSession.shared.dataTask(with: urlRequest) { [weak self] (data, response, error) in
guard let response = response as? HTTPURLResponse else { return }
let headerFields = response.allHeaderFields
print("fileds: \(headerFields)")
self?.printCookies(cookieStorge.cookies ?? [])
}.resume()
}
// 打印cookie
func printCookies(_ cookies: [HTTPCookie]) {
for cookie in cookies {
print(cookie)
}
}
- 删除cookie
func deleteCookies() {
let cookieStorge = HTTPCookieStorage.shared
guard let cookies = cookieStorge.cookies else { return }
for cookie in cookies {
cookieStorge.deleteCookie(cookie)
}
}
- 创建cookie
func createCookie() {
// 设置需要的属性
let cookieProperties: [HTTPCookiePropertyKey: Any] = [HTTPCookiePropertyKey.name: "username",
HTTPCookiePropertyKey.value: "value",
HTTPCookiePropertyKey.domain: "baidu.com",
HTTPCookiePropertyKey.path: "/"]
guard let cookie = HTTPCookie(properties: cookieProperties) else { return }
HTTPCookieStorage.shared.setCookie(cookie)
}
- 手动存储Cookie
如果没设置Cookie失效时间expiresDate:(null),sessionOnly:true,kill掉后系统不会自动保存Cookie,如果想持久化Cookie,使用UserDefaults存储即可。相关讨论可以看这里Persisting Cookies In An iOS Application
func saveCookies() {
guard let cookies = HTTPCookieStorage.shared.cookies else { return }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: cookies, requiringSecureCoding: false)
let defaults = UserDefaults.standard
defaults.set(data, forKey: "save-cookie")
}
catch {
print(error)
}
}
func loadSavedCookies() -> [HTTPCookie]? {
guard let cookieData = UserDefaults.standard.object(forKey: "save-cookie") as? Data else { return nil }
do {
guard let cookies = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(cookieData) as? [HTTPCookie]
else { return nil }
return cookies
} catch {
print(error)
}
return nil
}
参考
Httpcookiestorage
Httpcookie