Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
CFFP-HB
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chao Sun
CFFP-HB
Commits
c9e4a6ed
Commit
c9e4a6ed
authored
Nov 10, 2026
by
jianan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(统一身份): sso 会话失效信号只认 401、/sso/ 白名单保留 Authorization、跳过老状态检查
parent
d92e264c
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
83 additions
and
11 deletions
+83
-11
App.vue
+24
-2
util/interceptor.ts
+12
-2
util/router.js
+4
-1
util/sso.ts
+43
-6
No files found.
App.vue
View file @
c9e4a6ed
...
...
@@ -6,6 +6,7 @@
import
{
hshare
}
from
'@/util/fiveshare'
;
import
dataHandling
from
"@/util/dataHandling"
;
import
{
useH5IdleExit
}
from
'@/util/useH5IdleExit'
import
*
as
sso
from
'@/util/sso'
import
tracker
from
'@/util/tracker/tracker.js'
const
idleExit
=
useH5IdleExit
()
...
...
@@ -189,6 +190,13 @@
// 检查用户状态的方法 防止多端操作导致此用户不存在
async
checkUserStatus
()
{
// Casdoor 票据会话:跳过老 storage 登录态检查。
// queryInfo/checkToken/obtainToken 都是老会话语义接口,不认 Casdoor JWT,
// 在此链路跑它们只会产生访客 token 噪音并干扰 sso 会话;
// 用户注销/禁用已由 CffpTokenFilter 每请求 resolveUserId(isActive=1) 把关。
if
(
sso
.
isSsoMode
())
{
return
}
// 只有已登录用户才需要检查
if
(
uni
.
getStorageSync
(
'loginType'
)
==
'codelogin'
&&
uni
.
getStorageSync
(
'cffp_userId'
))
{
try
{
...
...
@@ -346,8 +354,22 @@
if
(
params
.
dataToken
)
{
uni
.
setStorageSync
(
'dataToken'
,
decodeURIComponent
(
params
.
dataToken
));
}
//家办商城跳转未登录状态下
if
(
params
.
mpCffp
&&
(
params
.
noLogin
||
!
params
.
dataToken
))
{
// 显式游客入口(noLogin=1):无条件清 sso 态并降级。
// MODE_KEY 在 storage 中跨 web-view 实例残留,不清会劫持游客入口
// (拦截器按「sso 应在」发起 relogin,把游客自动升级成会话)
if
(
params
.
mpCffp
&&
params
.
noLogin
)
{
if
(
sso
.
isSsoMode
())
{
sso
.
clearSso
()
}
uni
.
removeStorageSync
(
'userIsActive'
)
uni
.
removeStorageSync
(
'dataToken'
)
uni
.
setStorageSync
(
'loginType'
,
'visitor'
);
}
// 老语义的未登录降级:排除两种 sso 场景——
// ①URL 带 ticket=(票据落地首包,登录态即将由 ticket.vue 建立)
// ②sso 模式已建立(reLaunch 后文档 URL 无 ticket,靠 MODE_KEY 挡住二次覆盖)
// 否则会把 loginType='sso' 覆盖成 visitor(本轮游客根因)
else
if
(
params
.
mpCffp
&&
!
params
.
ticket
&&
!
sso
.
isSsoMode
()
&&
!
params
.
dataToken
)
{
uni
.
removeStorageSync
(
'userIsActive'
)
uni
.
removeStorageSync
(
'dataToken'
)
uni
.
setStorageSync
(
'loginType'
,
'visitor'
);
...
...
util/interceptor.ts
View file @
c9e4a6ed
...
...
@@ -119,10 +119,17 @@ export const interceptor = () => {
invoke
(
args
)
{
// alert(JSON.stringify(args))
// Casdoor SSO 匿名端点(票据消费带 query,精确白名单匹配不上,按前缀放行)
// 注意:csf 工作台接力 /csf/api/c/sso/ticket/issue 也命中此前缀,但它要求
// Authorization: Bearer——整体替换 header 会把它抹掉(真机 401 根因),
// 故此处必须保留调用方显式设置的 Authorization。
if
(
typeof
args
.
url
===
'string'
&&
args
.
url
.
indexOf
(
'/sso/'
)
!==
-
1
)
{
const
keepAuth
=
args
.
header
&&
args
.
header
.
Authorization
args
.
header
=
{
'content-type'
:
args
.
method
===
'POST'
?
'application/json'
:
'application/x-www-form-urlencoded'
,
}
if
(
keepAuth
)
{
args
.
header
.
Authorization
=
keepAuth
}
return
}
// Casdoor 票据通道(小程序 web-view):令牌只在内存,绝不走 obtainToken 游客降级
...
...
@@ -177,9 +184,12 @@ export const interceptor = () => {
// 响应拦截器,可以对数据进行预处理
success
(
args
)
{
const
bodyT001
=
args
&&
args
.
data
&&
args
.
data
.
errorCode
&&
"T001"
==
args
.
data
.
errorCode
// Casdoor 分支失败:sso 模式接受纯 HTTP 401;非 sso 模式严格维持老条件(仅 body T001),行为不变
// Casdoor 分支失败:sso 模式只认 HTTP 401(过滤器 Casdoor 分支的失效信号就是 401)。
// 业务层 T001 是老会话语义(如 loginVerification 不认 Casdoor JWT),
// 在 sso 模式把它当失效会形成 relogin 死循环,不再据此杀会话。
// 非 sso 模式严格维持老条件(仅 body T001),行为不变。
const
unauthorized
=
sso
.
isSsoMode
()
?
(
bodyT001
||
args
.
statusCode
===
401
)
?
args
.
statusCode
===
401
:
bodyT001
if
(
unauthorized
){
if
(
sso
.
isSsoMode
())
{
...
...
util/router.js
View file @
c9e4a6ed
...
...
@@ -72,7 +72,10 @@ export default function initApp() {
})
}
if
(
uni
.
getStorageSync
(
'loginType'
)
==
'codelogin'
)
{
// sso 与 codelogin 同需刷新 cffp_userInfo:queryInfo 走 ThreadLocal,
// Casdoor 会话已被 CffpTokenFilter 写入同一上下文(真机验证 success:true)
var
lt
=
uni
.
getStorageSync
(
'loginType'
)
if
(
lt
==
'codelogin'
||
lt
==
'sso'
)
{
api
.
queryInfo
({}).
then
(
res
=>
{
if
(
!
res
[
'success'
])
{
uni
.
setStorageSync
(
'loginType'
,
'visitor'
)
...
...
util/sso.ts
View file @
c9e4a6ed
...
...
@@ -14,22 +14,52 @@ const MODE_KEY = 'cffp_sso_mode'
let
casdoorToken
:
string
|
null
=
null
let
relogining
=
false
// sessionStorage 键:令牌随文档会话存活(web-view 销毁即清),见 setSsoToken 注释
const
SS_KEY
=
'cffp_sso_jwt'
/**
* 建立 SSO 会话:令牌只进内存;非敏感登录态(模式标志、isLogin、业务 userId)
* 落持久 storage——web-view 被系统回收重载后内存令牌丢失,据此识别「登录态应在」,
* 拦掉无令牌请求并交原生重走票据,绝不静默降级 obtainToken 游客链路。
* 建立 SSO 会话。
*
* 令牌存 sessionStorage 而非纯内存变量:uni-app H5 的 reLaunch 是整页导航
* (location 级),落地链路 ticket→lanch→index 必然经历文档重建,纯内存令牌
* 会在第一次落地时丢失,随后所有请求 401 → relogin → 再丢 → 死循环。
* sessionStorage 生命周期=当前文档会话:web-view 销毁/微信回收即清空,
* 不跨会话持久(区别于 localStorage),安全语义与原设计一致。
* 非敏感登录态(模式标志、isLogin、业务 userId)仍落持久 storage,
* 令牌丢失时据此识别「登录态应在」并交原生重走票据。
* loginType 写 'sso':作为第三种登录方式(codelogin/visitor/sso),让全局登录态
* UI 与「已登录才拉 queryInfo」门禁天然识别;code 登录专属链路仍显式比对 codelogin。
*/
const
readSessionToken
=
():
string
|
null
=>
{
try
{
return
window
.
sessionStorage
.
getItem
(
SS_KEY
)
}
catch
(
e
)
{
return
null
}
}
export
const
setSsoToken
=
(
token
:
string
,
userId
:
number
|
string
)
=>
{
casdoorToken
=
token
try
{
window
.
sessionStorage
.
setItem
(
SS_KEY
,
token
)
}
catch
(
e
)
{
// 隐私模式等 sessionStorage 不可用:退回纯内存,行为同旧
}
uni
.
setStorageSync
(
MODE_KEY
,
'1'
)
uni
.
setStorageSync
(
'isLogin'
,
'1'
)
uni
.
setStorageSync
(
'loginType'
,
'sso'
)
uni
.
setStorageSync
(
'cffp_userId'
,
userId
)
}
export
const
getSsoToken
=
():
string
|
null
=>
casdoorToken
export
const
getSsoToken
=
():
string
|
null
=>
{
if
(
casdoorToken
)
{
return
casdoorToken
}
const
restored
=
readSessionToken
()
if
(
restored
)
{
casdoorToken
=
restored
}
return
restored
}
/**
* 票桥接力(referrer-portal-api design D11):用内存中的 Casdoor token 向工作台后端
...
...
@@ -40,7 +70,9 @@ export const getSsoToken = (): string | null => casdoorToken
*/
export
const
issueWorkbenchTicket
=
(
csfApiBase
:
string
):
Promise
<
string
>
=>
new
Promise
((
resolve
,
reject
)
=>
{
if
(
!
casdoorToken
)
{
// 经 getSsoToken 取(含 sessionStorage 恢复),文档重建后仍可接力
const
token
=
getSsoToken
()
if
(
!
token
)
{
reject
(
new
Error
(
'无 Casdoor 令牌,无法签发工作台票据'
))
return
}
...
...
@@ -50,7 +82,7 @@ export const issueWorkbenchTicket = (csfApiBase: string): Promise<string> =>
data
:
{},
header
:
{
'Content-Type'
:
'application/json'
,
'Authorization'
:
`Bearer
${
casdoorT
oken
}
`
'Authorization'
:
`Bearer
${
t
oken
}
`
},
success
:
(
res
:
any
)
=>
{
const
data
=
res
.
data
&&
res
.
data
.
code
===
200
?
res
.
data
.
data
:
null
...
...
@@ -65,6 +97,11 @@ export const isSsoMode = (): boolean => uni.getStorageSync(MODE_KEY) === '1'
export
const
clearSso
=
()
=>
{
casdoorToken
=
null
try
{
window
.
sessionStorage
.
removeItem
(
SS_KEY
)
}
catch
(
e
)
{
/* ignore */
}
uni
.
removeStorageSync
(
MODE_KEY
)
uni
.
removeStorageSync
(
'isLogin'
)
uni
.
removeStorageSync
(
'cffp_userId'
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment