クッキーってこんな感じやん
Set-Cookie: AccessToken=a3fWa039adnaknd; Expires=Wed, 11 Oct 2018 07:28:00 GMT; Secure; HttpOnly
で、有効期限ってWed, 11 Oct 2018 07:28:00 GMTみたいに日付で指定されてるんだけど、この日時ってどこの時間をみてると思う?
この日時ってiOSアプリでは端末の時間で見てるみたいなんだよ。
つまりよ、クッキーの有効期限を10分後にしてて、iPhoneの端末時間を10分以上進めてるとするよ。
そうすると....
一瞬でクッキーが溶けていくーーー(´°̥̥̥̥̥̥̥̥ω°̥̥̥̥̥̥̥̥`)
ちなみに、Androidだとサーバーの時間との相対的な日時で見てくれてるみたいで、クッキーは溶けず、問題にならない。
クライアントによって解釈が違うのよね。
PCのブラウザも解釈がそれぞれ違う模様。
ということでiOSでクッキーを受け取ったら、なんとかしてサーバーとの相対時間で有効期限を設定して保持したい。
iOSでクッキーを管理してるのは、HTTPCookieStorage
HTTPCookieStorageを改造してクッキー保存するときに有効期限を相対的な値に書き換える作戦で行ってみる
CustomHTTPCookieStorage
class CustomHTTPCookieStorage : HTTPCookieStorage {
private let source: HTTPCookieStorage
init(source: HTTPCookieStorage) {
self.source = source
}
/*!
@abstract Get all the cookies
@result An NSArray of NSHTTPCookies
*/
override var cookies: [HTTPCookie]? {
get {
print("CustomHTTPCookieStorage.cookies -> \(self.source.cookies?.count.description ?? "nil")")
return self.source.cookies
}
}
/*!
@method setCookie:
@abstract Set a cookie
@discussion The cookie will override an existing cookie with the
same name, domain and path, if any.
*/
override func setCookie(_ cookie: HTTPCookie) {
print("CustomHTTPCookieStorage.setCookie", cookie)
//super.setCookie(cookie)
self.source.setCookie(cookie)
}
/*!
@method deleteCookie:
@abstract Delete the specified cookie
*/
override func deleteCookie(_ cookie: HTTPCookie) {
print("CustomHTTPCookieStorage.deleteCookie", cookie)
//super.deleteCookie(cookie)
self.source.deleteCookie(cookie)
}
/*!
@method removeCookiesSince:
@abstract Delete all cookies from the cookie storage since the provided date.
*/
@available(iOS 8.0, *)
override func removeCookies(since date: Date) {
print("CustomHTTPCookieStorage.removeCookies", date)
//super.removeCookies(since: date)
self.source.removeCookies(since: date)
}
/*!
@method cookiesForURL:
@abstract Returns an array of cookies to send to the given URL.
@param URL The URL for which to get cookies.
@result an NSArray of NSHTTPCookie objects.
@discussion The cookie manager examines the cookies it stores and
includes those which should be sent to the given URL. You can use
<tt>+[NSCookie requestHeaderFieldsWithCookies:]</tt> to turn this array
into a set of header fields to add to a request.
*/
override func cookies(for URL: URL) -> [HTTPCookie]? {
print("CustomHTTPCookieStorage.cookies for URL \(URL) -> \(self.source.cookies?.count.description ?? "nil")")
return self.source.cookies(for: URL)
//return super.cookies(for: URL)
}
/*!
@method setCookies:forURL:mainDocumentURL:
@abstract Adds an array cookies to the cookie store, following the
cookie accept policy.
@param cookies The cookies to set.
@param URL The URL from which the cookies were sent.
@param mainDocumentURL The main document URL to be used as a base for the "same
domain as main document" policy.
@discussion For mainDocumentURL, the caller should pass the URL for
an appropriate main document, if known. For example, when loading
a web page, the URL of the main html document for the top-level
frame should be passed. To save cookies based on a set of response
headers, you can use <tt>+[NSCookie
cookiesWithResponseHeaderFields:forURL:]</tt> on a header field
dictionary and then use this method to store the resulting cookies
in accordance with policy settings.
*/
override func setCookies(_ cookies: [HTTPCookie], for URL: URL?, mainDocumentURL: URL?) {
print("CustomHTTPCookieStorage.setCookies cookies for URL mainDocumentURL", cookies, URL ?? "nil", mainDocumentURL ?? "nil")
//super.setCookies(cookies, for: URL, mainDocumentURL: mainDocumentURL)
self.source.setCookies(cookies, for: URL, mainDocumentURL: mainDocumentURL)
}
/*!
@abstract The cookie accept policy preference of the
receiver.
*/
override var cookieAcceptPolicy: HTTPCookie.AcceptPolicy {
get {
return self.source.cookieAcceptPolicy
}
set {
self.source.cookieAcceptPolicy = newValue
}
}
/*!
@method sortedCookiesUsingDescriptors:
@abstract Returns an array of all cookies in the store, sorted according to the key value and sorting direction of the NSSortDescriptors specified in the parameter.
@param sortOrder an array of NSSortDescriptors which represent the preferred sort order of the resulting array.
@discussion proper sorting of cookies may require extensive string conversion, which can be avoided by allowing the system to perform the sorting. This API is to be preferred over the more generic -[NSHTTPCookieStorage cookies] API, if sorting is going to be performed.
*/
@available(iOS 5.0, *)
override func sortedCookies(using sortOrder: [NSSortDescriptor]) -> [HTTPCookie] {
print("CustomHTTPCookieStorage.sortedCookies", sortOrder)
return self.source.sortedCookies(using: sortOrder)
//return super.sortedCookies(using: sortOrder)
}
}
はい、やってみましたが、
HTTPCookieStorageを差し替えた途端、呼ばれなくなりました笑
次の作戦。
クッキーを受け取ったのを監視して、受け取った瞬間に有効期限を書き換える。
NotificationCenter.default.addObserver(
self, selector: #selector(handleCookieManagerCookiesChanged(_:)),
name: NSNotification.Name.NSHTTPCookieManagerCookiesChanged,
object: nil)
@objc private func handleCookieManagerCookiesChanged (_ notification: Notification) {
print("handleCookieManagerCookiesChanged", notification)
guard let cs: HTTPCookieStorage = notification.object as? HTTPCookieStorage else {
return
}
guard cs === self.manager.session.configuration.httpCookieStorage else {
print("Different httpCookieStorage")
return
}
self.adjustCookieExpires()
}
extension A {
func adjustCookieExpires () {
guard let cs: HTTPCookieStorage = self.manager.session.configuration.httpCookieStorage else {
return
}
adjustCookieExpires(cs: cs)
}
private func adjustCookieExpires (cs: HTTPCookieStorage) {
print("adjustCookieExpires")
for cookie : HTTPCookie in cs.cookies ?? [] {
guard var properties: [HTTPCookiePropertyKey: Any] = cookie.properties else {
print("cookie properties is nil")
continue
}
var modifiled: Bool = false
print("cookie properties = \(properties)")
let cookieComment: String? = properties[HTTPCookiePropertyKey.comment] as? String
print("cookie cookieComment = \(cookieComment ?? "nil")")
if let expires: Date = properties[HTTPCookiePropertyKey.expires] as? Date {
print("cookie expires = \(expires)")
let adjusted: Bool = cookieComment?.contains(cookieExpiresAdjestedComment) ?? false
if !adjusted {
properties[HTTPCookiePropertyKey.expires] = expires.addingTimeInterval(60 * 60 * 24 * 30)
let newComment: String
if let oldComment: String = properties[HTTPCookiePropertyKey.comment] as? String {
newComment = oldComment + " " + cookieExpiresAdjestedComment
} else {
newComment = cookieExpiresAdjestedComment
}
properties[HTTPCookiePropertyKey.comment] = newComment
modifiled = true
}
}
guard modifiled else {
print("cookie not modifiled")
continue
}
guard let newCookie: HTTPCookie = HTTPCookie(properties: properties) else {
print("cookie newCookie is nil")
continue
}
print("newCookie = \(newCookie)")
cs.setCookie(newCookie)
}
}
}
あ、、、動かしてみてから気づく。
HTTPCookieStorageに入った瞬間消えてるやん....
あきらめて、サーバー側でクッキーの有効期限をそもそも伸ばしました。