您好!欢迎来到源码码网

大型后台管理系统,用户登录状态该如何保存?

  • 源码教程
  • 来源:源码码网
  • 编辑:源码码网
  • 时间:2025-11-06 12:16
  • 阅读:785

大型后台管理系统的用户登录状态保存需要综合考虑安全性用户体验系统架构。以下是企业级的完整方案:

1. 多层级存储策略

class AuthManager {
    constructor() {
        this.storage = {
            // 内存存储(最高安全)
            memory: new Map(),
            
            // 会话级存储(较高安全)
            session: sessionStorage,
            
            // 持久化存储(用户偏好)
            local: localStorage,
            
            // 自动传输存储(兼容性)
            cookie: this.cookieManager
        }
    }
    
    // 分级存储策略
    setLoginState(userData) {
        // 1. 敏感数据 - 内存存储(刷新即失)
        this.storage.memory.set('access_token', userData.accessToken)
        this.storage.memory.set('session_key', userData.sessionKey)
        
        // 2. 刷新令牌 - HttpOnly Cookie(防XSS)
        this.setHttpOnlyCookie('refresh_token', userData.refreshToken, {
            maxAge: 7 * 24 * 60 * 60, // 7天
            httpOnly: true,
            secure: true,
            sameSite: 'strict'
        })
        
        // 3. 用户基本信息 - localStorage(持久化)
        this.storage.local.setItem('user_info', JSON.stringify({
            id: userData.id,
            name: userData.name,
            avatar: userData.avatar,
            roles: userData.roles,
            permissions: userData.permissions
        }))
        
        // 4. 登录状态标识 - sessionStorage(标签页级)
        this.storage.session.setItem('is_logged_in', 'true')
        this.storage.session.setItem('login_timestamp', Date.now())
        
        // 5. 辅助信息 - 普通Cookie
        this.setCookie('user_theme', userData.theme, { maxAge: 30 * 24 * 60 * 60 })
    }
}

2. Token 双令牌机制

class TokenService {
    constructor() {
        this.accessToken = null
        this.refreshToken = null
        this.tokenRefreshTimeout = null
    }
    
    // 设置双令牌
    setTokens(accessToken, refreshToken) {
        // Access Token - 短期(内存 + 备用localStorage)
        this.accessToken = accessToken
        localStorage.setItem('access_token_backup', accessToken)
        localStorage.setItem('access_token_expire', Date.now() + 2 * 60 * 60 * 1000) // 2小时
        
        // Refresh Token - 长期(HttpOnly Cookie)
        this.setHttpOnlyCookie('refresh_token', refreshToken, {
            maxAge: 7 * 24 * 60 * 60,
            httpOnly: true,
            secure: true
        })
        
        // 启动token自动刷新
        this.startTokenRefresh()
    }
    
    // 自动刷新token
    startTokenRefresh() {
        // 在token过期前5分钟自动刷新
        const refreshTime = 115 * 60 * 1000 // 115分钟
        
        this.tokenRefreshTimeout = setTimeout(async () => {
            try {
                await this.refreshAccessToken()
            } catch (error) {
                console.error('Token自动刷新失败:', error)
                this.handleTokenRefreshFailed()
            }
        }, refreshTime)
    }
    
    // 刷新access token
    async refreshAccessToken() {
        const refreshToken = this.getRefreshToken()
        
        const response = await fetch('/api/auth/refresh', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ refreshToken })
        })
        
        if (response.ok) {
            const data = await response.json()
            this.setTokens(data.accessToken, data.refreshToken)
            return data.accessToken
        } else {
            throw new Error('刷新token失败')
        }
    }
}

3. 应用启动状态恢复

class AppInitializer {
    constructor() {
        this.authManager = new AuthManager()
        this.tokenService = new TokenService()
    }
    
