Spring自定義註解+AOP實現權限控制

前言

做業系統在測試過程當中出現了學生修改路由能夠到達教師界面而且可使用教師功能的問題,學生不用任何工具就能夠修改本身的成績,真的挺要命的,這就要用權限管理進行控制了。
因爲沒有接觸過權限管理,因此一開始也是有點懵,後來應用到實踐中,發現也還能夠吧。java

自定義註解@Admin

若是在實現方式上去描述自定義註解,其實就是接口+註解編程

package club.yunzhi.workhome.annotation;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Admin {

    String value() default "";
}
@TARGET
  • 用於標註這個註解放在什麼地方,類上,方法上,構造器上
  • ElementType.METHOD 用於描述方法
  • ElementType.FIELD 用於描述成員變量,對象,屬性(包括enum實例)
  • ElementType.LOCAL_VARIABLE 用於描述局部變量
  • ElementType.CONSTRUCTOR 用於描述構造器
  • ElementType.PACKAGE 用於描述包
  • ElementType.PARAMETER 用於描述參數
  • ElementType.TYPE 用於描述類,接口,包括(包括註解類型)或enum聲明
@Retention
  • 用於說明這個註解的生命週期安全

    • RetentionPolicy.RUNTIME 始終不會丟棄,運行期也保留該註解。所以可使用反射機制來讀取該註解信息。
    • 咱們自定義的註解一般用這種方式
    • RetentionPolicy.CLASS 在類加載的時候丟棄,在字節碼文件的處理中有用。註解默認使用這種方式
    • RetentionPolicy.SOURCE 在編譯階段丟棄,這些註解在編譯結束後就再也不有任何意義,因此他們不會寫入字節碼中
    • @Override,@SuppressWarnings都屬於這類註解。
    • 咱們自定義使用中通常使用第一種
    • java過程爲 編譯-加載-運行
@Documented
  • 將註解信息添加到文本中

這樣就有了一個自定義註解,可是要想定義它的做用,就須要AOP了。app

AOP

基本概念
  • AOP(Aspect Oriented Programming)稱爲面向切面編程,在程序開發中主要用來解決一些系統層面上的問題,好比日誌,事務,權限等待,Struts2的攔截器設計就是基於AOP的思想,是個比較經典的例子。
  • 在不改變原有的邏輯的基礎上,增長一些額外的功能。代理也是這個功能,讀寫分離也能用aop來作。
  • AOP能夠說是OOP(Object Oriented Programming,面向對象編程)的補充和完善。OOP引入封裝、繼承、多態等概念來創建一種對象層次結構,用於模擬公共行爲的一個集合。不過OOP容許開發者定義縱向的關係,但並不適合定義橫向的關係,例如日誌功能。日誌代碼每每橫向地散佈在全部對象層次中,而與它對應的對象的核心功能毫無關係對於其餘類型的代碼,如安全性、異常處理和透明的持續性也都是如此,這種散佈在各處的無關的代碼被稱爲橫切(cross cutting),在OOP設計中,它致使了大量代碼的重複,而不利於各個模塊的重用。AOP技術偏偏相反,它利用一種稱爲"橫切"的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其命名爲"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減小系統的重複代碼,下降模塊之間的耦合度,並有利於將來的可操做性和可維護性。
  • 使用"橫切"技術,AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特色是,他們常常發生在覈心關注點的多處,而各處基本類似,好比權限認證、日誌、事物。AOP的做用在於分離系統中的各類關注點,將核心關注點和橫切關注點分離開來。
相關概念
  • 橫切關注點:對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之爲橫切關注點
  • Aspect(切面):一般是一個類,裏面能夠定義切入點和通知
  • JointPoint(鏈接點):程序執行過程當中明確的點,通常是方法的調用。被攔截到的點,由於Spring只支持方法類型的鏈接點,因此在Spring中鏈接點指的就是被攔截到的方法,實際上鍊接點還能夠是字段或者構造器
  • Advice(通知):AOP在特定的切入點上執行的加強處理,有before(前置),after(後置),afterReturning(最終),afterThrowing(異常),around(環繞)
  • Pointcut(切入點):就是帶有通知的鏈接點,在程序中主要體現爲書寫切入點表達式
  • weave(織入):將切面應用到目標對象並致使代理對象建立的過程
  • introduction(引入):在不修改代碼的前提下,引入能夠在運行期爲類動態地添加一些方法或字段
  • AOP代理(AOP Proxy):AOP框架建立的對象,代理就是目標對象的增強。Spring中的AOP代理可使JDK動態代理,也能夠是CGLIB代理,前者基於接口,後者基於子類
  • 目標對象(Target Object): 包含鏈接點的對象。也被稱做被通知或被代理對象。
通知類型
  • Before:在目標方法被調用以前作加強處理,@Before只須要指定切入點表達式便可
  • AfterReturning:在目標方法正常完成後作加強,@AfterReturning除了指定切入點表達式後,還能夠指定一個返回值形參名returning,表明目標方法的返回值
  • AfterThrowing:主要用來處理程序中未處理的異常,@AfterThrowing除了指定切入點表達式後,還能夠指定一個throwing的返回值形參名,能夠經過該形參名來訪問目標方法中所拋出的異常對象
  • After:在目標方法完成以後作加強,不管目標方法時候成功完成。@After能夠指定一個切入點表達式
  • Around:環繞通知,在目標方法完成先後作加強處理,環繞通知是最重要的通知類型,像事務,日誌等都是環繞通知,注意編程中核心是一個ProceedingJoinPoint

因爲初次接觸AOP,對部分概念還不太理解,也就不展開解釋了,之後有機會再寫吧。框架

@Aspect
@Component
public class AdminAspect {

    private static final Logger logger = LoggerFactory.getLogger(AdminAspect.class);

    @Autowired
    WorkService workService;
    @Pointcut(value = "@annotation(club.yunzhi.workhome.annotation.Admin)")
    public void annotationPointCut() {

    }
    @Before("annotationPointCut()")
    public Object doBefore(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getMethod().getName();
        System.out.println("方法名:" + methodName);

        if(!validate()){
           throw new AccessDeniedException("無操做權限");
        }
        try {
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            return null;
        }
    }
    private boolean validate(){
        System.out.println(this.workService.isTeacher());
      return this.workService.isTeacher();
    }
}
@Aspect : 將當前類標識爲一個切面
@Component :讓Spring容器掃描到。
@Pointcut :定義切點

這樣一來自定義註解就有了靈魂了,驗證到當前角色不是教師,那就拋出異常,不然執行加上註解的方法ide

以編輯學生爲例:工具

/**
     * 更新學生信息
     * @param id
     * @param student
     */
    @PutMapping("{id}")
    @Admin
    @JsonView(studentJsonView.class)
    public void update(@PathVariable Long id, @RequestBody Student student) {
        studentService.update(id, student);
    }

教師:
Peek 2020-05-02 09-46.gif
學生:
Peek 2020-05-02 09-49.gif測試

總結:

一開始感受挺難的,後來實踐了才發現還能夠,仍是不能眼高手低,認爲難的不必定難,總會有解決的辦法的。this

相關文章
相關標籤/搜索