后台管理系统项目亮点与难点

3,705 阅读10分钟

登录

Token(涉及JWT、HTTPS、请求头、Cookie/localstorage)

后台管理系统的登录权限设计通常会使用 token 来实现。下面是一个常见的设计方案:

  1. 用户登录:用户在登录页面输入用户名和密码后,后台验证用户信息是否正确。如果验证通过,后台会生成一个 token,并将其返回给前端。
  2. Token 生成:Token 是一个包含用户身份信息的字符串,通常使用 JWT(JSON Web Token)来生成。JWT 由三部分组成:头部、载荷和签名。头部包含算法和类型信息,载荷包含用户的身份信息和其他自定义信息,签名用于验证 Token 的完整性。
  3. Token 传递:后台将生成的 Token 返回给前端,前端通常会将 Token 存储在客户端的 Cookie 或者本地存储中。
  4. 请求验证:用户在后续的请求中,通过axios请求拦截器进行拦截,每次请求的时候头部携带token。后台在接收到请求后,会验证 Token 的合法性和有效期。
  5. Token 验证:后台会对收到的 Token 进行解析和验证。验证过程包括检查 Token 的签名是否正确、Token 是否过期以及用户权限等。
  6. 有效期管理:为了保证安全性,Token 通常会设置一个有效期,可以是固定的时间,也可以是根据用户的操作动态延长。在有效期过期之后,用户需要重新登录获取新的 Token。如果放在cookies里面,当浏览器关闭了就丢失了。重新打开浏览器都需要重新登录验证,后端也会在每周固定一个时间点重新刷新token,让后台用户全部重新登录一次,确保后台用户不会因为电脑遗失或者其它原因被人随意使用账号。
  1. 会话级别的 Cookie:如果不设置过期时间,Cookie 将成为会话级别的 Cookie,也就是说,它将在用户关闭浏览器时自动过期。这种类型的 Cookie 仅在用户当前会话期间有效,一旦用户关闭浏览器,该 Cookie 将被删除。

  2. 持久性 Cookie:可以通过设置过期时间来创建持久性 Cookie。过期时间可以是一个具体的日期和时间,或者是一个相对于当前时间的时间间隔。一旦设置了过期时间,浏览器将在过期时间到达后自动删除该 Cookie。

  1. Token 存储:后台通常会将 Token 存储在服务器端的内存、数据库或者缓存中。存储的方式取决于具体的应用需求和技术架构。

需要注意的是,为了保证安全性,Token 在传输过程中应该使用HTTPS进行加密传输,以防止被中间人窃取或篡改。此外,为了防止 Token 被盗用,可以使用一些额外的安全措施,如限制 Token 的使用范围、定期更换 Token 等。

Session

Session 登录是一种常见的用户认证和授权机制,它在 Web 应用程序中被广泛使用。基本工作原理:

  1. 用户登录:用户在登录页面输入用户名和密码后,后台验证用户信息是否正确。如果验证通过,后台会创建一个会话(session)并为该会话分配一个唯一的会话 ID。
  2. 会话管理:后台会将会话 ID 存储在服务器端,并将其发送给客户端。通常,会话 ID 会被存储在一个名为 sessionID 的 Cookie 中,或者通过 URL 参数的方式传递给客户端。
  3. 请求验证:用户在后续的请求中,会携带会话 ID。后台在接收到请求后,会根据会话 ID 来识别用户,并验证用户的身份和权限。
  4. 会话状态维护:后台会使用会话 ID 来维护用户的登录状态。会话对象中可以保存一些用户相关的信息,如用户 ID、角色、权限等。在后续的请求中,后台可以通过会话 ID 来获取用户的信息,以便进行权限验证和业务处理。
  5. 会话过期:为了保证安全性,会话通常会设置一个有效期。在有效期过期之后,用户需要重新登录获取新的会话。
  6. 会话存储:会话对象通常会被存储在服务器端的内存、数据库或者缓存中。存储的方式取决于具体的应用需求和技术架构。

需要注意的是,Session 登录是基于服务器端的会话管理,因此需要在服务器端进行会话状态的维护和验证。相比于 Token 登录,Session 登录的主要特点是会话状态存储在服务器端,相对更安全,但也需要服务器端进行额外的存储和管理。

权限管理

用户——角色——权限 RBAC(Role-Based Access Control)模型

  1. 定义角色和权限:首先,需要定义不同的角色和对应的权限。角色可以根据用户的职责和权限等级来划分,例如管理员、普通用户、编辑等。权限可以是系统中的具体功能或操作,如添加、编辑、删除等。
  2. 分配权限给角色:将定义好的权限分配给相应的角色。可以通过界面或配置文件等方式,将权限与角色进行关联。
  3. 用户角色关联:将用户与相应的角色进行关联。当用户登录系统时,系统会根据用户的角色来确定其具有的权限。
  4. 权限验证:在系统的各个功能模块中,需要进行权限验证。当用户访问某个功能时,系统会检查用户所属的角色是否具有相应的权限,如果没有权限,则禁止用户执行该操作或访问该页面。
  5. 后台管理界面:为了方便管理和配置权限,可以开发一个后台管理界面,供管理员进行权限的设置和管理。管理员可以通过该界面添加、编辑、删除角色和权限,以及分配权限给角色。
  6. 日志记录:为了安全和追溯,建议在系统中记录用户的操作日志。可以记录用户的登录、权限变更等操作,以便后续审计和追踪。

动态路由

通过token获取用户对应的 role,动态根据用户的 role 获取其对应有权限的路由数组,(中间处理数据将后台返回的数据映射成路由)通过 router.addRoutes 动态挂载这些路由。

