1. 程式人生 > 程式設計 >SpringBoot如何通過自定義註解實現許可權檢查詳解

SpringBoot如何通過自定義註解實現許可權檢查詳解

前言

最近開發了一個介面,完成後準備自測時,卻被攔截器攔截了,提示:(AUTH-NO)未能獲得有效的請求引數!怎麼會這樣呢?

於是我全域性搜了這個提示語,結果發現它被出現在一個Aspect類當中了,並且把一個 @interface 作為了一個切點,原來這裡利用了Spring AOP面向切面的方式進行許可權控制。

SpringBoot通過自定義註解實現日誌列印可參考:SpringBoot通過自定義註解實現日誌列印

正文

Spring AOP

Spring AOP 即面向切面,是對OOP面向物件的一種延伸。

AOP機制可以讓開發者把業務流程中的通用功能抽取出來,單獨編寫功能程式碼。在業務流程執行過程中,Spring框架會根據業務流程要求,自動把獨立編寫的功能程式碼切入到流程的合適位置。

我們通過AOP機制可以實現:Authentication 許可權檢查、Caching 快取、Context passing 內容傳遞、Error handling 錯誤處理、日誌列印等功能,這裡我們講一下怎麼用Spring AOP來實現許可權檢查。

SpringBoot 通過自定義註解實現許可權檢查

Maven依賴

<!--lombok-->
<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <version>1.18.2</version>
 <optional>true</optional>
</dependency>

<!--Spring AOP-->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

MyPermissionTag.class自定義註解

  • @Retention: 用來修飾註解,是註解的註解,稱為元註解。
  • @Target:用來說明物件的作用範圍
/**
 * 使用者請求許可權校驗
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyPermissionTag {
 String value() default "";
 String name() default "";
}

這裡特別講一下@Retention,按生命週期來劃分可分為3類:

  • RetentionPolicy.SOURCE:註解只保留在原始檔,當Java檔案編譯成class檔案的時候,註解被遺棄(執行時去動態獲取註解資訊);
  • RetentionPolicy.CLASS:註解被保留到class檔案,但jvm載入class檔案時候被遺棄,這是預設的生命週期(在編譯時進行一些預處理操作);
  • RetentionPolicy.RUNTIME:註解不僅被儲存到class檔案中,jvm載入class檔案之後,仍然存在(做一些檢查性的操作);

這3個生命週期分別對應於:Java原始檔(.java檔案) —> .class檔案 —> 記憶體中的位元組碼。

AuthInterceptor 許可權檢查的切面

這裡簡單介紹一下,切面的執行方法和其執行順序:

  • @Around 通知方法將目標方法封裝起來
  • @Before 通知方法會在目標方法呼叫之前執行
  • @After 通知方法會在目標方法返回或者異常後執行
  • @AfterReturning 通知方法會在目標方法返回時執行
  • @Afterthrowing 通知方法會在目標方法丟擲異常時執行

這裡以一個返回正常的情況為例:(異常替換最後一步即可)

SpringBoot如何通過自定義註解實現許可權檢查詳解

AuthInterceptor.class

注意要在啟動類掃描這個class,並且新增 @EnableAspectJAutoProxy(proxyTargetClass = true)

@Slf4j
@Aspect
@Component
public class AuthInterceptor {


 /**
 * 引數處理
 *
 * @param point
 */
 @Before("@annotation(com.luo.common.tag.MyPermissionTag)")
 public void beforeProReq(JoinPoint point) {
 log.info("前置攔截-開始");
 Request req = getOperationRequest(point.getArgs());
 if (req != null) {
  //解密帳號
  log.info("前置攔截-開始解密ACCOUNT:{}",req.getAccount());


  log.info("前置攔截-結束解密ACCOUNT:{}",req.getAccount());
 }
 log.info("前置攔截-結束");
 }


 @Around("@annotation(com.luo.common.tag.MyPermissionTag)")
 public Object authCheck(ProceedingJoinPoint pjp) throws Throwable {
 log.info("許可權攔截-開始");
 //請求方法
 ReqMethod reqMethod = getPermissionTag(pjp);


 MyPermissionTag myPermissionTag =reqMethod.perTag;
 log.info(myPermissionTag.value()); //獲取配置的值
 log.info("許可權攔截-開始-攔截到方法:{}",reqMethod.getMethodName());


 if("true".equals(myPermissionTag.value().toString())){
  //錯誤返回
  Response notGoRes = new Response();
  Request req = getOperationRequest(pjp.getArgs());
  // 校驗請求物件
  if (req == null) {
  notGoRes.setErrorMsg("(AUTH)未能獲得有效的請求引數!");
  log.info("(AUTH-NO)未能獲得有效的請求引數!");
  return notGoRes;
  }else {//可以在這裡根據請求引數對請求做進一步校驗


  log.info("完成請求校驗:"+req);




  }
 }else {
  log.info("未開啟許可權校驗");
 }


 return pjp.proceed();
 }




 /**
 * 獲取 request 介面中的請求引數
 * @param args
 * @return
 */
 private Request getOperationRequest(Object[] args) {
 if (args == null || args.length <= 0) {
  log.error("AUTH許可權驗證:攔截方法的請求引數為空!");
  return null;
 }
 Object obj = args[0];
 if (obj instanceof Request) {
  log.info("AUTH許可權驗證:請求物件為正確的OperationRequest物件");
  return (Request) obj;
 }
 return null;
 }




 /**
 * 獲取攔截的資源標籤
 * 這裡可以獲取方法名+註解資訊(包括 key+value 等)
 * @param pjp
 * @return
 * @throws SecurityException
 * @throws NoSuchMethodException
 */
 private ReqMethod getPermissionTag(ProceedingJoinPoint pjp) throws NoSuchMethodException,SecurityException {
 Signature signature = pjp.getSignature();
 MethodSignature methodSignature = (MethodSignature) signature;
 Method targetMethod = methodSignature.getMethod();
 Method realMethod = pjp.getTarget().getClass().getDeclaredMethod(signature.getName(),targetMethod.getParameterTypes());
 MyPermissionTag permissionTag = realMethod.getAnnotation(MyPermissionTag.class);
 return new ReqMethod(permissionTag,realMethod.getName());
 }


 @Setter
 @Getter
 class ReqMethod {


 private MyPermissionTag perTag;
 private String methodName;


 public ReqMethod(MyPermissionTag perTag,String methodName) {
  this.perTag = perTag;
  this.methodName = methodName;
 }


 }
}

驗證

測試介面

@PostMapping("/helloluo")
@MyPermissionTag(value = "true")
public String helloluo(UserPojoReq userPojoReq){
 return "Hello World";
}

傳送請求

SpringBoot如何通過自定義註解實現許可權檢查詳解

驗證

SpringBoot如何通過自定義註解實現許可權檢查詳解

總結

到此這篇關於SpringBoot如何通過自定義註解實現許可權檢查的文章就介紹到這了,更多相關SpringBoot自定義註解實現許可權檢查內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!