本文主要介紹利用注解方式實(shí)現(xiàn)權(quán)限過(guò)濾,基于Spring Boot項(xiàng)目,這里權(quán)限是指不同平臺(tái)的賬號(hào)訪問(wèn)同一項(xiàng)目,從而帶來(lái)的問(wèn)題,而非業(yè)務(wù)權(quán)限。例如:在一個(gè)項(xiàng)目中,有C端用戶和M端管理用戶,接口有些通用,有些不通用,如果不加以控制則會(huì)出現(xiàn)接口垮平臺(tái)調(diào)用,C端可以調(diào)用M端接口這種不可控情況,廢話少說(shuō)直接進(jìn)入主題。
自定義注解
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Authority {
Platform[] value(); // 那些平臺(tái)能夠訪問(wèn)
}
- 首先自定義個(gè)注解,名字可以隨便起,本文注解名稱 @Authority 意思為權(quán)限控制,該注解主要打在Controller 中對(duì)外暴露的方法中。
@RestController
@RequestMapping("/client_b_user")
public class ClientBUserController {
@Authority(value={Platform.B})
@GetMapping("/getUserInfo")
public UserInfo getUserInfo(){
......
}
}
Value[] 為一個(gè)枚舉類,主要是定義本項(xiàng)目中又那些用戶群體,例如 Platform.B 與 Platform.C 代表B端用戶與C端用戶。
在編碼的過(guò)程中就定義好Controller中方法可以由那些平臺(tái)或者用戶群體訪問(wèn)。
監(jiān)聽(tīng)Spring Bean的創(chuàng)建
通過(guò)實(shí)現(xiàn)BeanPostProcessor可以達(dá)到監(jiān)聽(tīng)SpringBean創(chuàng)建
這里我們可以通過(guò)判斷創(chuàng)建的Bean是否為 @Controller 或者 @RestController
如果 Bean 對(duì)象問(wèn) @Controller 或者 @RestController 那么則監(jiān)聽(tīng)掃描其方法是否有 @Authority 注解
@Component
public class AuthorityControllerBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class clazz = bean.getClass();
Boolean isControllerAnnotationInClass = clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(RestController.class);
if (isControllerAnnotationInClass) {
// 如果這是一個(gè) Controller Bean 的話
// 那么就讀取所有方法的權(quán)限配置
Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass());
for (Method method : methods) {
Boolean isAuthorityAnnotationInMethod = method.isAnnotationPresent(Authority.class);
String target= bean.getClass().getName() + "." + method.getName();
if (isAuthorityAnnotationInMethod) {
// 如果有 @Authority 注解的話
// 則記錄該注解中的內(nèi)容
Authority authority = method.getAnnotation(Authority.class);
AuthorityAnnotationContainer.addPlatformConfiguration(target,authority);
}
}
return bean;
} else {
// 否則的話直接返回Bean
return bean;
}
}
- AuthorityAnnotationContainer 實(shí)質(zhì)為一個(gè)Map 記錄著那些方法是打上了 @Authority 注解的,Key 為方法名,Value為注解的內(nèi)容,本文不再提供代碼。
實(shí)現(xiàn)攔截
注解打完之后,最終還是需要攔截器進(jìn)行攔截。
定義攔截器
@Component
public class AuthorityInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 將handler強(qiáng)轉(zhuǎn)為HandlerMethod, 前面已經(jīng)證實(shí)這個(gè)handler就是HandlerMethod
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 從方法處理器中獲取出要調(diào)用的方法
Method method = handlerMethod.getMethod();
String target = handlerMethod.getBean().getClass().getName() + "." + method.getName();
Authority authority = AuthorityAnnotationContainer .getAuthority(target);
Boolean isSuccess = false; // 是否能夠訪問(wèn)
if(authority != null){
// 這里進(jìn)行條件判斷,
// 本項(xiàng)目中的做法是,取得用戶信息,然后判斷用戶是哪個(gè)平臺(tái)的,在決定是否能夠訪問(wèn)。
// 如果能夠訪問(wèn) isSuccess = true;
}
return isAuthority;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
總結(jié)
本文的重點(diǎn)是對(duì)于 BeanPostProcessor 的使用,實(shí)現(xiàn) BeanPostProcessor 即可在創(chuàng)建Bean的時(shí)候增加一個(gè)監(jiān)聽(tīng)器,做一些對(duì)應(yīng)的操作。