springAop實現(xiàn)權(quán)限管理數(shù)據(jù)校驗操作日志的場景分析
前言
作為一個寫java的使用最多的輕量級框架莫過于spring,不管是老項目用到的springmvc,還是現(xiàn)在流行的springboot,都離不開spring的一些操作,我在面試的時候問的最多的spring的問題就是我們在平常的項目中使用spring最多的有哪幾個點
在我看來無非就兩個
- spring的bean管理,說的高大上一點就是spring的ioc,di
- spring的AOP
spring是一個很強大的輕量級框架,功能遠不止這兩點,但是我們用的最多的就是這兩點。
spring bean 管理
想我們常用的 @Controller @Service @Component 等等都是將我們的bean交給spring管理,我們在獲取bean的時候就直接通過 @Resource 就可以獲取,當(dāng)然@Resource 不是spring的,@Autowired 才是spring的,這樣我們可以很方便的管理我們的各種bean,使用起來也很方便,不用到處new
springAOP
這個應(yīng)該是spring面試最常問道的問題了,我面試的時候一般不直接問,我會說一個場景,如果面試者使用過aop立馬就可以回答出來。
有這么一個場景,一個系統(tǒng)已經(jīng)開發(fā)完成了,而且已經(jīng)上線運行了一段時間,很穩(wěn)定了,現(xiàn)在要加一個功能,就是想收集用戶的操作日志,操作日志要比較細致,比如 某某人,在哪個時間點,操作了哪個模塊,請求的參數(shù)是什么樣子的,操作結(jié)果如何,等一些比較細致的操作。很多面試者第一時間想到的就是使用過濾器,試想一下,過濾器真的能做到記錄這么細致的內(nèi)容嗎?有的可能會想到,我們定義一個公共的方法,所有需要記錄日志的地方都去調(diào)用這個方法,等等。其實這些都不好,最好的當(dāng)然是使用aop,使用aop侵入性最小,系統(tǒng)已經(jīng)穩(wěn)定運行了,不能去動之前的代碼,我們做個aop就可以了,對原來的代碼幾乎0侵入,對系統(tǒng)影響最小。
那aop實現(xiàn)的方式有哪幾種呢?aop實現(xiàn)的步驟又是怎么樣的呢?aop還能做些什么呢?這些問題如果在實際項目中使用過,一定能回答出來,如果沒有使用過,估計有點難回答,這些都是項目框架的東西,很多公司的項目這一塊已經(jīng)封裝好了,很多人直接一直在用,但是沒有去查看源碼,就不知道具體的實現(xiàn),實現(xiàn)起來其實也很簡單。
接下來我們就以一個小例子來說明下如何使用aop
aop總結(jié)起來最常用的就兩種方式
1、采用聲明的方式來實現(xiàn)(基于XML) 胡子眉毛一把抓,哈哈
2、是采用注解的方式來實現(xiàn)(基于AspectJ)精準(zhǔn)定位
我習(xí)慣使用注解的方式,更加靈活,使用起來也方便,接下來就以注解的方式來講下如何使用aop來做權(quán)限校驗,數(shù)據(jù)校驗,操作日志記錄
申明切面
我們使用springaop,首先要將定義的類交給spring管理,然后使用aspectj 定義切面,我們要額外引入
aspectj
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency>
@Component @Aspect public class OperationInterceptor { }
定義切點
我們采用注解的方式,那么我們首先要申明一個注解
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface GlobalInterceptor { /** * 權(quán)限編碼 */ PermissionCodeEnum permissionCode(); /** * 操作模塊 * * @return */ OperModuleEnum opObject() default OperModuleEnum.NO_OBJECT; /** * 操作描述 * * @return */ String opDescription() default ""; }
然后在我們的切面中定義切點
@Component @Aspect public class OperationInterceptor { @Pointcut("@annotation(com.xx.xx.GlobalInterceptor)") private void opMethods() { } }
定義通知類型
我們要輸入還想要輸出,那么我們就要將目標(biāo)方法包圍,所以使用around
@Component @Aspect public class OperationInterceptor { @Pointcut("@annotation(com.xx.xx.GlobalInterceptor)") private void opMethods() { } @Around("opMethods()") public Object doMethod(ProceedingJoinPoint point) throws BusinessException { //TODO 業(yè)務(wù)代碼 return null; } }
這樣一個完整的切面就定義好了
使用切面
在我們的controller中直接使用,我們拿一個登錄來講
@RequestMapping("/login") @GlobalInterceptor(permissionCode = PermissionCodeEnum.NO_PERMISSION, opObject = OperModuleEnum.OBJECT_LOGIN, opDescription = "登錄賬號:#{#param1}") public AjaxResponseVO login(HttpSession session, @VerifyParam(required = true) String account, @VerifyParam(required = true) String password,@VerifyParam(required = true) String checkCode){ }
我們要傳入 切面注解需要的參數(shù)
permissionCode 權(quán)限編碼,我這里定義的是一個枚舉,類型自己根據(jù)實際情況定義
opObject 操作模塊
opDescription 操作描述,這里簡單的組織下描述文字,參數(shù)的地方使用占位符,到時候根據(jù)占位符index獲取具體的參數(shù)
這樣我們在切面中就可以拿到這些參數(shù)
@Component @Aspect public class OperationInterceptor { @Pointcut("@annotation(com.xx.xx.GlobalInterceptor)") private void opMethods() { } @Around("opMethods()") public Object doMethod(ProceedingJoinPoint point) throws BusinessException { Object obj = null; try { /** * 獲取登錄信息 */ SessionUserDto sessionUserDto = getSessionUser(); /** * 獲取目標(biāo)切點 */ Object target = point.getTarget(); /** * 獲取參數(shù) */ Object[] arguments = point.getArgs(); /** * 獲取方法名 */ String method = point.getSignature().getName(); /** * 獲取參數(shù)類型 */ Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes(); /** * 獲取具體的方法 */ Method m = target.getClass().getMethod(method, parameterTypes); GlobalInterceptor interceptor = m.getAnnotation(GlobalInterceptor.class); if (null == interceptor) { return obj; } /** * 校驗權(quán)限 */ if (sessionUserDto != null) { validatePermission(interceptor, sessionUserDto); } /** * 校驗參數(shù) */ validateParams(interceptor, m, arguments); /** * 獲取描述信息,這里在執(zhí)行方法之前組織好描述信息,當(dāng)參數(shù)是傳值引用時 執(zhí)行具體方法后,會改變原始參數(shù)對象值 */ String description = getDescription(arguments, interceptor.opDescription()); /** * 執(zhí)行操作 */ obj = point.proceed(); /** * 記錄日志 */ wirteLog(obj, description, interceptor, sessionUserDto); } catch (BusinessException e) { logger.error("全局攔截器異常", e); throw e; } catch (Exception e) { logger.error("全局攔截器異常", e); } catch (Throwable e) { logger.error("全局攔截器異常", e); } return obj; } }
這里只貼出了部分代碼,完整的代碼可以到這里獲取
總結(jié)
回到最開始的問題,這樣我們實現(xiàn)了一個對原有系統(tǒng)侵入極小,然后又實現(xiàn)了操作日志的解決方案。 我們使用spring的aop非常簡單,我們使用aop結(jié)合反射,可以做很多事情。aop對代碼的侵入非常小,不需要動原來的代碼,只需要在原有的方法上加一個注解就可以完成對系統(tǒng)的改造,加權(quán)限,加日志 等等一系列操作。
到此這篇關(guān)于springAop實現(xiàn)權(quán)限管理數(shù)據(jù)校驗操作日志的場景分析的文章就介紹到這了,更多相關(guān)springAop權(quán)限管理數(shù)據(jù)校驗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring boot整合redis主從sentinel方式
這篇文章主要介紹了spring boot整合redis主從sentinel方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03Mybatis Criteria使用and和or進行聯(lián)合條件查詢的操作方法
這篇文章主要介紹了Mybatis Criteria的and和or進行聯(lián)合條件查詢的方法,本文通過例子給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2021-10-10SpringMVC中轉(zhuǎn)發(fā)與重定向的區(qū)別淺析
這篇文章主要給大家介紹了關(guān)于SpringMVC中轉(zhuǎn)發(fā)與重定向的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12