From 27a05210eeeb5592396326c38099e4461672c727 Mon Sep 17 00:00:00 2001 From: Zuncle <34310384@qq.com> Date: Thu, 26 Mar 2026 14:35:26 +0800 Subject: [PATCH] =?UTF-8?q?fix(auth):=20=E4=BF=AE=E5=A4=8D=E6=B4=BB?= =?UTF-8?q?=E5=8A=A8=E9=A1=B5=E5=92=8C=E5=95=86=E5=93=81=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E9=A1=B5=E6=9C=AA=E7=99=BB=E5=BD=95=E5=8D=B3=E5=BC=B9=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=A1=86=E5=AF=BC=E8=87=B4=E5=AE=A1=E6=A0=B8=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题背景: - 平台审核结论:页面未完整浏览、体验详情时即要求授权登录,属于不合规 - 用户应能先浏览页面内容,仅在执行操作(抽奖/兑换/购买)时才引导登录 根因分析: 1. api/appUser.js 中活动浏览类 API(getActivityDetail 等)使用 authRequest, 虽然后端接口是公开的,但同页面的 getGamePasses 等需认证接口返回 401 触发全局登录弹窗 2. getProductDetail 使用 authRequest 调用认证接口,未登录直接 401 3. 全局 401 拦截器不区分浏览请求和操作请求 修改内容: 1. api/appUser.js: 6 个浏览类 API 函数从 authRequest 改为 request - getActivityDetail, getActivityIssues, getActivityIssueRewards - getIssueDrawLogs, getMatchingCardTypes, getProductDetail 这些接口在后端均为公开路由,不需要携带 token 2. 活动页面 onLoad 中条件调用认证接口: - wuxianshang/index.vue: fetchPasses() 仅在已登录时调用 - yifanshang/index.vue: fetchPasses() 仅在已登录时调用 - duiduipeng/index.vue: fetchGamePasses() 仅在已登录时调用 次数卡(game passes)接口需要认证,未登录时跳过即可, 不影响页面浏览体验 3. utils/request.js: request() 函数增加 suppressAuthModal 参数 支持调用方按需静默 401 弹窗,作为安全兜底机制 验证场景: - 未登录 → 打开无限赏/一番赏/对对碰/商品详情 → 正常显示,无登录弹窗 - 未登录 → 点击抽奖/兑换按钮 → 弹出登录提示(符合平台规范) - 已登录 → 所有功能正常,次数卡信息正常加载 --- api/appUser.js | 12 ++++++------ pages-activity/activity/duiduipeng/index.vue | 6 ++++-- pages-activity/activity/wuxianshang/index.vue | 4 +++- pages-activity/activity/yifanshang/index.vue | 4 +++- utils/request.js | 4 ++-- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/api/appUser.js b/api/appUser.js index a68d21c..43f8d45 100755 --- a/api/appUser.js +++ b/api/appUser.js @@ -117,19 +117,19 @@ export function setDefaultAddress(user_id, address_id) { } export function getActivityDetail(activity_id) { - return authRequest({ url: `/api/app/activities/${activity_id}`, method: 'GET' }) + return request({ url: `/api/app/activities/${activity_id}`, method: 'GET' }) } export function getActivityIssues(activity_id) { - return authRequest({ url: `/api/app/activities/${activity_id}/issues`, method: 'GET' }) + return request({ url: `/api/app/activities/${activity_id}/issues`, method: 'GET' }) } export function getActivityIssueRewards(activity_id, issue_id) { - return authRequest({ url: `/api/app/activities/${activity_id}/issues/${issue_id}/rewards`, method: 'GET' }) + return request({ url: `/api/app/activities/${activity_id}/issues/${issue_id}/rewards`, method: 'GET' }) } export function getIssueDrawLogs(activity_id, issue_id) { - return authRequest({ url: `/api/app/activities/${activity_id}/issues/${issue_id}/draw_logs`, method: 'GET' }) + return request({ url: `/api/app/activities/${activity_id}/issues/${issue_id}/draw_logs`, method: 'GET' }) } export function drawActivityIssue(activity_id, issue_id) { @@ -141,7 +141,7 @@ export function getIssueChoices(activity_id, issue_id) { } export function getProductDetail(product_id) { - return authRequest({ url: `/api/app/products/${product_id}`, method: 'GET' }) + return request({ url: `/api/app/products/${product_id}`, method: 'GET' }) } export function redeemInventory(user_id, ids) { @@ -346,7 +346,7 @@ export function playMatchingGame(game_id) { * 获取所有启用的卡牌配置 */ export function getMatchingCardTypes() { - return authRequest({ url: '/api/app/matching/card_types', method: 'GET' }) + return request({ url: '/api/app/matching/card_types', method: 'GET' }) } export function createMatchingPreorder({ issue_id, position, coupon_id = 0, item_card_id = 0, use_game_pass = false }) { diff --git a/pages-activity/activity/duiduipeng/index.vue b/pages-activity/activity/duiduipeng/index.vue index 313259d..673e776 100755 --- a/pages-activity/activity/duiduipeng/index.vue +++ b/pages-activity/activity/duiduipeng/index.vue @@ -1589,8 +1589,10 @@ onLoad((opts) => { syncResumeGame(id) fetchDetail(id) fetchIssues(id) - // 获取次数卡 - fetchGamePasses() + // 获取次数卡(仅登录用户) + if (uni.getStorageSync('token')) { + fetchGamePasses() + } } fetchCardTypes() }) diff --git a/pages-activity/activity/wuxianshang/index.vue b/pages-activity/activity/wuxianshang/index.vue index 7648578..95f9827 100755 --- a/pages-activity/activity/wuxianshang/index.vue +++ b/pages-activity/activity/wuxianshang/index.vue @@ -476,7 +476,9 @@ onLoad(async (opts) => { if (currentIssueId.value) { fetchWinRecords(id, currentIssueId.value) } - fetchPasses() + if (uni.getStorageSync('token')) { + fetchPasses() + } }) // 监听期切换,刷新记录 diff --git a/pages-activity/activity/yifanshang/index.vue b/pages-activity/activity/yifanshang/index.vue index ae77436..3140c49 100755 --- a/pages-activity/activity/yifanshang/index.vue +++ b/pages-activity/activity/yifanshang/index.vue @@ -774,7 +774,9 @@ onLoad(async (opts) => { fetchWinRecords(id, currentIssueId.value) } // 获取次数卡 - fetchPasses() + if (uni.getStorageSync('token')) { + fetchPasses() + } }) onUnload(() => { diff --git a/utils/request.js b/utils/request.js index 55efad8..1fa68e4 100755 --- a/utils/request.js +++ b/utils/request.js @@ -23,7 +23,7 @@ function handleAuthExpired() { // 不再脱敏,直接打印原始数据 -export function request({ url, method = 'GET', data = {}, header = {} }) { +export function request({ url, method = 'GET', data = {}, header = {}, suppressAuthModal = false }) { return new Promise((resolve, reject) => { const finalHeader = { ...buildDefaultHeaders(), ...header } uni.request({ @@ -39,7 +39,7 @@ export function request({ url, method = 'GET', data = {}, header = {} }) { resolve(body && body.data !== undefined ? body.data : body) } else { if (code === 401) { - const suppress = finalHeader && (finalHeader['X-Suppress-Auth-Modal'] || finalHeader['x-suppress-auth-modal']) + const suppress = suppressAuthModal || finalHeader && (finalHeader['X-Suppress-Auth-Modal'] || finalHeader['x-suppress-auth-modal']) if (!suppress) { handleAuthExpired() }