From f6f28f4764aaf33d34d5abb348e5827acdd0bfb1 Mon Sep 17 00:00:00 2001 From: tsagano Date: Wed, 16 Apr 2025 11:17:15 +0900 Subject: [PATCH 1/5] =?UTF-8?q?Webview=E3=81=AB=E3=82=BB=E3=83=83=E3=83=88?= =?UTF-8?q?=E3=81=99=E3=82=8Bcookie=E3=81=AE=E5=86=85=E5=AE=B9=E5=A4=89?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AgileWorks/App/RootViewController.swift | 1 + .../WebView/View/WebViewController.swift | 65 ++++++++++++++++++- .../Common/DataStore/KeychainDataStore.swift | 20 ++++++ .../Common/WebClient/GetSessionEndpoint.swift | 21 +++++- 4 files changed, 104 insertions(+), 3 deletions(-) diff --git a/AgileWorks/AgileWorks/App/RootViewController.swift b/AgileWorks/AgileWorks/App/RootViewController.swift index 7710298..e6712b7 100644 --- a/AgileWorks/AgileWorks/App/RootViewController.swift +++ b/AgileWorks/AgileWorks/App/RootViewController.swift @@ -109,6 +109,7 @@ extension RootViewController { KeychainDataStore().removeAccessToken(serverNumber: serverNumber) KeychainDataStore().removeDeviceID(serverNumber: serverNumber) KeychainDataStore().removeSessionID(serverNumber: serverNumber) + KeychainDataStore().removeCookieHeader(serverNumber: serverNumber) } //認証情報削除 KeychainDataStore().removeSystemName() diff --git a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift index edee032..5ff6162 100644 --- a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift +++ b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift @@ -166,8 +166,20 @@ class WebViewController: UIViewController { // WebView ロード処理 private func loadWebView(url: String) { - let sesstionid = "JSESSIONID=" + KeychainDataStore().readSessionID()! - + var requestCookie = "" + if let cookieHeader = KeychainDataStore().readCookieHeader(){ + let individualCookies = self.separateCookieHeader(cookieHeader: cookieHeader) + for cookie in individualCookies { + let componentCookies = cookie.components(separatedBy: ";") + for componentCookie in componentCookies { + if !(componentCookie.contains("JSESSIONID=") || componentCookie.contains("Path=") || componentCookie.contains("Secure") || componentCookie.contains("HttpOnly") || componentCookie.contains("Expires=") || componentCookie.contains("SameSite=") || componentCookie.contains("Domain=")) { + requestCookie += componentCookie + ";" + } + } + } + } + + let sesstionid = "JSESSIONID=" + KeychainDataStore().readSessionID()! + ";" + requestCookie let url = URL(string: url)! var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 30.0) request.httpShouldHandleCookies = false @@ -188,6 +200,22 @@ class WebViewController: UIViewController { self.setWebView() loadWebView(url: url) } + + private func separateCookieHeader(cookieHeader: String) -> [String] { + var individualCookies: [String] = [] + let cookies = cookieHeader.components(separatedBy: ",") + var currentCookie = "" + for cookie in cookies { + currentCookie += cookie + if cookie.contains("Path=") { + individualCookies.append(currentCookie) + currentCookie = "" + } else { + currentCookie += "," + } + } + return individualCookies + } // sessionId を cookie にセット private func setSessionId() { @@ -201,6 +229,39 @@ class WebViewController: UIViewController { ]) let cookieStore = self.mainWebView.configuration.websiteDataStore.httpCookieStore cookieStore.setCookie(cookie!) + + if let cookieHeader = KeychainDataStore().readCookieHeader(){ + let individualCookies = self.separateCookieHeader(cookieHeader: cookieHeader) + for cookie in individualCookies { + var cookiePath = "" + var cookieName = "" + var cookieValue = "" + let components = cookie.components(separatedBy: ";") + for component in components { + if component.contains("Path=") { + let componentValue = component.split(separator: "=") + if componentValue.count == 1 { + cookiePath = String(componentValue[1]) + } + } else if !(component.contains("Secure") || component.contains("HttpOnly") || component.contains("Expires=") || component.contains("SameSite=") || component.contains("Domain=")) { + let componentValue = component.split(separator: "=") + if componentValue.count == 2 { + cookieName = String(componentValue[0]) + cookieValue = String(componentValue[1]) + } + } + } + if cookieName != "JSESSIONID" { + let cookie = HTTPCookie(properties: [ + HTTPCookiePropertyKey.domain: KeychainDataStore().readServerURL() ?? "", + HTTPCookiePropertyKey.path: cookiePath, + HTTPCookiePropertyKey.name: cookieName, + HTTPCookiePropertyKey.value: cookieValue + ]) + cookieStore.setCookie(cookie!) + } + } + } } } diff --git a/AgileWorks/Common/DataStore/KeychainDataStore.swift b/AgileWorks/Common/DataStore/KeychainDataStore.swift index 03de57d..d6de3c2 100644 --- a/AgileWorks/Common/DataStore/KeychainDataStore.swift +++ b/AgileWorks/Common/DataStore/KeychainDataStore.swift @@ -21,6 +21,7 @@ final class KeychainDataStore: DataStoreProtocol { private let kSessionID: String = "SessionID" private let kSystemName: String = "SystemName" private let kCertificateLabel: String = "CertificateLabel" + private let kCookieHeader: String = "CookieHeader" // アクセストークンの書き込み func writeAccessToken(accessToken: String) { @@ -175,6 +176,25 @@ final class KeychainDataStore: DataStoreProtocol { removeKeychainValue(key: kSessionID) } } + + // cookieの書き込み + func writeCookieHeader(cookieHeader: String) { + setKeychainValue(key: kCookieHeader, value: cookieHeader) + } + + // cookieの読み込み + func readCookieHeader() -> String? { + return getKeychainValue(key: kCookieHeader) + } + + // cookieの削除 + func removeCookieHeader(serverNumber: Int? = nil) { + if let serverNumber = serverNumber { + removeKeychainValue(key: kCookieHeader, serverNumber: serverNumber) + } else { + removeKeychainValue(key: kCookieHeader) + } + } // サーバー識別名の書き込み func writeSystemName(systemName: String?) { diff --git a/AgileWorks/Common/WebClient/GetSessionEndpoint.swift b/AgileWorks/Common/WebClient/GetSessionEndpoint.swift index b36a22b..fa65907 100644 --- a/AgileWorks/Common/WebClient/GetSessionEndpoint.swift +++ b/AgileWorks/Common/WebClient/GetSessionEndpoint.swift @@ -32,10 +32,29 @@ extension GetSessionEndpoint { do { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase - return try decoder.decode(SessionInfo.self, from: object as! Data) + // 新規JSESSIONID生成のとき、cookieを保存する + let decodedSessionInfo = try decoder.decode(SessionInfo.self, from: object as! Data) + let bodySessionId = decodedSessionInfo.sessionId + let headers = urlResponse.allHeaderFields[AnyHashable("Set-Cookie")] as! String + let headerSessionId = extractSessionId(from: headers) + if headerSessionId == bodySessionId { + print("CookieHeaders: \(headers)") + KeychainDataStore().writeCookieHeader(cookieHeader: headers) + } + return decodedSessionInfo } catch { log.error(error) throw ResponseError.unexpectedObject(object) } } + func extractSessionId(from cookieString: String) -> String?{ + let components = cookieString.components(separatedBy: ";") + for component in components { + let trimmedComponent = component.trimmingCharacters(in: .whitespaces) + if trimmedComponent.hasPrefix("JSESSIONID="){ + return String(trimmedComponent.dropFirst("JSESSIONID=".count)) + } + } + return nil + } } -- GitLab From 22cf32ff8b815749bff5621e0df62d27c71930dd Mon Sep 17 00:00:00 2001 From: tsagano Date: Wed, 16 Apr 2025 14:34:28 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=E5=88=A4=E5=88=A5=E3=81=99=E3=82=8Bcookie?= =?UTF-8?q?=E3=81=AE=E6=A7=8B=E6=88=90=E8=A6=81=E7=B4=A0=E3=81=AE=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AgileWorks/AgileWorks/WebView/View/WebViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift index 5ff6162..5a4020d 100644 --- a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift +++ b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift @@ -172,7 +172,7 @@ class WebViewController: UIViewController { for cookie in individualCookies { let componentCookies = cookie.components(separatedBy: ";") for componentCookie in componentCookies { - if !(componentCookie.contains("JSESSIONID=") || componentCookie.contains("Path=") || componentCookie.contains("Secure") || componentCookie.contains("HttpOnly") || componentCookie.contains("Expires=") || componentCookie.contains("SameSite=") || componentCookie.contains("Domain=")) { + if !(componentCookie.contains("JSESSIONID=") || componentCookie.contains("Path=") || componentCookie.contains("Secure") || componentCookie.contains("HttpOnly") || componentCookie.contains("Expires=") || componentCookie.contains("Max-Age=") || componentCookie.contains("SameSite=") || componentCookie.contains("Domain=")) { requestCookie += componentCookie + ";" } } @@ -243,7 +243,7 @@ class WebViewController: UIViewController { if componentValue.count == 1 { cookiePath = String(componentValue[1]) } - } else if !(component.contains("Secure") || component.contains("HttpOnly") || component.contains("Expires=") || component.contains("SameSite=") || component.contains("Domain=")) { + } else if !(component.contains("Secure") || component.contains("HttpOnly") || component.contains("Expires=") || component.contains("Max-Age=") || component.contains("SameSite=") || component.contains("Domain=")) { let componentValue = component.split(separator: "=") if componentValue.count == 2 { cookieName = String(componentValue[0]) -- GitLab From fcb61652714f1d302fab1100e636923c396b7007 Mon Sep 17 00:00:00 2001 From: tsagano Date: Wed, 16 Apr 2025 15:13:42 +0900 Subject: [PATCH 3/5] =?UTF-8?q?webview=E3=81=AE=E3=83=AA=E3=82=AF=E3=82=A8?= =?UTF-8?q?=E3=82=B9=E3=83=88cookie=E3=81=AE=E5=88=A4=E5=88=A5=E3=81=A8?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AgileWorks/AgileWorks/WebView/View/WebViewController.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift index 5a4020d..27fbcbf 100644 --- a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift +++ b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift @@ -172,18 +172,16 @@ class WebViewController: UIViewController { for cookie in individualCookies { let componentCookies = cookie.components(separatedBy: ";") for componentCookie in componentCookies { - if !(componentCookie.contains("JSESSIONID=") || componentCookie.contains("Path=") || componentCookie.contains("Secure") || componentCookie.contains("HttpOnly") || componentCookie.contains("Expires=") || componentCookie.contains("Max-Age=") || componentCookie.contains("SameSite=") || componentCookie.contains("Domain=")) { + if !(componentCookie.contains("Path=") || componentCookie.contains("Secure") || componentCookie.contains("HttpOnly") || componentCookie.contains("Expires=") || componentCookie.contains("Max-Age=") || componentCookie.contains("Domain=")) { requestCookie += componentCookie + ";" } } } } - - let sesstionid = "JSESSIONID=" + KeychainDataStore().readSessionID()! + ";" + requestCookie let url = URL(string: url)! var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 30.0) request.httpShouldHandleCookies = false - request.setValue(sesstionid, forHTTPHeaderField: "Cookie") + request.setValue(requestCookie, forHTTPHeaderField: "Cookie") log.debug(""" Session Request: \(String(describing: request.url)) \nHTTPMethod: \(String(describing: request.httpMethod) ) -- GitLab From 8fd55068834e83a04d7c5274736f7365f55361fa Mon Sep 17 00:00:00 2001 From: tsagano Date: Wed, 16 Apr 2025 16:02:29 +0900 Subject: [PATCH 4/5] =?UTF-8?q?updateSession=E3=81=AEcookie=E6=A7=8B?= =?UTF-8?q?=E6=88=90=E8=A6=81=E7=B4=A0=E5=88=A4=E5=88=A5=E3=81=AE=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WebView/View/WebViewController.swift | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift index 27fbcbf..181c6c9 100644 --- a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift +++ b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift @@ -234,6 +234,7 @@ class WebViewController: UIViewController { var cookiePath = "" var cookieName = "" var cookieValue = "" + var samesite: String? = nil let components = cookie.components(separatedBy: ";") for component in components { if component.contains("Path=") { @@ -241,7 +242,16 @@ class WebViewController: UIViewController { if componentValue.count == 1 { cookiePath = String(componentValue[1]) } - } else if !(component.contains("Secure") || component.contains("HttpOnly") || component.contains("Expires=") || component.contains("Max-Age=") || component.contains("SameSite=") || component.contains("Domain=")) { + } else if component.contains("SameSite=") { + let componentValue = component.split(separator: "=") + if componentValue.count == 1 { + if String(componentValue[1]) == "Strict" { + samesite = HTTPCookieStringPolicy.sameSiteStrict.rawValue + } else if String(componentValue[1]) == "Lax" { + samesite = HTTPCookieStringPolicy.sameSiteLax.rawValue + } + } + } else if !(component.contains("Secure") || component.contains("HttpOnly") || component.contains("Expires=") || component.contains("Max-Age=") || component.contains("Domain=")) { let componentValue = component.split(separator: "=") if componentValue.count == 2 { cookieName = String(componentValue[0]) @@ -254,7 +264,8 @@ class WebViewController: UIViewController { HTTPCookiePropertyKey.domain: KeychainDataStore().readServerURL() ?? "", HTTPCookiePropertyKey.path: cookiePath, HTTPCookiePropertyKey.name: cookieName, - HTTPCookiePropertyKey.value: cookieValue + HTTPCookiePropertyKey.value: cookieValue, + HTTPCookiePropertyKey.sameSitePolicy: "" ]) cookieStore.setCookie(cookie!) } -- GitLab From 19bc4e583257161ea3e619c517b934d1c6e8b8aa Mon Sep 17 00:00:00 2001 From: tsagano Date: Wed, 16 Apr 2025 16:47:13 +0900 Subject: [PATCH 5/5] =?UTF-8?q?updateSession=E3=81=AEcookie=E6=A7=8B?= =?UTF-8?q?=E6=88=90=E8=A6=81=E7=B4=A0=E5=88=A4=E5=88=A5=E3=81=AE=E4=BF=AE?= =?UTF-8?q?=E6=AD=A32?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WebView/View/WebViewController.swift | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift index 181c6c9..106cc37 100644 --- a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift +++ b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift @@ -234,7 +234,8 @@ class WebViewController: UIViewController { var cookiePath = "" var cookieName = "" var cookieValue = "" - var samesite: String? = nil + var samesite: String? = "" + var secure = false let components = cookie.components(separatedBy: ";") for component in components { if component.contains("Path=") { @@ -242,6 +243,8 @@ class WebViewController: UIViewController { if componentValue.count == 1 { cookiePath = String(componentValue[1]) } + } else if component.contains("Secure") { + secure = true } else if component.contains("SameSite=") { let componentValue = component.split(separator: "=") if componentValue.count == 1 { @@ -249,9 +252,11 @@ class WebViewController: UIViewController { samesite = HTTPCookieStringPolicy.sameSiteStrict.rawValue } else if String(componentValue[1]) == "Lax" { samesite = HTTPCookieStringPolicy.sameSiteLax.rawValue + } else if String(componentValue[1]) == "None" { + samesite = nil } } - } else if !(component.contains("Secure") || component.contains("HttpOnly") || component.contains("Expires=") || component.contains("Max-Age=") || component.contains("Domain=")) { + } else if !(component.contains("HttpOnly") || component.contains("Expires=") || component.contains("Max-Age=") || component.contains("Domain=")) { let componentValue = component.split(separator: "=") if componentValue.count == 2 { cookieName = String(componentValue[0]) @@ -259,13 +264,19 @@ class WebViewController: UIViewController { } } } + if samesite == nil { + if secure != true { + samesite = "" + } + } if cookieName != "JSESSIONID" { let cookie = HTTPCookie(properties: [ HTTPCookiePropertyKey.domain: KeychainDataStore().readServerURL() ?? "", HTTPCookiePropertyKey.path: cookiePath, + HTTPCookiePropertyKey.secure: secure, HTTPCookiePropertyKey.name: cookieName, HTTPCookiePropertyKey.value: cookieValue, - HTTPCookiePropertyKey.sameSitePolicy: "" + HTTPCookiePropertyKey.sameSitePolicy: samesite ]) cookieStore.setCookie(cookie!) } -- GitLab