一、場景
在使用shiro框架的時候,遇到了這樣的需求:本系統有多個用戶,每個用戶分配不同角色,每個角色的權限也不一致。比如A用戶擁有新聞列表的增刪改查權限,而B用戶只有查看新聞列表的權限,而沒有刪除、新增、修改的權限,此時有3種方案:1、不給B用戶分配刪除、新增、修改的菜單,這樣用戶就無法點擊從而無法操作。2、給B用戶分配菜單,后台中進行增刪改查操作時都要進行權限驗證。 3、給B用戶分配菜單並且進行操作的時候校驗權限。
顯然,第2、3種方案比第1中方案要安全。本系統中使用第二種方案。
二、為什么使用注解+AOP
使用shiro過程中一般都會自定義Realm,Realm主要進行權限和登錄的校驗,當校驗登錄用戶是否有某個權限的時候,有2種方式:1、使用注解 @RequiresPermissions("news:*") 來判斷用戶是否有news的所有權限。 2、使用Subject.isPermitted("news:*") 方法判斷用戶是否有news的所有權限。
這兩種方法的區別在於,第一種:比如當前用戶在新聞列表中刪除某篇新聞,但是該用戶並沒有這種權限,此時會拋出異常,我們需要處理異常即可,但是頁面進行跳轉,我們希望用戶在新聞列進行刪除操作的時候,如果沒有該權限則會彈窗提示,而不是跳轉到統一的異常頁面。 第二種,可以實現第一種的不足,但是沒有第一種方便快捷。
為了綜合上述兩種的有點以及缺點,實現shiro校驗權限時有異常但不刷新頁面,同時以注解的形式使用。
三、實現
實現的效果:需要校驗權限的方法比如刪除方法del(),只要在該方法上添加自定義注解,即可實現上述效果。
3.1 自定義注解
/** * 類名 :權限控制注解 * 用法 : * 創建人 : shyroke * 時間:2018/12/18 10:33 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface PermissionAnnotation { String permissionName(); }
3.2 編寫切面
/** * 類名 :權限的切面類 * 用法 : * 創建人 : shyroke * 時間:2018/12/18 10:38 */ @Aspect @Component public class PermissionAspect { @Pointcut("@annotation(com.shyroke.daydayzhuan.util.PermissionAnnotation)") private void permisson(){ } /** * 給添加PermissionAnnotation注解的方法校驗權限,而不必每個方法內都判斷權限 * @param joinPoint * @param permissionAnnotation * @return * @throws Throwable */ @Around("permisson()&&@annotation(permissionAnnotation)") public Object advice(ProceedingJoinPoint joinPoint, PermissionAnnotation permissionAnnotation) throws Throwable { R r = null; r = (R) joinPoint.proceed(); String permissionName =permissionAnnotation.permissionName(); if(StringUtils.isEmpty(permissionName)){ r.setFlag(false); r.setMessage("權限名稱不能為空"); return r; } //校驗當前登錄用戶是否有該權限 boolean isPermission = UserUtils.isPermission(permissionName); if(!isPermission){ r.setFlag(false); r.setMessage("沒有此操作權限!"); } return r; } }
UserUtils.java
/** * 類名 :用戶的工具類 * 用法 : * 創建人 : shyroke * 時間:2018/12/18 14:39 */ public class UserUtils { /** * 校驗當前登錄用戶是否有該權限 * @param permissionname 權限名稱 * @return */ public static boolean isPermission(String permissionname) { Subject subject = SecurityUtils.getSubject(); if(subject.isPermitted(permissionname)){ return true; }else{ return false; } } }
3.3 使用
@PermissionAnnotation(permissionName = "boke:*") @PostMapping(value = "desc") @ResponseBody public R desc(){ return R.ok("刪除成功!"); }