embeddedBrowserStorage.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /**
  2. * App 端内置浏览器(plus.webview)数据同步:
  3. *
  4. * 1) 登录:writeTokenToEmbeddedBrowser(token)
  5. * 把原始 JWT 写入 https://api.hnyunzhu.com/ 源下的 localStorage(key = "token")。
  6. * localStorage 严格按 origin 隔离,必须在同源 WebView 内 evalJS,故用一个不可见的后台
  7. * webview 加载该 origin 后写入,完成再关闭。仅在「应用设置」选择「用内置浏览器打开链接」
  8. * (getOpenUrlInSystemBrowser() === false)时同步,系统浏览器偏好下写入无意义。
  9. *
  10. * 2) 退出:clearAllEmbeddedBrowserData()
  11. * 不再区分站点——直接清空 WebView 所有 origin 的 localStorage / IndexedDB / SessionStorage /
  12. * WebSQL / Cookies 以及 App 层 HTTP 缓存。通过原生 API(Android: CookieManager + WebStorage +
  13. * WebViewDatabase;iOS: WKWebsiteDataStore)调用,无需加载任何页面,不受偏好 gate。
  14. */
  15. import { getOpenUrlInSystemBrowser } from './openUrlPreference'
  16. const ORIGIN = 'https://api.hnyunzhu.com/'
  17. const TOKEN_KEY = 'token'
  18. const WEBVIEW_ID = 'yz-embedded-storage-writer'
  19. /** 内置 WebView 首次加载 api.hnyunzhu.com 的最长等待时间(弱网兜底) */
  20. const LOADED_TIMEOUT_MS = 4000
  21. /**
  22. * 在隐藏的 api.hnyunzhu.com webview 中执行一段 JS。
  23. * - 同一时刻仅保留一个后台 webview;如已存在则直接复用 + evalJS
  24. * - loaded / titleUpdate 任一事件先到先执行;均未回调时由超时兜底
  25. * @param {string} js 要 evalJS 的脚本
  26. */
  27. function runOnOrigin(js) {
  28. // #ifdef APP-PLUS
  29. try {
  30. if (getOpenUrlInSystemBrowser()) return
  31. if (typeof plus === 'undefined' || !plus.webview) return
  32. const existing = plus.webview.getWebviewById(WEBVIEW_ID)
  33. if (existing) {
  34. try {
  35. existing.evalJS(js)
  36. } catch (e) {}
  37. return
  38. }
  39. const wv = plus.webview.create(ORIGIN, WEBVIEW_ID, {
  40. visible: false,
  41. width: '1px',
  42. height: '1px',
  43. top: '-9999px',
  44. left: '-9999px',
  45. background: 'transparent'
  46. })
  47. let fired = false
  48. const runAndClose = () => {
  49. if (fired) return
  50. fired = true
  51. try {
  52. wv.evalJS(js)
  53. } catch (e) {}
  54. // 给 evalJS 一点时间落盘再关闭,避免异步写入被打断
  55. setTimeout(() => {
  56. try {
  57. wv.close('none')
  58. } catch (e) {}
  59. }, 300)
  60. }
  61. try {
  62. wv.addEventListener('loaded', runAndClose, false)
  63. } catch (e) {}
  64. // 部分机型 loaded 触发偏晚;titleUpdate 早于 loaded,兜底一次
  65. try {
  66. wv.addEventListener('titleUpdate', runAndClose, false)
  67. } catch (e) {}
  68. // 弱网 / 无响应兜底:到点照样尝试 evalJS
  69. setTimeout(runAndClose, LOADED_TIMEOUT_MS)
  70. } catch (e) {}
  71. // #endif
  72. }
  73. /**
  74. * 登录成功后把原始 JWT 写入 api.hnyunzhu.com 源下的 localStorage(key 为 "token")
  75. * @param {string} token 原始 JWT
  76. */
  77. export function writeTokenToEmbeddedBrowser(token) {
  78. const safe = JSON.stringify(String(token || ''))
  79. const key = JSON.stringify(TOKEN_KEY)
  80. runOnOrigin(`try{localStorage.setItem(${key}, ${safe})}catch(e){}`)
  81. }
  82. // #ifdef APP-PLUS
  83. /**
  84. * Android:CookieManager + WebStorage + WebViewDatabase 全清
  85. * - CookieManager.removeAllCookies(ValueCallback) 为 API 21+;旧机降级 removeAllCookie()
  86. * - WebStorage.deleteAllData() 清所有 origin 的 LocalStorage / IndexedDB / WebSQL / AppCache
  87. * - WebViewDatabase 清表单 & HTTP Basic 凭证
  88. */
  89. function clearAllAndroidWebViewData() {
  90. try {
  91. const CookieManager = plus.android.importClass('android.webkit.CookieManager')
  92. const cm = CookieManager.getInstance()
  93. try {
  94. cm.removeAllCookies(null)
  95. } catch (e) {
  96. try {
  97. cm.removeAllCookie()
  98. } catch (e2) {}
  99. }
  100. try {
  101. cm.removeSessionCookies(null)
  102. } catch (e) {
  103. try {
  104. cm.removeSessionCookie()
  105. } catch (e2) {}
  106. }
  107. try {
  108. cm.flush()
  109. } catch (e) {}
  110. } catch (e) {}
  111. try {
  112. const WebStorage = plus.android.importClass('android.webkit.WebStorage')
  113. WebStorage.getInstance().deleteAllData()
  114. } catch (e) {}
  115. try {
  116. const WebViewDatabase = plus.android.importClass('android.webkit.WebViewDatabase')
  117. const main = plus.android.runtimeMainActivity()
  118. const db = WebViewDatabase.getInstance(main)
  119. try {
  120. db.clearFormData()
  121. } catch (e) {}
  122. try {
  123. db.clearHttpAuthUsernamePassword()
  124. } catch (e) {}
  125. } catch (e) {}
  126. }
  127. /**
  128. * iOS:WKWebsiteDataStore 一次清除所有类型数据(localStorage/IndexedDB/Cookies/Cache 等)
  129. * selector 自 iOS 9 起稳定:removeDataOfTypes:modifiedSince:completionHandler:
  130. */
  131. function clearAlliOSWebViewData() {
  132. try {
  133. const WKWebsiteDataStore = plus.ios.importClass('WKWebsiteDataStore')
  134. const NSDate = plus.ios.importClass('NSDate')
  135. const types = plus.ios.invoke(WKWebsiteDataStore, 'allWebsiteDataTypes')
  136. const store = plus.ios.invoke(WKWebsiteDataStore, 'defaultDataStore')
  137. const since = plus.ios.invoke(NSDate, 'distantPast')
  138. plus.ios.invoke(
  139. store,
  140. 'removeDataOfTypes:modifiedSince:completionHandler:',
  141. types,
  142. since,
  143. null
  144. )
  145. } catch (e) {}
  146. }
  147. // #endif
  148. /**
  149. * 退出登录时:清空 WebView 所有站点的缓存与存储(LocalStorage/IndexedDB/SessionStorage/WebSQL/Cookies),
  150. * 外加 App 层 HTTP 缓存。不受「内置浏览器」偏好 gate,只要 App 端退出就清。
  151. */
  152. export function clearAllEmbeddedBrowserData() {
  153. // #ifdef APP-PLUS
  154. try {
  155. if (typeof plus === 'undefined') return
  156. let platform = ''
  157. try {
  158. platform = uni.getSystemInfoSync().platform
  159. } catch (e) {}
  160. if (platform === 'android') {
  161. clearAllAndroidWebViewData()
  162. } else if (platform === 'ios') {
  163. clearAlliOSWebViewData()
  164. }
  165. try {
  166. if (plus.cache && typeof plus.cache.clear === 'function') {
  167. plus.cache.clear()
  168. }
  169. } catch (e) {}
  170. } catch (e) {}
  171. // #endif
  172. }