自己實現註解式權限校驗(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() {
}