摘要:的自身注解的用法。所以自定義注解的作用很廣。但是在這里,我僅僅基于的來實現適用于它的自定義注解。其他的自定義的注解的編寫思路和這個也是類似的。
基于shiro的自定義注解的擴展
根據我的上一篇文章,權限設計的雜談中,涉及到了有關于前后端分離中,頁面和api接口斷開表與表層面的關聯,另辟蹊徑從其他角度找到方式進行關聯。這里我們主要采取了shiro的自定義注解的方案。本篇文章主要解決以下的問題。
如何通過邏輯進行頁面與api接口的關聯。
shiro的自身注解的用法。
如何編寫自定義注解。
如何通過邏輯進行頁面與api接口的關聯在表與表的結構關系中,頁面和接口表最終都是與權限表進行的關聯(詳情請查看我的上一篇文章《權限設計的雜談》)。
我們現在希望用另一種方案去替代他,實現一個低成本同時兼顧一定程度的權限控制。這里我們引入兩個概念。業務模塊,操作類型。
業務模塊
概念:將系統中的業務模塊抽象成一種數據,我們可以用字符串的形式去表示,例如:角色管理對應是role-manage、用戶管理對應是user-manage等等。我們將系統中所存在的業務模塊通過“最小特權原則”進行劃分,最終形成一批可分配的數據。
使用原則:api接口和頁面以及功能從本質上來說,都和業務模塊有邏輯關系,于是,我們可以對api接口與頁面(以及功能點)進行邏輯匹配,來判斷頁面與接口的關系。
操作類型
概念:將系統中的所有的操作類型抽象成一種數據,我們也可以用字符串的形式去表示,例如:新增對應的是add、分配對應的是allot等等。我們將系統中所有的操作類型根據業務模塊通過“數據許可證”進行劃分,最終形成一批可分配的數據。
使用原則:頁面是展示,功能點是動作,而接口是最終動作的資源提供,通過“業務模塊”確定了調取的資源,通過“操作類型”確定了資源的使用方式。通過兩者可以大致無誤的判斷頁面的功能點觸發的接口是否在鑒權之內。
現在提出了這兩個概念,他們最終的實際的使用方式是什么,我們先從以下幾個角度去思考一下。
數據庫中的頁面表或的api接口表中的數據就是真實有效嗎?
頁面或接口的實際使用,是以功能存在為前提,還是以數據庫表中的數據存在為前提。
權限結構中,“控制對象”的存儲只有數據庫這一種途徑嗎?
我們從結論出發來看這幾個問題,首先“控制對象”的存儲除了在數據庫中也可以代碼中,也可以在配置文件中,并不一定非得在數據庫;那么接著回答第二個問題,當數據庫存在的接口信息,而服務端并沒有開發這個接口的時候,數據庫的信本身就有問題,亦或者,數據庫里新增的接口必定是服務端上已經部署的接口才能生效;接著就是第一個問題,那么數據庫中關于“控制對象”的表中的數據并不一定是真實有效的。所以我們可以得出以下的解決方案
我們可以在接口上用注解的形式補充“業務模塊”和“操作類型”的數據信息,這兩類信息都可以存于常量類中,
在數據庫添加創建頁面表結構和頁面功能表結構的時候,添加“業務模塊”和“操作類型”字段。
可以將“業務模塊”和“操作類型”的信息存于數據庫的字典表中。
模塊的新增或操作的新增,必定帶來了接口的新增,那么就會帶來一次系統部署活動,這個運維成本是無法減少的,并不能通過表結構來減少。
但是這種方案僅適用于非強控制接口型的項目,在強控制型的接口項目仍然要將頁面與接口進行綁定,雖然這會帶來巨大的運維成本。另外也可以通過接口路由規則進行劃分,例如:/api/page/xxxx/(僅對頁面使用),/api/mobile/xxxxx(僅對移動端使用)將僅供頁面使用的接口進行分類,這類接口僅做認證不做授權,也可以達到目的。shiro的自身注解的用法
通過一個理論上的思路認可之后,剩下的則是付諸技術上的實踐,我們這邊采用的是Apache Shiro的安全框架,在Spring Boot的環境下應用。簡要說明以下幾個shiro的注解。
注解名 | 作用 |
---|---|
@RequiresAuthentication | 作用于的類、方法、實例上。調用時,當前的subject是必須經過了認證的。 |
@RequiresGuest | 作用于的類、方法、實例上。調用時,subject可以是guest狀態。 |
@RequiresPermissions | 作用于的類、方法、實例上。調用時,需要判斷suject中是否包含當前接口中的Permission(權限信息)。 |
@RequiresRoles | 作用于的類、方法、實例上。調用時,需要判斷subject中是否包含當前接口中的Role(角色信息)。 |
@RequiresUser | 作用于的類、方法、實例上。調用時,需要判斷subject中是否當前應用中的用戶。 |
/** * 1.當前接口需要經過"認證"過程 * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresAuthentication public String test(){ return "恭喜你,拿到了參數信息"; } /** * 2.1.當前接口需要經過權限校驗(需包含 角色的查詢 或 菜單的查詢) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR) public String test(){ return "恭喜你,拿到了參數信息"; } /** * 2.2.當前接口需要經過權限校驗(需包含 角色的查詢 與 菜單的查詢) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR) public String test(){ return "恭喜你,拿到了參數信息"; } /** * 3.1.當前接口需要經過角色校驗(需包含admin的角色) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresRoles(value={"admin"}) public String test(){ return "恭喜你,拿到了參數信息"; } /** * 3.2.當前接口需要經過角色與權限的校驗(需包含admin的角色,以及角色的查詢 或 菜單的查詢) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresRoles(value={"admin"}) @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR) public String test(){ return "恭喜你,拿到了參數信息"; }
在我們的實際使用過程中,實際上只需要使用@RequiresPermissions和@RequiresAuthentication就可以了這一個注解就可以了,在上一小節的結尾,我們采取了業務模塊與操作的結合方案來解耦頁面和api接口的關系,和apache Shiro的這種方式正好一致。但是@RequiresRoles這個我們盡可能不采用,因為角色的組合形式太多,角色名沒有辦法在接口中具象唯一化(很難指定接口歸某個角色調用,但是一定能知道接口歸屬于某些業務模塊的某些操作。)
現在我們來回顧一下整個運轉的流程。
如何編寫自定義注解但是僅僅是擁有shiro中的這5個注解肯定是不夠使用的。在實際的使用過程中,根據需求,我們會在權限認證中加入我們自己特有的業務邏輯的,我們為了便捷則可以采用自定義注解的方式進行使用。這種方法不僅僅適用于Apache Shiro,很多其他的框架如:Hibernate Validator、SpringMVC、甚至我們可以寫一套校驗體系,在aop中去驗證權限,這都是沒問題的。所以自定義注解的作用很廣。但是在這里,我僅僅基于shiro的來實現適用于它的自定義注解。
定義注解類
/** * 用于認證的接口的注解,組合形式默認是“或”的關系 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Auth { /** * 業務模塊 * @return */ String[] module(); /** * 操作類型 */ String[] action(); }
定義注解的處理類
/** * Auth注解的操作類 */ public class AuthHandler extends AuthorizingAnnotationHandler { public AuthHandler() { //寫入注解 super(Auth.class); } @Override public void assertAuthorized(Annotation a) throws AuthorizationException { if (a instanceof Auth) { Auth annotation = (Auth) a; String[] module = annotation.module(); String[] action = annotation.action(); //1.獲取當前主題 Subject subject = this.getSubject(); //2.驗證是否包含當前接口的權限有一個通過則通過 boolean hasAtLeastOnePermission = false; for(String m:module){ for(String ac:action){ //使用hutool的字符串工具類 String permission = StrFormatter.format("{}:{}",m,ac); if(subject.isPermitted(permission)){ hasAtLeastOnePermission=true; break; } } } if(!hasAtLeastOnePermission){ throw new AuthorizationException("沒有訪問此接口的權限"); } } } }
定義shiro攔截處理類
/** * 攔截器 */ public class AuthMethodInterceptor extends AuthorizingAnnotationMethodInterceptor { public AuthMethodInterceptor() { super(new AuthHandler()); } public AuthMethodInterceptor(AnnotationResolver resolver) { super(new AuthHandler(), resolver); } @Override public void assertAuthorized(MethodInvocation mi) throws AuthorizationException { // 驗證權限 try { ((AuthHandler) this.getHandler()).assertAuthorized(getAnnotation(mi)); } catch (AuthorizationException ae) { if (ae.getCause() == null) { ae.initCause(new AuthorizationException("當前的方法沒有通過鑒權: " + mi.getMethod())); } throw ae; } } }
定義shiro的aop切面類
/** * shiro的aop切面 */ public class AuthAopInterceptor extends AopAllianceAnnotationsAuthorizingMethodInterceptor { public AuthAopInterceptor() { super(); // 添加自定義的注解攔截器 this.methodInterceptors.add(new AuthMethodInterceptor(new SpringAnnotationResolver())); } }
定義shiro的自定義注解啟動類
/** * 啟動自定義注解 */ public class ShiroAdvisor extends AuthorizationAttributeSourceAdvisor { public ShiroAdvisor() { // 這里可以添加多個 setAdvice(new AuthAopInterceptor()); } @SuppressWarnings({"unchecked"}) @Override public boolean matches(Method method, Class targetClass) { Method m = method; if (targetClass != null) { try { m = targetClass.getMethod(m.getName(), m.getParameterTypes()); return this.isFrameAnnotation(m); } catch (NoSuchMethodException ignored) { } } return super.matches(method, targetClass); } private boolean isFrameAnnotation(Method method) { return null != AnnotationUtils.findAnnotation(method, Auth.class); } }
總體的思路順序:定義注解類(定義業務可使用的變量)->定義注解處理類(通過注解中的變量做業務邏輯處理)->定義注解的攔截器->定義aop的切面類->最后定義shiro的自定義注解啟用類。其他的自定義的注解的編寫思路和這個也是類似的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76662.html
摘要:細粒度權限管理就是數據級別的權限管理。張三只能查看行政部的用戶信息,李四只能查看開發部門的用戶信息。比如通過的攔截器實現授權。 前言 本文主要講解的知識點有以下: 權限管理的基礎知識 模型 粗粒度和細粒度的概念 回顧URL攔截的實現 Shiro的介紹與簡單入門 一、Shiro基礎知識 在學習Shiro這個框架之前,首先我們要先了解Shiro需要的基礎知識:權限管理 1.1什...
摘要:小程序官方流程圖如下,官方地址如果此圖理解不清楚的地方也可參看我的博客本文是對接微信小程序自定義登錄的一個完整例子實現,技術棧為。調用微信接口獲取和根據和自定義登陸態返回自定義登陸態給小程序端。 小程序官方流程圖如下,官方地址 : https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login....
摘要:的統一認證授權是下面的一個簡單,易用的權限框架,對于單體應用來講,完全能夠極好的,快速的滿足權限的需求,所以一般在做項目的時候,都會成為開發者的首選。 Shiro的統一認證授權 Shiro是Apache下面的一個簡單,易用的Java權限框架,對于單體應用來講,Shiro完全能夠極好的,快速的滿足權限的需求,所以一般在做項目的時候,Shiro都會成為開發者的首選。 可是,如果你需要做第二...
摘要:寫在前面在一款應用的整個生命周期,我們都會談及該應用的數據安全問題。用戶的合法性與數據的可見性是數據安全中非常重要的一部分。 寫在前面 在一款應用的整個生命周期,我們都會談及該應用的數據安全問題。用戶的合法性與數據的可見性是數據安全中非常重要的一部分。但是,一方面,不同的應用對于數據的合法性和可見性要求的維度與粒度都有所區別;另一方面,以當前微服務、多服務的架構方式,如何共享Sessi...
摘要:下一代服務端開發下一代服務端開發第部門快速開始第章快速開始環境準備,,快速上手實現一個第章企業級服務開發從到語言的缺點發展歷程的缺點為什么是產生的背景解決了哪些問題為什么是的發展歷程容器的配置地獄是什么從到下一代企業級服務開發在移動開發領域 《 Kotlin + Spring Boot : 下一代 Java 服務端開發 》 Kotlin + Spring Boot : 下一代 Java...
閱讀 3376·2021-11-22 13:53
閱讀 3411·2021-10-11 11:11
閱讀 932·2019-08-30 14:12
閱讀 1222·2019-08-29 17:16
閱讀 640·2019-08-29 16:45
閱讀 3349·2019-08-29 12:56
閱讀 670·2019-08-28 17:55
閱讀 2065·2019-08-26 13:24