    // 应用启动时恢复登录状态
    async initializeApp() {
        console.log('🚀 应用初始化...')
        
        // 1. 检查内存中的token(最高优先级)
        if (this.tokenService.accessToken) {
            console.log('✅ 内存中存在有效token')
            return await this.validateCurrentToken()
        }
        
        // 2. 检查备份的access token
        const backupToken = localStorage.getItem('access_token_backup')
        const tokenExpire = localStorage.getItem('access_token_expire')
        
        if (backupToken && tokenExpire && Date.now() < parseInt(tokenExpire)) {
            console.log('✅ 使用备份token恢复')
            this.tokenService.accessToken = backupToken
            return await this.validateCurrentToken()
        }
        
        // 3. 尝试使用refresh token刷新
        if (this.getRefreshToken()) {
            console.log('🔄 尝试刷新token...')
            try {
                await this.tokenService.refreshAccessToken()
                return await this.validateCurrentToken()
            } catch (error) {
                console.log('❌ token刷新失败')
                this.clearAuthState()
            }
        }
        
        // 4. 最终检查session状态
        if (sessionStorage.getItem('is_logged_in') === 'true') {
            console.log('🔍 会话状态存在,但token无效')
            // 可能需要重新认证
        }
        
        // 5. 未登录状态
        console.log('🔒 用户未登录')
        this.redirectToLogin()
    }
    
    // 验证当前token有效性
    async validateCurrentToken() {
        try {
            const response = await fetch('/api/auth/validate', {
                headers: {
                    'Authorization': `Bearer ${this.tokenService.accessToken}`
                }
            })
            
            if (response.ok) {
                const userData = await response.json()
                this.authManager.setUserData(userData)
                console.log('✅ 登录状态验证成功')
                return true
            } else {
                throw new Error('Token验证失败')
            }
        } catch (error) {
            console.error('❌ Token验证失败:', error)
            this.clearAuthState()
            return false
        }
    }
}

4. 请求拦截与自动处理

// axios请求拦截配置
import axios from 'axios'

class RequestInterceptor {
    constructor() {
        this.setupInterceptors()
    }
    
    setupInterceptors() {
        // 请求拦截器
        axios.interceptors.request.use(
            (config) => {
                const token = this.getAccessToken()
                if (token) {
                    config.headers.Authorization = `Bearer ${token}`
                }
                return config
            },
            (error) => Promise.reject(error)
        )
        
        // 响应拦截器 - 处理认证失败
        axios.interceptors.response.use(
            (response) => response,
            async (error) => {
                const originalRequest = error.config
                
                // 处理401错误(未认证)
                if (error.response?.status === 401 && !originalRequest._retry) {
                    originalRequest._retry = true
                    
                    try {
                        // 尝试刷新token
                        const newToken = await this.tokenService.refreshAccessToken()
                        originalRequest.headers.Authorization = `Bearer ${newToken}`
                        return axios(originalRequest)
                    } catch (refreshError) {
                        // 刷新失败,清除登录状态
                        this.clearAuthState()
                        this.redirectToLogin()
                        return Promise.reject(refreshError)
                    }
                }
                
                // 处理403错误(权限不足)
                if (error.response?.status === 403) {
                    this.showPermissionDenied()
                    return Promise.reject(error)
                }
                
                return Promise.reject(error)
            }
        )
    }
}

5. 安全增强措施

class SecurityEnhancer {
    constructor() {
        this.setupSecurityMeasures()
    }
    
    setupSecurityMeasures() {
        // 1. 防止XSS攻击
        this.sanitizeStorage()
        
        // 2. 设置安全头
        this.setSecurityHeaders()
        
        // 3. 监听异常行为
        this.setupBehaviorMonitoring()
    }
    
    // 存储数据清理
    sanitizeStorage() {
        // 定期清理过期的存储数据
        setInterval(() => {
            this.cleanExpiredStorage()
        }, 60 * 60 * 1000) // 每小时清理一次
    }
    
    // 行为监控
    setupBehaviorMonitoring() {
        // 监听多标签页登录冲突
        window.addEventListener('storage', (event) => {
            if (event.key === 'login_conflict' && event.newValue) {
                this.handleLoginConflict()
            }
        })
        
        // 监听用户活跃度
        this.setupActivityMonitoring()
    }
    
