基于抽象 HandlerInterceptor 快速實現接口鑒權

歡迎關注公眾號:冬瓜白

相關文章:

  • 每天學習一點點之 Spring Web MVC 之抽象 HandlerInterceptor 快速實現常用功能(限流、權限等)

在[每天學習一點點之 Spring Web MVC 之抽象 HandlerInterceptor 快速實現常用功能(限流、權限等)](vscode-file://vscode-app/Applications/Visual Studio Code.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/workbench.html)中已經介紹過可以基于抽象的 HandlerInterceptor 來實現很多常見的功能。本文快速實現了類似于 Shiro 的鑒權注解 @RequiresPermissions,并且功能更強大。

定義權限注解:

/*** @author Dongguabai* @description* @date 2024-06-25 10:57*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface RequiresPermissions {/*** 當前接口需要的權限(如 ADMIN,USER)* @return*/String[] value();/*** 對象id字段屬性名稱(默認id)*/String key() default "id";
}

在這個例子中使用了 @RequiresPermissions 注解來指定這個接口需要的權限。指定了兩種權限:ADMIN 和USER。意味著只有擁有 ADMIN 或 USER 權限的用戶才能訪問這個接口。還指定了key為"id",也就是說會從請求參數中獲取名為"id"的參數,并使用這個參數來進行額外的權限檢查。

這里 key 的作用是,比如有的目標對象只能由特定的用戶去操作,這里的 key 就提供了這樣一種方式。

繼承 CustomizedHandlerMethodInterceptor 實現鑒權邏輯:

/*** @author dongguabai* @date 2024-06-25 10:58*/
@Component
public class RequiresPermissionsHandlerMethodInterceptor extends CustomizedHandlerMethodInterceptor<RequiresPermissions> {private static final Logger LOGGER = LoggerFactory.getLogger(RequiresPermissionsHandlerMethodInterceptor.class);@Overrideprotected boolean preHandle(HttpServletRequest request, HttpServletResponse response,HandlerMethod handlerMethod, RequiresPermissions annotation) throws Exception {//獲取當前登陸用戶BaseUser user = getLogin();if (user == null) {LOGGER.error("Unable to get login information");return false;}String key = annotation.key();String[] value = annotation.value();if (StringUtils.isBlank(key) || ArrayUtils.isEmpty(value)) {return true;}//獲取目標idLong id = getId(request, handlerMethod, key);if (id != null) {//用戶鑒權return checkUserPermission(response, user, value, id);}return true;}private Long getId(HttpServletRequest request, HandlerMethod handlerMethod, String key) throws IOException {CachingWrapper requestWrapper = new CachingWrapper(request);MethodParameter[] methodParameters = handlerMethod.getMethodParameters();Long id = null;for (MethodParameter methodParameter : methodParameters) {id = getidFromParameter(request, key, methodParameter, requestWrapper);if (id != null) {break;}}return id;}private Long getidFromParameter(HttpServletRequest request, String key,MethodParameter methodParameter, CachingWrapper requestWrapper) throws IOException {String parameterName = methodParameter.getParameterName();if (key.equals(parameterName)) {return Long.valueOf(request.getParameter(parameterName));} else if (methodParameter.getParameterAnnotation(RequestBody.class) != null) {return getIdFromBody(key, requestWrapper);}return null;}private Long getIdFromBody(String key, CachingWrapper requestWrapper) throws IOException {ObjectMapper mapper = new ObjectMapper();JsonNode rootNode = mapper.readTree(requestWrapper.getCachedBody());JsonNode idNode;if (rootNode.isArray() && rootNode.size() > 0) {idNode = rootNode.get(0).path(key);} else {idNode = rootNode.path(key);}if (!idNode.isMissingNode()) {return idNode.asLong();}return null;}private boolean checkUserPermission(HttpServletResponse response, BaseUser user, String[] value, Long id) {// 業務鑒權邏輯return true;}@Overrideprotected void afterCompletion(HttpServletRequest request, HttpServletResponse response,HandlerMethod handlerMethod, RequiresPermissions annotation, Exception ex) {// Do nothing}@Overrideprotected void postHandle(HttpServletRequest request, HttpServletResponse response,HandlerMethod handlerMethod, ModelAndView modelAndView, RequiresPermissions annotation) {// Do nothing}/*** 獲取當前登陸用戶*/private BaseUser getLogin() {return null;}
}
/*** @author dongguabai* @date 2024-06-25 17:33*/
public class CachingWrapper extends HttpServletRequestWrapper {private byte[] cachedBody;public CachingWrapper(HttpServletRequest request) throws IOException {super(request);InputStream requestInputStream = request.getInputStream();ByteArrayOutputStream cachedBodyOutputStream = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int length;while ((length = requestInputStream.read(buffer)) != -1) {cachedBodyOutputStream.write(buffer, 0, length);}this.cachedBody = cachedBodyOutputStream.toByteArray();}@Overridepublic ServletInputStream getInputStream() throws IOException {return new CachedBodyServletInputStream(this.cachedBody);}public byte[] getCachedBody() {return this.cachedBody;}private static class CachedBodyServletInputStream extends ServletInputStream {private ByteArrayInputStream cachedBodyInputStream;CachedBodyServletInputStream(byte[] cachedBody) {this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody);}@Overridepublic boolean isFinished() {return this.cachedBodyInputStream.available() == 0;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setReadListener(ReadListener readListener) {throw new UnsupportedOperationException();}@Overridepublic int read() throws IOException {return this.cachedBodyInputStream.read();}}
}

在鑒權邏輯中,需要從請求參數中獲取目標id。這是因為權限檢查可能需要根據這個id來進行。

例如可能需要檢查用戶是否有權限訪問這個id對應的資源。為了獲取這個id,需要解析請求參數。這個過程可能會比較復雜,因為請求參數可能以不同的方式傳遞,例如,它們可能在URL的查詢字符串中,或者在POST請求的請求體中。因此這里提供了getId方法來處理這些情況,并盡可能地獲取到id。

這里比較麻煩的是從接口中解析 id 參數,這里支持兩種方式:

