优化报销管理增加挂靠

This commit is contained in:
hero920103 committed 2026-11-06 01:13:28 +08:00
1 parent 7b1c94b5ff
commit ee49ef15b5
24 files changed
+1267 -70

No files matched your search

+76 -6
View File
@@ -35,14 +35,28 @@ func (c *AuthLoginController) LoginPage() {
step := c.GetString("step")
errMsg := c.GetString("error")
// 已登录且已选企业:直接放行到 authorize,无需再输密码
// 已登录且已选企业:直接放行,无需再输密码。
// 即使 redirect 丢失也要放行到业务站首页,否则会一直停留在登录页,
// 表现为「登录后还是跳回登录界面」。
sid := strings.TrimSpace(c.Ctx.GetCookie(sessionCookieName))
if sid != "" && step != "tenant" {
if session, err := authsvc.GetSession(sid); err == nil && session.Tid != authsvc.PendingTenantID {
if back := decodeRedirect(redirect); back != "" {
if session, err := authsvc.GetSession(sid); err == nil {
if session.Tid == authsvc.PendingTenantID {
// 已登录但还没选企业:直接带到企业选择步骤,
// 不要再渲染一次登录表单让用户重新登录。
target := "/auth/login?step=tenant&client_id=" + url.QueryEscape(clientID) +
"&redirect=" + url.QueryEscape(redirect)
c.Redirect(target, 302)
return
}
if back := safeBackURL(clientID, redirect); back != "" {
c.Redirect(back, 302)
return
}
} else {
// 会话已被吊销/过期:清掉失效 Cookie,避免后续请求继续带上它
clearSessionCookieForCtx(c.Ctx)
sid = ""
}
}
@@ -55,12 +69,17 @@ func (c *AuthLoginController) LoginPage() {
// 只有一家企业直接进入,无需展示选择页
if len(tenants) == 1 && step == "tenant" {
if _, _, err := authsvc.ChooseTenant(sid, tenants[0].Tid, clientID); err == nil {
if back := decodeRedirect(redirect); back != "" {
if back := safeBackURL(clientID, redirect); back != "" {
c.Redirect(back, 302)
return
}
}
}
} else if step == "tenant" {
// 选企业时会话已失效:不要渲染一个空的企业列表,退回登录页重新登录
clearSessionCookieForCtx(c.Ctx)
c.Redirect("/auth/login?client_id="+url.QueryEscape(clientID), 302)
return
}
}
@@ -168,7 +187,7 @@ func (c *AuthLoginController) LoginSubmit() {
return
}
back := decodeRedirect(req.Redirect)
back := safeBackURL(req.ClientID, req.Redirect)
c.serveJSON(map[string]interface{}{
"code": 200,
"msg": "登录成功",
@@ -236,7 +255,7 @@ func (c *AuthLoginController) SwitchTenant() {
c.serveJSON(map[string]interface{}{
"code": 200,
"msg": "切换成功",
"redirect": decodeRedirect(req.Redirect),
"redirect": safeBackURL(req.ClientID, req.Redirect),
"data": map[string]interface{}{"tokens": tokens, "identity": profile},
})
}
@@ -360,6 +379,29 @@ func (c *AuthLoginController) LogoutAction() {
c.serveJSON(map[string]interface{}{"code": 200, "msg": "已登出"})
}
// ClearSiteData 清除当前站点在浏览器中的全部缓存数据
// POST /auth/clear-site-data
//
// 登录页「清除缓存」按钮调用。前端 JS 已清理同源的 Web Storage / Cache
// Storage / IndexedDB / Service Worker,但 yz_sid 是 HttpOnly Cookie,前端
// 读不到也删不掉,因此这里:
// - 用 Set-Cookie 直接删除会话 Cookie(兼容所有浏览器);
// - 返回 Clear-Site-Data 响应头,让浏览器在安全上下文(HTTPS / localhost)
// 下把本 origin 的 cookies / storage / HTTP cache 一并清空。
//
// Clear-Site-Data 严格限定在响应所属 origin,不会影响其它站点。
func (c *AuthLoginController) ClearSiteData() {
// Cookie 被清后服务端会话将无人使用,同步吊销,避免留下仍有效的孤儿会话
// 占用「1 号 1 机」名额。
if sid := strings.TrimSpace(c.Ctx.GetCookie(sessionCookieName)); sid != "" {
_ = authsvc.RevokeSession(sid, models.RevokeReasonLogout)
}
clearSessionCookieForCtx(c.Ctx)
c.Ctx.Output.Header("Clear-Site-Data", `"cache", "cookies", "storage"`)
c.Ctx.Output.Header("Cache-Control", "no-store")
c.serveJSON(map[string]interface{}{"code": 200, "msg": "已清除"})
}
// VerifyConfig 登录验证配置(替代 /backend/login/getOpenVerify)
// GET /auth/verify-config?tid=
//
@@ -391,6 +433,34 @@ func (c *AuthLoginController) VerifyConfig() {
// ---------------------------------------------------------------- 工具
// safeBackURL 计算登录成功后的回跳地址。
//
// 优先使用业务端传来的 authorize 地址(base64url 编码);若链路中 redirect
// 丢失(直接打开登录页、单点登出后再次登录、选企业时参数未透传等),则退回
// 该应用已登记回跳地址所在的站点首页。业务站没有令牌时会自动重新发起 OIDC,
// 认证中心此时已有会话可静默签码完成登录,不会卡在登录页形成循环。
func safeBackURL(clientID, redirect string) string {
if back := decodeRedirect(redirect); back != "" {
return back
}
client, err := findClient(strings.TrimSpace(clientID))
if err != nil || client.RedirectURIs == nil {
return ""
}
var list []string
if err := json.Unmarshal([]byte(*client.RedirectURIs), &list); err != nil {
return ""
}
for _, item := range list {
u, err := url.Parse(strings.TrimSpace(item))
if err != nil || u.Host == "" || (u.Scheme != "http" && u.Scheme != "https") {
continue
}
return u.Scheme + "://" + u.Host + "/"
}
return ""
}
func decodeRedirect(encoded string) string {
if encoded == "" {
return ""