From c5d8db2647724029dab5a1a3cfb9eb779a5ac366 Mon Sep 17 00:00:00 2001 From: hardy <2545433047@qq.com> Date: Tue, 15 Sep 2026 15:49:29 +0800 Subject: [PATCH 1/4] feat(TAP-11883): send auth via Bearer instead of URL tokens Stop appending access_token to REST, download, WS, and SSO URLs. Use Authorization Bearer, blob downloads, PDK icon object URLs, SAML login_code exchange, and keepalive fetch on data-preview unload. --- apps/daas/src/main.ts | 16 +++++- apps/daas/src/plugins/axios.ts | 55 ++++++++++++------- apps/daas/src/views/api-page/ApiServer.vue | 8 +-- apps/daas/src/views/login/SsoCallback.vue | 41 +++++++++++--- packages/api/src/JavascriptFunctions.ts | 9 ++- packages/api/src/Modules.js | 16 +++--- packages/api/src/PythonFunctions.ts | 9 ++- packages/api/src/Task.ts | 9 ++- packages/api/src/Users.ts | 4 +- packages/api/src/core/connections.ts | 7 ++- packages/api/src/core/function.ts | 5 +- packages/api/src/core/javascript-functions.ts | 5 +- packages/api/src/core/metadata-instances.ts | 7 +-- packages/api/src/core/modules.ts | 10 ++-- packages/api/src/core/python-functions.ts | 5 +- packages/api/src/core/sso.ts | 11 +++- packages/api/src/core/task.ts | 7 ++- packages/api/src/core/users.ts | 6 +- packages/api/src/download.ts | 12 ++++ packages/api/src/index.ts | 1 + .../business/src/components/logs/Download.vue | 9 +-- .../business/src/views/connections/util.js | 52 ++++++++++++------ .../business/src/views/data-server/Debug.vue | 9 ++- .../src/views/data-server/template.ts | 26 +++++---- packages/dag/src/composables/useFormScope.ts | 10 ++-- packages/dag/src/data-capture/DataCapture.vue | 18 ++++-- packages/dag/src/mixins/formScope.js | 15 +++-- 27 files changed, 242 insertions(+), 140 deletions(-) create mode 100644 packages/api/src/download.ts diff --git a/apps/daas/src/main.ts b/apps/daas/src/main.ts index 66f17344f5..068235e39d 100644 --- a/apps/daas/src/main.ts +++ b/apps/daas/src/main.ts @@ -71,6 +71,17 @@ const URL_LANG = getUrlSearch('lang') if (TOKEN) { Cookie.set('access_token', TOKEN) + const [hashPath, hashQuery] = location.hash.split('?') + if (hashQuery) { + const params = new URLSearchParams(hashQuery) + params.delete('token') + const nextHash = params.toString() ? `${hashPath}?${params}` : hashPath + history.replaceState( + null, + '', + `${location.pathname}${location.search}${nextHash}`, + ) + } } const token = Cookie.get('access_token') @@ -100,9 +111,8 @@ const init = () => { app.config.globalProperties.$ws = new WSClient(wsUrl, undefined, { getQuery: () => { - return { - access_token: Cookie.get('access_token'), - } + // Same-origin WS sends the access_token cookie; do not put the token in the URL (TAP-11883). + return {} }, }) diff --git a/apps/daas/src/plugins/axios.ts b/apps/daas/src/plugins/axios.ts index 3dc5484346..3afb7a320a 100644 --- a/apps/daas/src/plugins/axios.ts +++ b/apps/daas/src/plugins/axios.ts @@ -19,6 +19,7 @@ import { signOut } from '../utils/util' type AxiosRequestConfigPro = AxiosRequestConfig & { silenceMessage?: boolean + skipAuthExpire?: boolean } // Pending requests map for cancellation @@ -60,6 +61,9 @@ const errorCallback = (error: any): Promise => { switch (rsp.status) { // 用户无权限访问接口 case 401: { + if ((error.config as AxiosRequestConfigPro)?.skipAuthExpire) { + break + } const isSingleSession = getSettingByKey('login.single.session', 'open') signOut() @@ -114,6 +118,35 @@ const errorCallback = (error: any): Promise => { return Promise.reject(error) } +function stripUrlAccessToken(url?: string) { + if (!url) { + return url + } + let next = url.replaceAll(/([?&])access_token=[^&]*/gi, '$1') + next = next.replaceAll('?&', '?').replaceAll('&&', '&') + if (next.endsWith('?') || next.endsWith('&')) { + next = next.slice(0, -1) + } + return next +} + +function applyBearerAuth(config: InternalAxiosRequestConfig) { + const accessToken = Cookie.get('access_token') + if (accessToken) { + config.headers = config.headers || {} + if (!config.headers.Authorization) { + config.headers.Authorization = `Bearer ${accessToken}` + } + } + if (config.url) { + config.url = stripUrlAccessToken(config.url) + } + if (config.params && typeof config.params === 'object') { + delete (config.params as Record).access_token + } + return config +} + axios.interceptors.request.use(function ( config: InternalAxiosRequestConfig, ): InternalAxiosRequestConfig { @@ -123,16 +156,7 @@ axios.interceptors.request.use(function ( encoder: (str) => window.encodeURIComponent(str), }) } - const accessToken = Cookie.get('access_token') - if (accessToken && config.url) { - if (~config.url.indexOf('?')) { - if (!~config.url.indexOf('access_token')) { - config.url = `${config.url}&access_token=${accessToken}` - } - } else { - config.url = `${config.url}?access_token=${accessToken}` - } - } + applyBearerAuth(config) if (config.headers) { config.headers['x-requested-with'] = 'XMLHttpRequest' } @@ -170,16 +194,7 @@ export function initRequestClient() { requestClient.addRequestInterceptor({ synchronous: true, fulfilled: (config) => { - const accessToken = Cookie.get('access_token') - if (accessToken) { - if (config.url?.indexOf?.('?') !== -1) { - if (config.url?.indexOf?.('access_token') === -1) { - config.url = `${config.url}&access_token=${accessToken}` - } - } else { - config.url = `${config.url}?access_token=${accessToken}` - } - } + applyBearerAuth(config) if (isPassiveScope()) { config.headers = config.headers || {} config.headers['X-User-Activity'] = '0' diff --git a/apps/daas/src/views/api-page/ApiServer.vue b/apps/daas/src/views/api-page/ApiServer.vue index 6767c24936..507018808d 100644 --- a/apps/daas/src/views/api-page/ApiServer.vue +++ b/apps/daas/src/views/api-page/ApiServer.vue @@ -9,8 +9,8 @@ import { import PageContainer from '@tap/business/src/components/PageContainer.vue' import TablePage from '@tap/business/src/components/TablePage.vue' import { useHas } from '@tap/business/src/composables' +import { downloadAuthenticated } from '@tap/api/src/download' import i18n from '@tap/i18n' -import Cookie from '@tap/shared/src/cookie' import { ElMessage, ElMessageBox } from 'element-plus' import { escapeRegExp } from 'lodash-es' import { h, nextTick, reactive, ref } from 'vue' @@ -113,11 +113,7 @@ const remove = (item: Record) => { // 下载api配置文件 const downloadConfig = (item: Record) => { - const token = Cookie.get('access_token') - window.open( - `${API_SERVER_BASE_URL}/download/${item.id}?access_token=${token}`, - '_blank', - ) + downloadAuthenticated(`${API_SERVER_BASE_URL}/download/${item.id}`) } // 保存 diff --git a/apps/daas/src/views/login/SsoCallback.vue b/apps/daas/src/views/login/SsoCallback.vue index 84bdd82d6f..29372d46e6 100644 --- a/apps/daas/src/views/login/SsoCallback.vue +++ b/apps/daas/src/views/login/SsoCallback.vue @@ -1,5 +1,6 @@