    // 用户活跃度监控
    setupActivityMonitoring() {
        let lastActivityTime = Date.now()
        
        const updateActivityTime = () => {
            lastActivityTime = Date.now()
        }
        
        // 监听用户操作
        ['mousedown', 'keypress', 'scroll', 'touchstart'].forEach(event => {
            document.addEventListener(event, updateActivityTime, true)
        })
        
        // 检查用户是否活跃
        setInterval(() => {
            const inactiveTime = Date.now() - lastActivityTime
            if (inactiveTime > 30 * 60 * 1000) { // 30分钟无操作
                this.handleUserInactive()
            }
        }, 60 * 1000) // 每分钟检查一次
    }
}

6. 完整的登录状态管理

// 主入口文件 - main.js
import { createApp } from 'vue'
import App from './App.vue'
import { AuthManager, TokenService, AppInitializer } from './services/auth'

const app = createApp(App)

// 初始化认证服务
const authManager = new AuthManager()
const tokenService = new TokenService()
const appInitializer = new AppInitializer(authManager, tokenService)

// 全局提供认证服务
app.provide('authManager', authManager)
app.provide('tokenService', tokenService)

// 应用启动
appInitializer.initializeApp().then((isAuthenticated) => {
    if (isAuthenticated) {
        console.log('✅ 应用启动完成 - 用户已登录')
        app.mount('#app')
    } else {
        console.log('🔒 应用启动完成 - 用户未登录')
        // 可以挂载应用但显示登录界面
        app.mount('#app')
    }
}).catch((error) => {
    console.error('❌ 应用启动失败:', error)
    // 错误处理
})

7. 不同场景的配置方案

// 安全等级配置
export const SECURITY_LEVELS = {
    // 金融级安全
    FINANCE: {
        accessTokenExpire: 15 * 60, // 15分钟
        refreshTokenExpire: 24 * 60 * 60, // 24小时
        autoLogout: 30 * 60, // 30分钟无操作自动登出
        multiFactorAuth: true,
        sessionPerDevice: true
    },
    
    // 企业级安全
    ENTERPRISE: {
        accessTokenExpire: 2 * 60 * 60, // 2小时
        refreshTokenExpire: 7 * 24 * 60 * 60, // 7天
        autoLogout: 60 * 60, // 1小时无操作
        multiFactorAuth: false,
        sessionPerDevice: false
    },
    
    // 内部系统
    INTERNAL: {
        accessTokenExpire: 8 * 60 * 60, // 8小时
        refreshTokenExpire: 30 * 24 * 60 * 60, // 30天
        autoLogout: 4 * 60 * 60, // 4小时无操作
        multiFactorAuth: false,
        rememberMe: true
    }
}

总结

大型后台管理系统的登录状态管理应该是:

  1. 分层存储:敏感数据存内存,持久数据存localStorage,自动传输用Cookie

  2. 双令牌机制:短期access token + 长期refresh token

  3. 自动恢复:应用启动时智能恢复登录状态

  4. 安全增强:XSS防护、行为监控、自动登出

  5. 错误处理:完善的token刷新和错误处理机制

  6. 灵活配置:根据不同安全需求调整策略

这样的设计既保证了安全性,又提供了良好的用户体验。


