feat: 新增补丁管理和异常检测插件及相关功能
feat(protocol): 添加补丁管理和行为指标协议类型 feat(client): 实现补丁管理插件采集功能 feat(server): 添加补丁管理和异常检测API feat(database): 新增补丁状态和异常检测相关表 feat(web): 添加补丁管理和异常检测前端页面 fix(security): 增强输入验证和防注入保护 refactor(auth): 重构认证检查逻辑 perf(service): 优化Windows服务恢复策略 style: 统一健康评分显示样式 docs: 更新知识库文档
This commit is contained in:
@@ -27,40 +27,43 @@ const router = createRouter({
|
||||
{ path: 'plugins/print-audit', name: 'PrintAudit', component: () => import('../views/plugins/PrintAudit.vue') },
|
||||
{ path: 'plugins/clipboard-control', name: 'ClipboardControl', component: () => import('../views/plugins/ClipboardControl.vue') },
|
||||
{ path: 'plugins/plugin-control', name: 'PluginControl', component: () => import('../views/plugins/PluginControl.vue') },
|
||||
{ path: 'plugins/patch', name: 'PatchManagement', component: () => import('../views/plugins/PatchManagement.vue') },
|
||||
{ path: 'plugins/anomaly', name: 'AnomalyDetection', component: () => import('../views/plugins/AnomalyDetection.vue') },
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
/** Check if a JWT token is structurally valid and not expired */
|
||||
function isTokenValid(token: string): boolean {
|
||||
if (!token || token.trim() === '') return false
|
||||
try {
|
||||
const parts = token.split('.')
|
||||
if (parts.length !== 3) return false
|
||||
const payload = JSON.parse(atob(parts[1]))
|
||||
if (!payload.exp) return false
|
||||
// Reject if token expires within 30 seconds
|
||||
return payload.exp * 1000 > Date.now() + 30_000
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
/** Track whether we've already validated auth this session */
|
||||
let authChecked = false
|
||||
|
||||
router.beforeEach((to, _from, next) => {
|
||||
router.beforeEach(async (to, _from, next) => {
|
||||
if (to.path === '/login') {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('token')
|
||||
if (!token || !isTokenValid(token)) {
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('refresh_token')
|
||||
next('/login')
|
||||
} else {
|
||||
// If we've already verified auth this session, allow navigation
|
||||
// (cookies are sent automatically, no need to check on every route change)
|
||||
if (authChecked) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
// Check auth status via /api/auth/me (reads access_token cookie)
|
||||
try {
|
||||
const { me } = await import('../lib/api')
|
||||
await me()
|
||||
authChecked = true
|
||||
next()
|
||||
} catch {
|
||||
next('/login')
|
||||
}
|
||||
})
|
||||
|
||||
/** Reset auth check flag (called after logout) */
|
||||
export function resetAuthCheck() {
|
||||
authChecked = false
|
||||
}
|
||||
|
||||
export default router
|
||||
|
||||
Reference in New Issue
Block a user