1:希望实现对系统中所有资源的动态管理,即不同权限的用户只能访问到自己权限范围以内对应的资源,系统内其它资源对其是不可见的
2:一个完整的权限管理模块应该是什么样的?
权限管理是一个系统的核心模块,其性能的优劣将直接影响到系统的整体质量。理想中的权限模块应该是管理员在用户,角色,权限的管理上是极其人性化与易操作的。同时对于权限的变更也应该是明了的,对于变更的数据的原始状态也应该是清晰的。
责任划分
产品经理: 应尽量保证页面的设计合理性以及各模块之间的交互是人性化的
前端: 和产品经理共同商量页面的设计并保证技术的可行性,实现页面数据的渲染,最核心的模块是动态菜单的生成,以及各操作组件的动态生成
后端:完成数据库的设计,尽量保证扩展性和数据获取的便捷性,统一与前端交互的数据格式,要实现的核心模块是权限拦截
解决思路
1.数据库表设计
权限模块的数据库主流设计思路:RBAC模型:英文全称Role-Based Access Control,基于角色的访问控制,就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般则是多对多的关系。如下图所示:
表的分析与具体设计
由RBAC模型我们知道至少应该存在的是五张表:用户表,权限表,角色表,用户角色关联表,角色权限关联表,接下来我会一一阐述为什么要引入其他三张表,以及每张表最核心的字段(数据关联关系的字段)
部门表:
1)之所以要引入部门表,主要是为了授权的方便,我们知道,当我们为一个用户授权时,我们的做法是先为用户设定一个角色,再为角色赋予相应的权利,但是当我们的系统中同时存在很多拥有相同权限的用户时,如果对每一个用户进行授权就会在操作上显得繁琐,同时数据量会大大增加,于是我们就希望引入分组的概念,即把相同权限的用户分在一个部门下,然后对部门进行授权,那么只要是属于这一部门的用户就会首先获得该部门拥有的权限,此时用户的权限为对应的角色权限加上所在部门对应的权限。
2)部门通常是具有层级结构的,对于具有层级结构的数据,我们通常会引入一个基本字段parent_id以实现这种数据的层级关系,现在需要思考的问题是当引入了parent_id后我们有没有必要再引入一个表示层级关系的字段,比如说level,一级如果是1,那么它的子集的level就是1-1,1-2,至少笔者目前看来还没有发现引入这个字段的必要
3)具有层级结构的数据还会涉及到一个排序的问题,同级的数据谁放在前谁放在后必须要有字段区分,所以引入一个seq字段也是很好的。
4)综上,部门表所必须具备的字段应该是id,role_id,parent_id,seq
用户表:
1)RBAC模型基本表,不多赘述,必须具备的字段应该是id,telephone,email,status,dept_id,
2)telephone和email:主要是为了方便后台短信或者邮件通知用户
status:主要是为了标注当前用户的账号状态,用过微信和qq的人都应该知道,当被盗号时我们登入时会提示当前账号已冻结或者怎样,该字段就是为了实现这样的功能而存在
权限模块表:
1)之所以引入一个权限模块表,主要是为了让功能模块和权限点区分开来,如果权限表既包含功能模块的数据又包含权限点的数据,那么用户对本系统所具有的功能模块和每个模块所具有的的权限点就没有那么清晰,于是可以将原来的权限表拆成两张表,一张模块表,一张权限点表,这样逻辑也显得很清晰
2)模块表的数据也是层级结构的,所以id,parent_id,seq应该具备(详情见部门表)
3)需要注意的是对于最低级菜单,因为本身就是一个资源所以我们应该既要将其设计成一个权限模块又要将其设计成一个权限点
权限表:
1)RBAC模型基本表,不多赘述,必须具备的字段应该是id,url(资源路径),modul_id(关联模块字段),type(类型,用于区分是菜单还是按钮)
角色表:
1)RBAC模型基本表,不多赘述,必须具备的字段应该是id,name
用户角色关联表:
1)RBAC模型基本表,因为用户表和角色表的多对多关系而存在的,不多赘述,必须具备的字段应该是id,user_id,role_id
角色权限关联表:
1)RBAC模型基本表,因为权限表和角色表的多对多关系而存在的,不多赘述,必须具备的字段应该是id,acl_id,role_id
权限相关更细记录表
1)主要用于记录用户,角色,权限变更的表,非必须表,根据实际需求选择
表与表之间的关系如下图所示
权限拦截开发思路(采用过滤器的方式实现)
1.自定义一个权限过滤器,实现Filter接口,重写里面的三个方法
1.1在init方法中获得配置文件里的初始化参数,即不需要拦截的请求,然后将他们存进一个set集合
1.2在doFilter方法中实现拦截逻辑
首先获得当前请求的路径,判断是否为不需要拦截的路径,是的话就让请求继续执行下去,不是的话就继续判断
获得登入用户信息,如果当前用户为空,则进行无权限相关的操作,如果当前用户不为空则继续判断下去
判断当前用户是否拥有当前请求的权限,如果没有,则进行无权限相关的操作,如果有则让请求继续执行下去
提示:1)无权限操作,最好单独抽离出来封装成一个方法
2)判断是否拥有当前权限时,必然要进行数据库的操作,需要在过滤器中引入一个service执行相关数据库操作
1.3无权限操作的逻辑
定义一个无权限操作的跳转路径,并且需要保证该路径不在拦截范围之内,即必须把该路径放在之前定义的set集合中
当没有权限访问时,应该直接给出相应提示,可以使用response的print方法向页面打印信息,或者重定向方法,直接让它跳转到无权限访问时的处理路径
1.4判断当前用户是否拥有当前请求路径的逻辑
首先判断当前用户是否是超级管理员,是的话就默认拥有所有权限,至于如何判断当前用户是否超级是管理员,就要看系统的设计者是如何区分超级管理员和普通用户的。
然后要获得当前URL对应的权限点对象,如果为空,则表明系统中对该权限漠不关心,或者说系统中压根就没有当前权限,返回true
如果不为空,则判断当前用户所拥有的权限是否包含该权限点,如果包含则返回true。
2.需要解决的问题:后端应该以什么样的数据格式将当前用户的菜单信息和权限点信息发送给前端以便于前端实现菜单和按钮的动态生成呢?
想法:权限应该设置一个权限码,并且为权限码设置一个生成规则,后端也可以以map的形式存储用户的权限信息,权限码作为value,权限路径作为key(全线路径是唯一的所以适合作为key),尽量避免用list而用map是因为map查询更快,避免前端每次判断一个权限时都遍历一次集合
权限码应该系统自动录入(自定义规则)且不能人为操作去修改它,因为它实际上并没有什么作用,只是作为一个value,判断当前路径有效,因为权限点和权限模块是关联的,那么根据用户的权限点数据我们可以获得模块信息,我们应该把每个权限点所对应的模块信息存进一个set集合里,这样菜单的集合也就拿到了,权限的集合也拿到了,将这两个集合发送给前端就行了
题外话:(每次在jsp上引用css,js等静态资源时总是报404的错误让我很困扰)
//关于Spring MVC中的请求路径的问题
点后缀匹配的方式不会拦截js,css,jsp等静态资源
而通配符匹配路径的方式则会对所有资源路径进行拦截,包括静态资源
如果是采用这种方式配置的路径,则必须在spring-mvc配置文件里进行相关配置让DispartcheServlet默认允许访问静态资源,不然你会发现在jsp页面引用的css和js等静态资源都找不到
总结:作为一个还没毕业的java开发实习生,仅以我目前所掌握的那一点浅薄的知识来看,我觉得一个系统的最核心的地方在于数据库的设计,好的数据库设计是建立在对需求的高度理解之上,只有吃透了需求才能建立起来良好的关联关系,表与表之间的关系清晰明了的话,最直接的好处就是你后端的sql不会写成一坨。写的不对的地方还望批评指正。