自己實現註解式權限校驗(SpringBoot)

自己實現註解式權限校驗(SpringBoot)

權限校驗是很多情況都會用到的,結合Java註解和攔截器,直接在Controller層的方法上添加一個註解,可以無侵入式的進行權限校驗。

一.Java註解

1.RequestMapping

我們打開一個最常用的Spring註解

在這裏插入圖片描述
可以看到,RequestMapping註解上,還有幾個註解,分別代表

Target:註解目標(如:可以在方法、類、參數中使用)

Retention:是註解的註解,稱爲元註解。 分爲3類 :

  • RetentionPolicy.SOURCE:註解只保留在源文件,當Java文件編譯成class文件的時候,註解被遺棄;
  • RetentionPolicy.CLASS:註解被保留到class文件,但jvm加載class文件時候被遺棄,這是默認的生命週期;
  • RetentionPolicy.RUNTIME:註解不僅被保存到class文件中,jvm加載class文件之後,仍然存在;

Documented:表明這個註釋是由 javadoc記錄的。

Mapping:Spring的註解,不做過多說明

二.自定義一個判斷權限的註解

1.Permission.java

在瞭解了註解的組成後,我們可以嘗試着,自己編寫一個註解。用來判斷權限。

注意:此註解使用了Spring的AliasFor註解,改註解成對出現,表示雙方互爲別名屬性。(既設置了name,則value同樣有該值),使用AliasFor註解後,必須用Spring的AnnotationUtils工具獲取註解纔可以達到上述效果。

import org.springframework.core.annotation.AliasFor;

import java.lang.annotation.*;

/**
 * @author litong
 * @date 2019/11/29 16:18
 */

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface Permission {

	/**
	 * 權限
	 */
	@AliasFor("value")
	PermissionEnum[] name() default {};

	/**
	 * 權限
	 */
	@AliasFor("name")
	PermissionEnum[] value() default {};

}

2.PermissionEnum.java

權限的枚舉,使用枚舉可以更好的說明參數,易讀性高,且可以減少報錯機率。

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * @author litong
 * @date 2019/11/29 16:20
 */
@Getter
@AllArgsConstructor
public enum PermissionEnum {
   /**
    * 用戶管理權限
    */
   USER(1, "用戶管理權限"),
   /**
    * 教師管理權限
    */
   TEACHER(2, "教師管理權限"),

   /**
    * 無需校驗,
    */
   NO(-99999, "無需權限"),
   ;
   /**
    * 權限編碼
    */
   private Integer code;
   /**
    * 權限名稱
    */
   private String msg;
}

三.自定義攔截器

注:此攔截器,一般還會有未登錄攔截,Token解析,用戶注入、權限校驗等功能,現只展示其中之一,權限校驗。

1.AuthenticationInterceptor.java

/**
 * @Author: litong
 * @Date: 2019-09-20 11:50
 * @Description: 攔截器
 */
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
    /**
	 * 在業務處理器處理請求之前被調用
	 *
	 * @param request
	 * @param response
	 * @param handler
	 * @return
	 * @throws Exception
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		// 如果不是映射到方法直接通過
		if (!(handler instanceof HandlerMethod)) {
			return true;
		}
         // 獲取方法中的註解
         HandlerMethod handlerMethod = (HandlerMethod) handler;
		Method method = handlerMethod.getMethod();
        // 省略判斷是否需要登錄的方法.....
        // 省略Token解析的方法.....
        // 此處根據自己的系統架構,通過Token或Cookie等獲取用戶信息。
        UserInfo userInfo = userService.getUserByToken(token);
        
        // 獲取類註解
		Permission permissionClass = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Permission.class);
		// 獲取方法註解
		Permission permissionMethod = AnnotationUtils.findAnnotation(method,Permission.class);
		// 判斷是否需要權限校驗
		if (permissionClass == null && permissionMethod == null) {
			// 不需要校驗權限,直接放行
			return true;
		}
        // 獲取該方法註解,優先級:方法註解>類註解
		PermissionEnum[] permissionEnums;
		if (permissionClass != null && permissionMethod == null) {
			// 類註解不爲空,方法註解爲空,使用類註解
			permissionEnums = permissionClass.name();
		} else if (permissionClass == null) {
			// 類註解爲空,使用方法註解
			permissionEnums = permissionMethod.name();
		} else {
			// 都不爲空,使用方法註解
			permissionEnums = permissionMethod.name();
		}
        // 校驗該用戶是否有改權限
        // 校驗方法可自行實現,拿到permissionEnums中的參數進行比較
        if(userService.checkPermissionForUser(userInfo,permissionEnums)){
            // 擁有權限
            return true;
        } else {
            // 拋出自定義異常,可在全局異常捕獲後自行處理。
            throw new AuthTokenException(CheckConstants.PERMISSION_ERROR);
        }

    }
    
    /**
	 * 請求處理之後進行調用,但是在視圖被渲染之前(Controller方法調用之後)
	 *
	 * @param httpServletRequest
	 * @param httpServletResponse
	 * @param o
	 * @param modelAndView
	 * @throws Exception
	 */
	@Override
	public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

	}

	/**
	 * 在整個請求結束之後被調用,也就是在DispatcherServlet 渲染了對應的視圖之後執行(主要是用於進行資源清理工作)
	 *
	 * @param httpServletRequest
	 * @param httpServletResponse
	 * @param o
	 * @param e
	 * @throws Exception
	 */
	@Override
	public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
	}
}

四.使用

1.類(該類中,所有方法校驗權限)

/**
 * @author litong
 * @date 2019/9/20 13:26
 */
@RestController
@RequestMapping("/auth")
@AllArgsConstructor
@Slf4j
@Permission({PermissionEnum.USER})
public class AuthController {
    
}

2.方法(此方法校驗權限,優先級大於類上的註解)

@PostMapping("/get")
@Permission({PermissionEnum.USER})
public R get() {
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章