您好!欢迎来到源码码网

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

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

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

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)
推荐阅读
  • 原生小程序和封装的小程序有什么区别
  • 原生小程序和封装的小程序有什么区别
  • 原生小程序和封装的小程序是两种不同的开发模式,主要区别在于开发方式、性能表现、代码维护以及跨平台能力。为了让你更清晰地理解,我们可以把“原生”比作用钢筋水泥直接盖楼,而“封装”则是用预制板或套件来组装楼。以下是详细的对比分析:1.定义与本质原生小程序:定义: 直接使用微信官方提供的开发工具、语法和API进行开发。语言: 使用微信自己定义的一套技术栈(WXML(微信标记语言)做结构、WXSS(微信样式表)做样式、JS
  • 行业资讯
  • 来源:源码码网
  • 编辑:源码码网
  • 时间:2026-03-06 11:54
  • 阅读:179
  • 深度解读:一颗“感知”气息的MEMS传感器,如何定义雾化设备的交互未来?
  • 深度解读:一颗“感知”气息的MEMS传感器,如何定义雾化设备的交互未来?
  • 消费电子与物联网设备智能化普及,人与机器之间的交互方式早已不再局限于按键的“咔哒”声。从智能手机的触控滑动,到智能音箱的语音唤醒,交互正朝着更自然、更无感的方向演进。而在电子雾化设备(如雾化器、电子烟)这一细分领域,一种看似微小却至关重要的交互变革正在发生:如何精准、可靠地识别用户的“吸气”动作?答案,藏在了一颗不足3毫米见方的微型芯片里——MEMS气流压力开关传感器。我们不谈晦涩的术语,而是以一颗具体的产品——MS2202AA-M05
  • 源码教程
  • 来源:源码码网
  • 编辑:源码码网
  • 时间:2026-03-06 11:41
  • 阅读:40
  • 基于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
  • 阅读:278
  • 基于JAVA开发的企业级平台微信公众号管理系统SmartWx
  • 基于JAVA开发的企业级平台微信公众号管理系统SmartWx
  • SmartWx微信公众号管理系统是一个完整的微信公众号web操作版,直接编译即可运行。让用户不再用关心微信的底层接口,直接使用页面进行操作,简单方便。包括服务器绑定、文本管理、图文管理、菜单管理、粉丝管理、群发消息等。技术框架开发语言:JAVA数据库:MYSQLJAVA开发框架:SpringMVC+Spring+Mybatis缓存框架:j2cache前端开发框架:Layui+JQuery+html前台模板引擎:art-templat
  • 源码教程
  • 来源:gitee
  • 编辑:源码码网
  • 时间:2026-01-24 21:53
  • 阅读:272
  • 前后端分离的Java快速开发平台renren-security可免费商用
  • 前后端分离的Java快速开发平台renren-security可免费商用
  • renren-security是一个轻量级的,前后端分离的Java快速开发平台,能快速开发项目并交付【接私活利器】采用SpringBoot3.x、Shiro、MyBatis-Plus、Vue3、TypeScript、ElementPlus、VueRouter、Pinia、Axios、Vite框架,开发的一套权限系统,极低门槛,拿来即用。设计之初,就非常注重安全性,为企业系统保驾护航,让一切都变得如此简单。提供了代码生成器,只
  • 源码教程
  • 来源:gitee
  • 编辑:源码码网
  • 时间:2026-01-23 13:03
  • 阅读:174
联系客服
源码代售 源码咨询 技术开发 联系客服
029-84538663
手机版

扫一扫进手机版
返回顶部