Files
gangyan/chat_web_front/vite.config.ts
liuguancen b83c540018 fix(cas): 接通 CAS 单点登录全链路 + 清理冗余配置
修复链上的 8 个真 bug:
1. UserDetail(UserVO,Set) 漏 setAuthorities → CAS token 构造抛 IllegalArgumentException
   修:CasUserDetailsService.buildGptUserDetail 手动补 empty authorities

2. frontEndUrl 写死内网 IP,localhost 隧道用户跳回时"无法访问此网站"
   修:CasUrlBuilder 用 X-Forwarded-Host / Host 动态拼 service URL

3. vite proxy 没配 /metalinfo/chat_web_backend,CAS 回跳 ticket 被前端路由吞
   修:加一条 proxy(rewrite 去 /metalinfo 前缀)+ X-Forwarded-Host 转发

4. ticket 校验 service URL 跟 entry point 不一致 → CAS server mismatch
   修:自定义 AuthenticationDetailsSource 用同一个动态 URL

5. sendRedirect URL 含 # 经容器编码成 %23,浏览器拿不到 hash → 404
   修:改用 query 参数(/metalinfo/?cas_token=xxx),前端 router beforeEach 拦截

6. CAS 登录后 HttpSession 残留,第二次访问 /cas/login 不触发 entryPoint → 落到
   DispatcherServlet → 找不到映射 → 404 Whitelabel
   修:SuccessHandler 完成后 invalidate session + clear SecurityContext

7. CAS 路径漏写 Redis token,JwtAuthenticationFilter 校验时 LOGIN_TOKEN_KEY 找不到
   → "token已失效" → 前端 axios interceptor 清 token 跳回 login
   修:SuccessHandler 同步写 redisUtils.set,与 LoginController.saveLoginLog 对齐

8. permission.ts 没拦 query 里的 cas_token,hash 路由下 location.search 取不到
   修:router beforeEach 优先消费 cas_token 再走 getSession

清理冗余:
- CasProperties 删 6 个未用字段(enabled/serverLogout/appLogout/appKey/
  appSecret/httpsFlag/frontEndUrl)
- application.yml 同步删,移除写死的 app-secret 等敏感字段
- 删外部 override 文件 chat_web_backend/config/application.yml
- casServiceProperties.setService 改占位符(实际不被读取,只满足
  ServiceProperties.afterPropertiesSet 的非空校验)
- 删 permission.ts 的 [CAS] [GUARD] debug log,保留 catch error 一条

新增:
- CasUrlBuilder 工具类:从请求动态解析 host/scheme,多个地方共用

UI:
- welcome 页面玻璃按钮 + 呼吸光晕/光感动画(用户自己调过,本次保留)
- App.vue:/welcome 路径不渲染 Operates 侧边栏

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:22:57 +08:00

80 lines
2.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { fileURLToPath, URL } from 'node:url'
import { defineConfig,loadEnv } from 'vite'
import type { PluginOption } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
function setupPlugins(env: ImportMetaEnv): PluginOption[] {
return [
vue(),
vueDevTools(),
// topLevelAwait(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
]
}
export default defineConfig((env) => {
const viteEnv = loadEnv(env.mode, process.cwd()) as unknown as ImportMetaEnv
return {
base: viteEnv.VITE_GLOB_FRONT_CTX,
plugins: setupPlugins(viteEnv),
server: {
host: '0.0.0.0',
port: viteEnv.VITE_GLOB_FRONT_PORT,
open: false,
proxy: {
[viteEnv.VITE_GLOB_API_CTX]: {
target: viteEnv.VITE_GLOB_API_DEV_IP,
changeOrigin: true,
// 转发原 Host让后端能用 X-Forwarded-Host 拼出用户浏览器的实际 URL
configure: (proxy) => {
proxy.on('proxyReq', (proxyReq, req) => {
if (req.headers.host) proxyReq.setHeader('X-Forwarded-Host', req.headers.host)
proxyReq.setHeader('X-Forwarded-Proto', 'http')
})
},
},
// CAS 回调service URL 形如 /metalinfo/chat_web_backend/cas/login?ticket=xxx
// 把 /metalinfo 前缀去掉转给后端,否则 vite 当前端路由吃掉
[`${viteEnv.VITE_GLOB_FRONT_CTX}${viteEnv.VITE_GLOB_API_CTX}`]: {
target: viteEnv.VITE_GLOB_API_DEV_IP,
changeOrigin: true,
rewrite: (p) => p.replace(new RegExp(`^${viteEnv.VITE_GLOB_FRONT_CTX}`), ''),
configure: (proxy) => {
proxy.on('proxyReq', (proxyReq, req) => {
if (req.headers.host) proxyReq.setHeader('X-Forwarded-Host', req.headers.host)
proxyReq.setHeader('X-Forwarded-Proto', 'http')
})
},
},
// 工具服务通过 Nginx(:18000) 反代sub_filter 处理子资源路径
'/pdf/': {
target: 'http://localhost:18000',
changeOrigin: true,
},
'/draw/': {
target: 'http://localhost:18000',
changeOrigin: true,
},
},
},
build: {
target: 'esnext',
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
}
})