如何实现一个权限管理系统?(附源码)
系統(tǒng)安全一直是在系統(tǒng)開發(fā)中不可規(guī)避的問(wèn)題,而權(quán)限控制又跟系統(tǒng)安全密不可分,大到用戶的訪問(wèn),小到一個(gè)頁(yè)面的按鈕,都有可能涉及到權(quán)限的控制。而renren-security便給我們提供了一套權(quán)限系統(tǒng)開發(fā)的解決方案。
renren-security是"人人社區(qū)"社區(qū)開源的輕量級(jí)權(quán)限管理系統(tǒng)。系統(tǒng)采用SprinBoot、Mybatis、Shiro框架進(jìn)行開發(fā),極低門檻,拿來(lái)即用,支持分布式部署、Quartz分布式集群調(diào)度、部門管理、數(shù)據(jù)權(quán)限、云存儲(chǔ)等功能。
項(xiàng)目特點(diǎn)
-
靈活的權(quán)限控制,可控制到頁(yè)面或按鈕,滿足絕大部分的權(quán)限需求
-
完善的部門管理及數(shù)據(jù)權(quán)限,通過(guò)注解實(shí)現(xiàn)數(shù)據(jù)權(quán)限的控制
-
完善的XSS防范及腳本過(guò)濾,徹底杜絕XSS攻擊
-
支持MySQL、Oracle、SQL Server、PostgreSQL等主流數(shù)據(jù)庫(kù)
系統(tǒng)結(jié)構(gòu)的設(shè)計(jì)也比較清晰,由admin、api、common等幾個(gè)模塊組成,每個(gè)模塊實(shí)現(xiàn)的功能大體如下:
common:公共模塊,以jar包的形式被其他模塊所依賴。實(shí)現(xiàn)了一些工具類和公共功能。包含時(shí)間處理、分頁(yè)、Sql過(guò)濾、Xss過(guò)濾和Redis切面定義、自定義異常處理等功能。
admin:管理系統(tǒng)模塊,以war包形式獨(dú)立部署。基于前后端分離的思想,主要用來(lái)用來(lái)開發(fā)后臺(tái)管理系統(tǒng)。包含用戶管理、角色管理、部門管理、菜單管理、定時(shí)任務(wù)、文件上傳、API校驗(yàn),同時(shí)采用Redis進(jìn)行數(shù)據(jù)緩存,支持單機(jī)和集群的部署。
api:API接口模塊,以war包形式獨(dú)立部署。模塊主要提供給前端UI調(diào)用的一些業(yè)務(wù)接口,實(shí)現(xiàn)了用戶注冊(cè)、登錄、接口權(quán)限認(rèn)證和用戶信息獲取。同時(shí)整合了swagger2實(shí)現(xiàn)了API接口文檔,方便了接口的查詢和調(diào)試。
系統(tǒng)架構(gòu)圖系統(tǒng)設(shè)計(jì)之初就特別注重安全性,基于Shiro在頁(yè)面和接口都實(shí)現(xiàn)了權(quán)限校驗(yàn)。
用戶登錄時(shí)對(duì)用戶的賬號(hào)密碼進(jìn)行驗(yàn)證,獲取用戶的信息和role權(quán)限,頁(yè)面顯示的時(shí)候會(huì)根據(jù)用戶擁有的權(quán)限顯示對(duì)應(yīng)的狀態(tài),接口請(qǐng)求的時(shí)候也會(huì)進(jìn)行用戶權(quán)限的校驗(yàn),數(shù)據(jù)保存到數(shù)據(jù)庫(kù)時(shí)候還進(jìn)行Sql和Xss的過(guò)濾,整個(gè)過(guò)程的核心思路是Shiro對(duì)用戶的認(rèn)證和授權(quán)。具體流程如下圖:
權(quán)限校驗(yàn)整體思路Shiro的認(rèn)證和授權(quán)
實(shí)現(xiàn)Shiro的認(rèn)證和授權(quán),需要自定義Realm繼承于AuthorizingRealm,同時(shí)重寫doGetAuthenticationInfo(認(rèn)證)和doGetAuthorizationInfo(授權(quán))這兩個(gè)方法。這里對(duì)于系統(tǒng)與Shiro的整合就不再做多的說(shuō)明。
用戶登錄的時(shí)候,將用戶的賬號(hào)和密碼包裝成一個(gè)UsernamePasswordToken后,再調(diào)用login提交賬戶認(rèn)證,shiro會(huì)自動(dòng)調(diào)用我們重寫的doGetAuthenticationInfo方法。
Subject?subject?=?SecurityUtils.getSubject(); UsernamePasswordToken?token?=?new?UsernamePasswordToken(username,?password); //提交認(rèn)證 subject.login(token); //Shiro進(jìn)行認(rèn)證 @Override protected?AuthenticationInfo?doGetAuthenticationInfo(AuthenticationToken?authcToken)?throws?AuthenticationException?{UsernamePasswordToken?token?=?(UsernamePasswordToken)authcToken;//獲取用戶信息SysUserEntity?user?=?new?SysUserEntity();user.setUsername(token.getUsername());user?=?sysUserDao.selectOne(user);//賬號(hào)不存在if(user?==?null)?{throw?new?UnknownAccountException("賬號(hào)或密碼不正確");}SimpleAuthenticationInfo?info?=?new?SimpleAuthenticationInfo(user,?user.getPassword(),?ByteSource.Util.bytes(user.getSalt()),?getName());return?info; }如果認(rèn)證成功,那么在系統(tǒng)的任何地方通過(guò)SecurityUtils.getSubject()方法就可以獲取認(rèn)證通過(guò)的信息。我們也可以借助它的這點(diǎn)特性,實(shí)現(xiàn)用戶的自動(dòng)登錄。
這里需要補(bǔ)充一點(diǎn),系統(tǒng)把權(quán)限化成了一個(gè)個(gè)的標(biāo)簽保存在數(shù)據(jù)庫(kù)中,用戶的權(quán)限中持有對(duì)應(yīng)的標(biāo)簽則表示擁有對(duì)應(yīng)的操作權(quán)限。而對(duì)于Shiro的授權(quán),在doGetAuthorizationInfo中需要獲取用戶的所有權(quán)限列表,通過(guò)權(quán)限列表篩選出是否擁有操作權(quán)限。
//Shiro進(jìn)行授權(quán) @Override protected?AuthorizationInfo?doGetAuthorizationInfo(PrincipalCollection?principals)?{//獲取認(rèn)證時(shí)候添加到SimpleAuthenticationInfo中的實(shí)例SysUserEntity?user?=?(SysUserEntity)principals.getPrimaryPrincipal();Long?userId?=?user.getUserId();//查詢用戶所有權(quán)限Set<String>?permsSet?=?new?HashSet<String>();List<String>?permsList?=?sysUserDao.queryAllPerms(userId);for(String?perms?:?permsList){if(StringUtils.isBlank(perms)){continue;}permsSet.addAll(Arrays.asList(perms.trim().split(",")));}SimpleAuthorizationInfo?info?=?new?SimpleAuthorizationInfo();info.setStringPermissions(permsSet);return?info; }Shiro的授權(quán)是被動(dòng)的,只有被相應(yīng)的條件觸發(fā)才會(huì)進(jìn)行用戶授權(quán),方式有以下幾種:
1.作用于頁(yè)面。頁(yè)面里如果遇到<#if shiro.hasPermission("sys:del")>,Shiro會(huì)調(diào)用自定義Realm獲取權(quán)限信息,看"sys:del"是否在權(quán)限數(shù)據(jù)中存在,存在則授權(quán)通過(guò),不存在則拒絕訪問(wèn),可應(yīng)用于對(duì)一些按鈕和標(biāo)簽的特定開放。
<#if?shiro.hasPermission("sys:add")><a?class="btn?btn-primary"?@click="add">新增</a> </#if> <#if?shiro.hasPermission("sys:del")><a?class="btn?btn-primary"?@click="del">刪除</a> </#if>2.通過(guò)注解的方式作用于接口。在controller中,方法如果加了@RequiresPermissions("sys:del")注解,Shiro同樣會(huì)調(diào)用自定義Realm獲取權(quán)限信息,看"sys:del"是否在權(quán)限數(shù)據(jù)中存在,存在則授權(quán)通過(guò),不存在則拒絕訪問(wèn),從而實(shí)現(xiàn)對(duì)接口的權(quán)限校驗(yàn)。
@RequestMapping("/delete") @RequiresPermissions("sys:del") public?R?delete(long?deptId){//判斷是否有子部門List<Long>?deptList?=?sysDeptService.queryDetpIdList(deptId);if(deptList.size()?>?0){return?R.error("請(qǐng)先刪除子部門");}sysDeptService.deleteById(deptId);return?R.ok(); }到此,基本上便實(shí)現(xiàn)了Shiro在頁(yè)面和接口的權(quán)限控制。當(dāng)然,Shiro更多是作用于表現(xiàn)層的一個(gè)控制,而出于系統(tǒng)安全考慮也應(yīng)該增加對(duì)數(shù)據(jù)的校驗(yàn)。因此在數(shù)據(jù)層面,則可通過(guò)Sql過(guò)濾和Xss過(guò)濾的方式實(shí)現(xiàn)過(guò)濾。項(xiàng)目中已經(jīng)為其封裝成工具,原理都是正則匹配和字符串替換,感興趣的伙伴可以直接到項(xiàng)目里查看,這里就不再累述了。
系統(tǒng)除了實(shí)現(xiàn)權(quán)限控制外,也實(shí)現(xiàn)了很多后臺(tái)管理系統(tǒng)開發(fā)中常用到的一些功能,像Quartz分布式集群調(diào)度、多數(shù)據(jù)源動(dòng)態(tài)切換以及集群部署下Session管理,感興趣的伙伴也可以查看源碼。
項(xiàng)目地址:https://gitee.com/renrenio/renren-security
總結(jié)
以上是生活随笔為你收集整理的如何实现一个权限管理系统?(附源码)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数据库并发控制,选择乐观锁还是悲观锁?
- 下一篇: Spring Boot 2发送邮件手把手