  1. 當前接口調用者需要有目標對象 ID為傳入的 id 的 OWNER 權限:
@PostMapping("/count")
@ResponseBody
@RequiresPermissions("OWNER")
public Response count(Long id) {return Response.getSuccess(count(projectId));
}
  1. 當前接口調用者需要有項目ID為傳入的 project.id 項目的 USE 權限:
@PostMapping("/list")
@ResponseBody
@RequiresPermissions("USE")
public Response list(@RequestBody Project project) {return Response.getSuccess(list(project);
}

getId 方法用于解析請求中的參數,包括URL的查詢參數和POST請求的請求體參數。getIdFromBody 方法專門用于解析POST請求的請求體參數。通過這種方式,可以靈活地控制用戶的訪問權限。

總結

本文主要探討了基于抽象的 HandlerInterceptor 來實現鑒權注解 @RequiresPermissions,它可以靈活地控制用戶的訪問權限。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/42753.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/42753.shtml
英文地址,請注明出處:http://en.pswp.cn/web/42753.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Numpy的廣播機制(用于自動處理不同形狀的數組)

NumPy 廣播是一種強大的機制&#xff0c;允許 NumPy 在執行元素級運算時自動處理不同形狀的數組。廣播的規則使得無需顯式地創建匹配形狀的數組&#xff0c;直接進行運算&#xff0c;大大簡化了代碼并提高了效率。 基本概念 廣播的基本思想是讓較小的數組在需要的維度上進行擴…

【MySQL數據庫之概念性問題】

1、關系型數據庫和非關系型數據庫 關系型數據庫&#xff08;Relational Database&#xff0c;簡稱RDBMS&#xff09;和非關系型數據庫&#xff08;NoSQL Database&#xff09;是兩種不同的數據庫類型。SQL本身叫做結構化查詢語言1、關系型數據庫&#xff1a;&#xff08;MySQL…

Django 更新數據 save()方法

1&#xff0c;添加模型 Test/app11/models.py from django.db import modelsclass Post(models.Model):title models.CharField(max_length200)content models.TextField()pub_date models.DateTimeField(date published)class Book(models.Model):title models.CharFie…

Spring Boot集成grpc快速入門demo

1.什么是GRPC&#xff1f; gRPC 是一個高性能、開源、通用的RPC框架&#xff0c;由Google推出&#xff0c;基于HTTP2協議標準設計開發&#xff0c;默認采用Protocol Buffers數據序列化協議&#xff0c;支持多種開發語言。gRPC提供了一種簡單的方法來精確的定義服務&#xff0c…

UE5.3-基礎藍圖類整理一

常用藍圖類整理&#xff1a; 1、獲取當前關卡名&#xff1a;Get Current LevelName 2、通過關卡名打開關卡&#xff1a;Open Level(by name) 3、碰撞檢測事件&#xff1a;Event ActorBeginOverlap 4、獲取當前player&#xff1a;Get Player Pawn 5、判斷是否相等&#xff1…

深入解析CSS中的!important規則:優先級與最佳實踐

先上實踐&#xff0c;再討論設計 在實際工程中&#xff0c;!important 的使用場景通常出現在需要確保某個樣式規則具有最高優先級&#xff0c;以覆蓋其他可能沖突的樣式規則時。以下是一個具體的例子&#xff1a; 場景描述 假設你正在開發一個網站&#xff0c;該網站使用了多…

JavaScript的數組與函數

數組 <script type"text/javascript">/** 知識點&#xff1a;數組* 理解&#xff1a;一維數組的容器* 概念&#xff1a;* 1.數組中的數據叫做元素* 2.元素都有編號叫做下標/索引* 3.下標從0開始* 注意&#xff1a;* 1.數組作為數據的容器…

【JavaScript腳本宇宙】狀態管理利器:JavaScript 庫全面解析

提升項目效率與可維護性&#xff1a;JavaScript 狀態管理庫大揭秘 前言 在現代前端開發中&#xff0c;狀態管理是一個至關重要的話題。隨著復雜性的增加&#xff0c;有效地管理應用程序的狀態變得越來越具有挑戰性。本文將介紹一些流行的 JavaScript 庫&#xff0c;這些庫提供…

WEB安全基礎:網絡安全常用術語

一、攻擊類別 漏洞&#xff1a;硬件、軟件、協議&#xff0c;代碼層次的缺陷。 后?&#xff1a;方便后續進行系統留下的隱蔽后?程序。 病毒&#xff1a;一種可以自我復制并傳播&#xff0c;感染計算機和網絡系統的惡意軟件(Malware)&#xff0c;它能損害數據、系統功能或攔…

C++語言學習精簡筆記(包含C++20特性)

目錄 1 C新語法C與CC編譯運行String編程范式C基礎類型**自動類型推導**統一對象初始化&#xff1a;Uniform Initialization 控制結構if語句for語句switch語句namespace 2 函數函數聲明形式參數函數參數傳遞的選擇函數返回值的選擇 函數重載 Lambda表達式函數的定義和申明生存期…

磁力貓磁力搜索大全教程,如何使用磁力鏈接

磁力鏈接是一種特殊的下載鏈接&#xff0c;磁力鏈接可以理解為一個文件識別碼&#xff0c;而并非具體的資源地址&#xff0c;下載軟件需要拿著這個識別碼去整個互聯網(DHT網絡)去尋找持有該資源的用戶(節點)&#xff0c;如果找到則可以進行傳輸下載。一般年代越久遠的磁力鏈接下…

【一】m2芯片的mac中安裝ubuntu24虛擬機集群

文章目錄 1. 虛擬機配置2. 復制虛擬機2.1 修改主機名2.2 修改網絡 1. 虛擬機配置 在官方網站下載好ubuntu24-arm版鏡像開始安裝&#xff0c;安裝使用VMWare Fusion的社區免費授權版,使用一臺m2芯片的mac電腦作為物理機平臺。 為什么選擇ubuntu24&#xff1f;因為centOS7目前已…

Proteus + Keil單片機仿真教程(五)多位LED數碼管的靜態顯示

Proteus + Keil單片機仿真教程(五)多位LED數碼管 上一章節講解了單個數碼管的靜態和動態顯示,這一章節將對多個數碼管的靜態顯示進行學習,本章節主要難點: 1.鎖存器的理解和使用; 2.多個數碼管的接線封裝方式; 3.Proteus 快速接頭的使用。 第一個多位數碼管示例 元件…

『C + ⒈』‘\‘

&#x1f942;在反斜杠(\)有⒉種最常用的功能如下所示&#x1f44b; #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main(void) {int a 10;int b 20;int c 30;if (a 10 &&\b 20 &&\c 30){printf("Your print\n");}else{prin…

二分查找3

1. 有序數組中的單一元素&#xff08;540&#xff09; 題目描述&#xff1a; 算法原理&#xff1a; 二分查找解題關鍵就在于去找到數組的二段性&#xff0c;這里數組的二段性是從單個數字a開始出現然后分隔出來的&#xff0c;如果mid落入左半部分那么當mid為偶數時nums[mid1]…

ByteMD富文本編輯器的vue3配置

Git地址&#xff1a;GitHub - bytedance/bytemd: ByteMD v1 repository 控制面板輸入 npm install bytemd/vue-next 下載成功后在src/main.ts中引用 import "bytemd/dist/index.css";引入后保存&#xff0c;下面是一些插件&#xff0c;比如說我用到gmf和hightLight&…

java后端向jsp傳日期,jsp調用數據錯誤問題

問題 今天遇到個bug&#xff0c;后端使用request.setAttribute("key", value);將startDate、endDate兩個日期字符串傳遞到jsp中&#xff0c;使jsp可以獲取到日期進行查詢操作。但接口拼接的參數startDate為2017&#xff0c;endDate為1986&#xff0c;讓人百思不得其…

彩色圖像(RGB)或灰度圖像(Gray)轉tensor數據(附img2tensor代碼)

&#x1f4aa; 專業從事且熱愛圖像處理&#xff0c;圖像處理專欄更新如下&#x1f447;&#xff1a; &#x1f4dd;《圖像去噪》 &#x1f4dd;《超分辨率重建》 &#x1f4dd;《語義分割》 &#x1f4dd;《風格遷移》 &#x1f4dd;《目標檢測》 &#x1f4dd;《暗光增強》 &a…

homebrew常用命令

Homebrew 提供了許多命令和選項來管理軟件包。以下是一些常用的 Homebrew 命令&#xff1a; ### 常用 Homebrew 命令 1. **安裝軟件包**&#xff1a; brew install <軟件包名稱> 2. **卸載軟件包**&#xff1a; brew uninstall <軟件包名稱> 3. **更…

CompletableFuture工具類使用

CompletableFuture工具類可以幫助實現Java并發編程中的任務編排 以上除了join用于阻塞調用該方法的線程并且接受CompletableFuture的返回值以外其它方法皆有Async異步和Executor指定線程池選項 對于supply,run,apply,accept的區別在于函數式編程的接口類型不同: supply: Sup…