特别声明:
1、如无特殊说明,内容均为本站原创发布,转载请注明出处;
2、部分转载文章已注明出处,转载目的为学习和交流,如有侵犯,请联系客服删除;
3、编辑非《源码码网》的文章均由用户编辑发布,不代表本站立场,如涉及侵犯,请联系删除;
全部评论(0)
推荐阅读
  • 陀螺匠OA办公系统源码 PHP + Vue 前后端分离 完整可商用
  • 陀螺匠OA办公系统源码 PHP + Vue 前后端分离 完整可商用
  • 项目介绍《陀螺匠》是一套采用Laravel9框架结合Swoole高性能框架与Vue.js前端技术栈构建的AI+低代码双引擎智能办公系统。陀螺匠深度融合了AI智能体以及低代码开发平台,系统简约,易于功能扩展,方便二次开发。旨在为企业提供一站式、数智化转型的全方位解决方案,助力企业高效运营与智能决策。源码说明源码说明:项目代码100%开源,基于AGPL-3.0,支持免费商用!技术栈:系统基于Laravel9+Swoole+Vue.
  • 软件教程
  • 来源:CRMEB
  • 编辑:CRMEB
  • 时间:2026-03-27 15:33
  • 阅读:392
  • open claw最近很火,如何理性看待?
  • open claw最近很火,如何理性看待?
  • openclaw是2026年初科技圈最火爆的现象级话题。它不仅仅是又一个AI产品,更被百川智能的王小川誉为与ChatGPT诞生同等重要的里程碑——标志着AI从”只会说”的对话者,进化到了”能够做”的行动者。对于这股全民”养虾”热潮,我的观察是:它代表了一次人机关系的革命性跃迁,但同时也是一场充满风险与泡沫的狂欢,其真正的价值需要在喧嚣褪去、安全与成本的挑战被克服后才能显现。下
  • 行业资讯
  • 来源:源码码网
  • 编辑:源码码网
  • 时间:2026-03-17 11:06
  • 阅读:431
  • 原生小程序和封装的小程序有什么区别
  • 原生小程序和封装的小程序有什么区别
  • 原生小程序和封装的小程序是两种不同的开发模式,主要区别在于开发方式、性能表现、代码维护以及跨平台能力。为了让你更清晰地理解,我们可以把“原生”比作用钢筋水泥直接盖楼,而“封装”则是用预制板或套件来组装楼。以下是详细的对比分析:1.定义与本质原生小程序:定义: 直接使用微信官方提供的开发工具、语法和API进行开发。语言: 使用微信自己定义的一套技术栈(WXML(微信标记语言)做结构、WXSS(微信样式表)做样式、JS
  • 行业资讯
  • 来源:源码码网
  • 编辑:源码码网
  • 时间:2026-03-06 11:54
  • 阅读:666
  • 基于NetCore(Razor Page)开发的Cms建站系统MIT协议
  • 基于NetCore(Razor Page)开发的Cms建站系统MIT协议
  • FytSoaCms一款基于NetCore2.2/3.1(RazorPage)功能强大的Cms建站系统,支持前后端分离。简介:模块化:全新的架构和模块化的开发机制,便于灵活扩展和二次开发。模型/栏目/分类信息体系:通过栏目和模型绑定,以及不同的模型类型,不同栏目可以实现差异化的功能,轻松实现诸如资讯、下载、讨论和图片等功能。通过分类信息和栏目绑定,可以自动建立索引表,轻松实现复杂的信息检索。FytSoa是一套基于NetCore+SqlS
  • 开发工具
  • 来源:gitee
  • 编辑:源码码网
  • 时间:2026-01-24 22:21
  • 阅读:592
  • 基于JAVA开发的企业级平台微信公众号管理系统SmartWx
  • 基于JAVA开发的企业级平台微信公众号管理系统SmartWx
  • SmartWx微信公众号管理系统是一个完整的微信公众号web操作版,直接编译即可运行。让用户不再用关心微信的底层接口,直接使用页面进行操作,简单方便。包括服务器绑定、文本管理、图文管理、菜单管理、粉丝管理、群发消息等。技术框架开发语言:JAVA数据库:MYSQLJAVA开发框架:SpringMVC+Spring+Mybatis缓存框架:j2cache前端开发框架:Layui+JQuery+html前台模板引擎:art-templat
  • 源码教程
  • 来源:gitee
  • 编辑:源码码网
  • 时间:2026-01-24 21:53
  • 阅读:601
联系客服
源码代售 源码咨询 技术开发 联系客服
029-84538663
手机版

扫一扫进手机版
返回顶部