上述所有的数据和操作都是通过vuex全局管理控制的。(刷新页面后 vuex的内容也会丢失,所以需要重复上述的那些操作,或者将数据保存在本地刷新时自动加载本地数据)

封装请求 axios

切换环境

我们的项目环境可能有开发环境、测试环境和生产环境。 我们通过node的环境变量来匹配我们的默认的接口url前缀。 axios.defaults.baseURL可以设置axios的默认请求地址。

// 环境的切换
if (process.env.NODE_ENV == 'development') {    
    axios.defaults.baseURL = 'https://www.baidu.com';} 
else if (process.env.NODE_ENV == 'debug') {    
    axios.defaults.baseURL = 'https://www.ceshi.com';
} 
else if (process.env.NODE_ENV == 'production') {    
    axios.defaults.baseURL = 'https://www.production.com';
}

请求拦截

import store from '@/store/index';

// 请求拦截器
axios.interceptors.request.use(    
    config => {        
        const token = store.state.token;        
        token && (config.headers.Authorization = token);  //请求头增加 token     
        return config;    
    },    
    error => {        
        return Promise.error(error);    
})

响应的拦截

axios.interceptors.response.use(    
    response => {   
        // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据     
        // 否则的话抛出错误
        if (response.status === 200) {            
            return Promise.resolve(response);        
        } else {            
            return Promise.reject(response);        
        }    
    },    
    // 服务器状态码不是2开头的的情况
    // 这里可以跟你们的后台开发人员协商好统一的错误状态码    
    // 然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等等
    // 下面列举几个常见的操作,其他需求可自行扩展
    error => {            
        if (error.response.status) {            
            switch (error.response.status) {                
                // 401: 未登录
                // 未登录则跳转登录页面,并携带当前页面的路径
                // 在登录成功后返回当前页面,这一步需要在登录页操作。                
                case 401:                    
                    router.replace({                        
                        path: '/login',                        
                        query: { 
                            redirect: router.currentRoute.fullPath 
                        }
                    });
                    break;
                // 403 token过期
                // 登录过期对用户进行提示
                // 清除本地token和清空vuex中token对象
                // 跳转登录页面                
                case 403:
                     Toast({
                        message: '登录过期,请重新登录',
                        duration: 1000,
                        forbidClick: true
                    });
                    // 清除token
                    localStorage.removeItem('token');
                    store.commit('loginSuccess', null);
                    // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面 
                    setTimeout(() => {                        
                        router.replace({                            
                            path: '/login',                            
                            query: { 
                                redirect: router.currentRoute.fullPath 
                            }                        
                        });                    
                    }, 1000);                    
                    break; 

                // 404请求不存在
                case 404:
                    Toast({
                        message: '网络请求不存在',
                        duration: 1500,
                        forbidClick: true
                    });
                    break;
                // 其他错误,直接抛出错误提示
                default:
                    Toast({
                        message: error.response.data.message,
                        duration: 1500,
                        forbidClick: true
                    });
            }
            return Promise.reject(error.response);
        }
    }    
});

post请求头

axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

封装get方法和post方法

/**
 * get方法,对应get请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
export function get(url, params){    
    return new Promise((resolve, reject) =>{        
        axios.get(url, {            
            params: params        
        }).then(res => {
            resolve(res.data);
        }).catch(err =>{
            reject(err.data)        
    })    
});}

post方法必须要使用对提交从参数对象进行序列化的操作,所以这里我们通过node的qs模块来序列化我们的参数。这个很重要,如果没有序列化操作,后台是拿不到你提交的数据的。

import QS from 'qs';
/** 
 * post方法,对应post请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
export function post(url, params) {
    return new Promise((resolve, reject) => {
         axios.post(url, QS.stringify(params))
        .then(res => {
            resolve(res.data);
        })
        .catch(err =>{
            reject(err.data)
        })
    });
}复制代码

后台管理系统的开发和维护可能面临以下一些难点:

  1. 复杂的业务逻辑:后台管理系统通常需要处理大量的业务逻辑,包括权限管理、数据管理、用户管理等。这些业务逻辑可能相互关联,且涉及到复杂的数据操作和业务规则,需要开发人员具备深入的业务理解和设计能力。
  2. 多角色权限管理:后台管理系统通常需要支持多角色的权限管理,不同角色的用户可能具有不同的操作权限和数据访问权限。实现灵活且安全的权限管理是一个挑战,需要考虑角色的细粒度控制、权限的动态分配和管理等。
  3. 数据可视化和报表:后台管理系统通常需要提供数据可视化和报表功能,以便管理员能够直观地查看和分析数据。这需要开发人员具备数据分析和可视化技术的能力,并能够设计和实现复杂的数据报表和图表。
  4. 性能和扩展性:后台管理系统通常需要处理大量的数据和用户请求,因此性能和扩展性是关键考虑因素。开发人员需要优化数据库查询、缓存策略和系统架构,以确保系统能够高效地处理大规模的数据和并发请求。
  5. 用户体验和界面设计:后台管理系统虽然主要面向管理员和运营人员,但良好的用户体验和界面设计仍然是重要的。开发人员需要考虑用户操作的便捷性、界面的友好性和响应速度,以提高用户的工作效率和满意度。
  6. 安全性和数据保护:后台管理系统通常涉及敏感的用户数据和业务信息,因此安全性和数据保护是至关重要的。开发人员需要采取合适的安全措施,如用户认证、数据加密、访问控制等,以保护系统和用户数据的安全。

以上只是一些常见的难点,实际的后台管理系统开发中还可能面临其他具体的挑战,需要根据具体的业务需求和技术要求来解决。