本文主要介绍利用注解方式实现权限过滤,基于Spring Boot项目,这里权限是指不同平台的账号访问同一项目,从而带来的问题,而非业务权限。例如:在一个项目中,有C端用户和M端管理用户,接口有些通用,有些不通用,如果不加以控制则会出现接口垮平台调用,C端可以调用M端接口这种不可控情况,废话少说直接进入主题。
自定义注解
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Authority {
Platform[] value(); // 那些平台能够访问
}
- 首先自定义个注解,名字可以随便起,本文注解名称 @Authority 意思为权限控制,该注解主要打在Controller 中对外暴露的方法中。
@RestController
@RequestMapping("/client_b_user")
public class ClientBUserController {
@Authority(value={Platform.B})
@GetMapping("/getUserInfo")
public UserInfo getUserInfo(){
......
}
}
Value[] 为一个枚举类,主要是定义本项目中又那些用户群体,例如 Platform.B 与 Platform.C 代表B端用户与C端用户。
在编码的过程中就定义好Controller中方法可以由那些平台或者用户群体访问。
监听Spring Bean的创建
通过实现BeanPostProcessor可以达到监听SpringBean创建
这里我们可以通过判断创建的Bean是否为 @Controller 或者 @RestController
如果 Bean 对象问 @Controller 或者 @RestController 那么则监听扫描其方法是否有 @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) {
// 如果这是一个 Controller Bean 的话
// 那么就读取所有方法的权限配置
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 注解的话
// 则记录该注解中的内容
Authority authority = method.getAnnotation(Authority.class);
AuthorityAnnotationContainer.addPlatformConfiguration(target,authority);
}
}
return bean;
} else {
// 否则的话直接返回Bean
return bean;
}
}
- AuthorityAnnotationContainer 实质为一个Map 记录着那些方法是打上了 @Authority 注解的,Key 为方法名,Value为注解的内容,本文不再提供代码。
实现拦截
注解打完之后,最终还是需要拦截器进行拦截。
定义拦截器
@Component
public class AuthorityInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 将handler强转为HandlerMethod, 前面已经证实这个handler就是HandlerMethod
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 从方法处理器中获取出要调用的方法
Method method = handlerMethod.getMethod();
String target = handlerMethod.getBean().getClass().getName() + "." + method.getName();
Authority authority = AuthorityAnnotationContainer .getAuthority(target);
Boolean isSuccess = false; // 是否能够访问
if(authority != null){
// 这里进行条件判断,
// 本项目中的做法是,取得用户信息,然后判断用户是哪个平台的,在决定是否能够访问。
// 如果能够访问 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 {
}
}
总结
本文的重点是对于 BeanPostProcessor 的使用,实现 BeanPostProcessor 即可在创建Bean的时候增加一个监听器,做一些对应的操作。