权限控制归根到底是用来控制对资源的访问,它需要前后端共同来实现。
一、后端实现
从后端来说,权限控制的对象一般是接口,有时候会细致到某个具体的方方法,核心就是保护对底层数据库的访问。
1.模型设计
一般涉及如下几张表,包括user表、role表(也可以理解为拥有一系列权限的group)和permission表,
user_role和role_permission一般是为了实现用户和组,以及组和权限的多对多灵活控制的辅助表,
就我个人理解,如果更核心一些,其中只需要两张表:user表和permission表,role表的出现更多的是因为权限粒度很小的时候,一个用户会对应很多权限,一个一个的开非常繁琐,对一系列权限进行归纳整合成一个角色,更方便管理。
CREATE TABLE `user` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`id`) ); CREATE TABLE `role` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ); CREATE TABLE `user_role` ( `user_id` bigint(11) NOT NULL, `role_id` bigint(11) NOT NULL ); CREATE TABLE `role_permission` ( `role_id` bigint(11) NOT NULL, `permission_id` bigint(11) NOT NULL ); CREATE TABLE `permission` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `url` varchar(255) NOT NULL, `name` varchar(255) NOT NULL, `description` varchar(255) NULL, `pid` bigint(11) NOT NULL, PRIMARY KEY (`id`) );
有了这些表之后,在后端就可以设计全局的拦截器,对权限和路由进行判断,没有权限的访问会被直接拒绝或者跳转。
2.方法保护
spring security还有一些更细节的控制,会采用注解的方式加在service层的方法上,避免有人突破了URL层的限制。显然这样可以更好的保护对数据层的访问。
import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PreAuthorize; class A{ @Override @Transactional @Secured("ROLE_ADMIN") public void createModel(String tbName, List<Columns> columnsList) { saveModelColumns(columnsList); createDataTable(tbName); } @PreAuthorize("hasRole('ADMIN') and hasRole('DBA')") public void saveModelColumns(List<Columns> columnsList) { columnsDao.batchInsertColumns(columnsList); } }
二、前端实现
前端的权限控制五花八门,在我看来,大概是通过如下三个方面来实现的。
1.通过路由跳转时的拦截控制用户对具体页面的访问
router.beforeEach((to, from, next) => { const auth = store.state.user.authority; const token = store.state.user.token; /*1.去登录页没有权限要求; 2.去登录页之外的页面要判断是否登录,未登录先跳转到登录页; 3.如果已登录则判断是否有权限,没权限则返回403; 4.权限验证通过,直接放行 */ if (to.name === "Login") { next(); } else if (!token || token === "") { next({ name: "Login" }); } else if (to.meta.authority && !auth.includes(to.meta.authority)) { next({ name: "Forbidden" }); } else { next(); } });
2.通过对按钮的显示隐藏控制用户对具体内容的编辑
这个主要通过自定义指令实现
user.js用户权限缓存
const state = () => ({ username: "", token: "123", authority:['add','edit','delete','chart_view'], });
main.js
app.directive('auth', { mounted(el, binding) { if (!store.state.user.authority.some(item=> item===binding.value)) { el.parentNode && el.parentNode.removeChild(el) } } });
*.vue
<el-button v-auth="'add'" size="mini" type="primary" plain @click="handleCreate">添加</el-button>
只有拥有add权限的用户才能看见这个添加按钮
3.通过菜单项的显示隐藏控制用户对具体页面的访问
这个主要用于提升用户体验,如果用户没有权限也能看到某个菜单,一点就是403,体验会很差,还不如不让看见。
在通过route.js菜单渲染的时候,加入权限的过滤,就可以实现不同用户看到不同的菜单栏
methods: { //渲染菜单时根据当前用户的权限排除当前用户没有权限的菜单 getLinks() { let rt = this.$router.options.routes; rt.forEach((item) => { if (item.path === "/doc") { this.linkData = []; item.children.forEach((child) => { if (child.name) { const auth = this.$store.state.user.authority; if (child.meta) { if (auth.includes(child.meta.authority)) { this.linkData.push(child); } } else { this.linkData.push(child); } } }); } }); }, },
当系统要求对权限做特别细粒度的控制时,可以采用用户-权限这种模式,如果要求不是那么高,推荐使用用户-角色这种比较粗粒度的实现。