Commit 3cece355 by jianan

feat(工作台入口): openWorkbench 票桥接力,向 csf 换票零自证(referrer-portal-api D11)

- sso.ts 新增 issueWorkbenchTicket:以内存 Casdoor token 向工作台同源 /csf/api 换一次性票
  (csf Result 形态解析,跨域由 csf allowed-origins 白名单放行)
- openWorkbench:有票落 /auth/ticket 建会话;无 token/换票失败静默回落原直连(PKCE 兜底)
parent bcaa3557
......@@ -180,6 +180,7 @@
import JoinPopup from '@/components/commonPopup/JoinPopup.vue';
import PartnerTipPopup from "@/components/commonPopup/PartnerTipPopup.vue";
import { initJssdkShare, setWechatShare } from '@/util/fiveshare';
import { getSsoToken, issueWorkbenchTicket } from "@/util/sso";
import restrictedTip from '@/components/commonPopup/restrictedTip.vue';
import wx from 'weixin-js-sdk'
export default {
......@@ -769,17 +770,30 @@
'我的',
'pages/personalCenter/personalCenter'
)
// D11 票桥接力:已持 Casdoor token(经 cffp 票桥免登进来)时,先向工作台后端
// (与 workbench_url 同源的 /csf/api)换一次性票据,落 /auth/ticket 建会话——
// 全程不再经过 Casdoor 登录页;无 token 或换票失败则回落直连(工作台自身 OIDC 登录照旧)。
const navigate = (target) => {
// #ifdef APP-PLUS
// APP 内复用通用 web-view 整屏页
uni.navigateTo({
url: '/components/webview?url=' + encodeURIComponent(url)
url: '/components/webview?url=' + encodeURIComponent(target)
});
// #endif
// #ifndef APP-PLUS
// H5(含微信小程序 web-view 内、普通浏览器):replace 顶层跳转、不压历史栈,
// 避免微信内核脏历史快照恢复后「我的」页菜单入口丢失
window.location.replace(url);
window.location.replace(target);
// #endif
}
if (getSsoToken()) {
const csfApiBase = url.replace(/\/+$/, '') + '/csf/api'
issueWorkbenchTicket(csfApiBase)
.then((ticket) => navigate(url + 'auth/ticket?ticket=' + encodeURIComponent(ticket) + '&redirect=' + encodeURIComponent('/')))
.catch(() => navigate(url))
} else {
navigate(url)
}
},
ckidentity(item) {
if (item.identity == true && this.customerBasicInfo.partnerType == null) {
......
/**
* Casdoor 统一身份·CFFP/SFP H5 侧(design D3):
* - /pages/auth/ticket 落地页用一次性票据换 Casdoor JWT,仅存内存(不落 localStorage)
* - sso 模式标志与业务 userId 存持久 storage(不含令牌、非敏感):web-view 被系统回收后
* 重载业务页时,内存令牌已丢而登录态「应在」,据此交原生重走票据,绝不静默降级 obtainToken
* - 401/T001:销毁 web-view,由原生 reLaunch 重走 wx.login→票据闭环
* 普通浏览器直连票据 URL 也可消费;但无 wx.miniProgram 可回时降级跳 H5 登录页。
*/
import wx from 'weixin-js-sdk'
import { apiURL } from '@/environments/environment'
import dataHandling from '@/util/dataHandling'
const MODE_KEY = 'cffp_sso_mode'
let casdoorToken: string | null = null
let relogining = false
/**
* 建立 SSO 会话:令牌只进内存;非敏感登录态(模式标志、isLogin、业务 userId)
* 落持久 storage——web-view 被系统回收重载后内存令牌丢失,据此识别「登录态应在」,
* 拦掉无令牌请求并交原生重走票据,绝不静默降级 obtainToken 游客链路。
* loginType 写 'sso':作为第三种登录方式(codelogin/visitor/sso),让全局登录态
* UI 与「已登录才拉 queryInfo」门禁天然识别;code 登录专属链路仍显式比对 codelogin。
*/
export const setSsoToken = (token: string, userId: number | string) => {
casdoorToken = token
uni.setStorageSync(MODE_KEY, '1')
uni.setStorageSync('isLogin', '1')
uni.setStorageSync('loginType', 'sso')
uni.setStorageSync('cffp_userId', userId)
}
export const getSsoToken = (): string | null => casdoorToken
/**
* 票桥接力(referrer-portal-api design D11):用内存中的 Casdoor token 向工作台后端
* (csf-api,与 workbench_url 同源)换一次性票据,使「CFFP → 工作台」免 Casdoor 登录页。
* 跨域携带 Authorization,服务端按 referrer.ticket.allowed-origins 白名单放行本 H5 来源。
* 失败仅 reject 不跳转——调用方降级为直连工作台(照旧走工作台自身 OIDC 登录)。
* 注意响应是 csf 的 Result 形态 {code:200,msg,data:{ticket}}(区别于 cffp 票桥的 success 字段)。
*/
export const issueWorkbenchTicket = (csfApiBase: string): Promise<string> =>
new Promise((resolve, reject) => {
if (!casdoorToken) {
reject(new Error('无 Casdoor 令牌,无法签发工作台票据'))
return
}
uni.request({
url: `${csfApiBase}/c/sso/ticket/issue`,
method: 'POST',
data: {},
header: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${casdoorToken}`
},
success: (res: any) => {
const data = res.data && res.data.code === 200 ? res.data.data : null
const ticket = data && data.ticket
ticket ? resolve(ticket) : reject(new Error('工作台票据签发失败: ' + (res.data && res.data.msg)))
},
fail: () => reject(new Error('工作台票据签发网络失败'))
})
})
export const isSsoMode = (): boolean => uni.getStorageSync(MODE_KEY) === '1'
export const clearSso = () => {
casdoorToken = null
uni.removeStorageSync(MODE_KEY)
uni.removeStorageSync('isLogin')
uni.removeStorageSync('cffp_userId')
// 回到 H5 老链路的游客语义
uni.setStorageSync('loginType', 'visitor')
}
export const isMiniprogram = (): boolean => {
// #ifdef H5
return dataHandling.h5RuntimeEnv() === 'wechat-miniprogram'
// #endif
// #ifndef H5
return false
// #endif
}
/** 一次性票据换 Casdoor 令牌与业务 userId(匿名接口,唯一凭据是票据本身) */
export const consumeTicket = (ticket: string): Promise<{token: string, userId: number | string}> =>
new Promise((resolve, reject) => {
uni.request({
url: `${apiURL}/sso/ticket/consume?ticket=${encodeURIComponent(ticket)}`,
method: 'GET',
success: (res: any) => {
const data = res.data && res.data.success ? res.data.data : null
data && data.token && data.userId != null
? resolve({ token: data.token, userId: data.userId })
: reject(new Error('票据无效、已过期或已使用'))
},
fail: () => reject(new Error('票据消费网络失败'))
})
})
/** 当前应用内路径(剥 history base),供原生重走票据后精确回跳 */
export const currentAppPath = (): string => {
const path = window.location.pathname + window.location.search
const m = path.match(/\/((?:pages|myPackageA|myPackageB|sfp)\/.*)/)
return m ? '/' + m[1] : '/pages/index/index'
}
/**
* 通知原生销毁 web-view 重走票据(design D3 重登闭环)。
* 非小程序环境无法回原生:清理 sso 态并降级 H5 登录页。
*/
export const reloginViaMiniprogram = (appPath?: string) => {
if (relogining) return
const redirect = appPath || currentAppPath()
// #ifdef H5
if (isMiniprogram()) {
relogining = true
wx.miniProgram.reLaunch({
url: `/pages/launch/index?ssoRelogin=1&cffpRedirect=${encodeURIComponent(redirect)}`
})
return
}
// #endif
clearSso()
uni.reLaunch({ url: '/myPackageA/login/login' })
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment