一个Web项目,想做一个默认通用接口的功能。父类BaseController有默认的对外接口(/page,/list,/detail,/update等)以及默认的实现。已经实现,(继承shiro,需要接口访问权限功能)但是shiro的授权(@RequiresPermissions)不能自定义了。想着利用Spring的BeanPostProcessor给ControllerBean的API方法的授权做动态修改。但实际并不能如此只能另寻他法了。
//父类定义默认对外接口,以及方法实现 public class BaseController<T extends BaseService,M extends BaseModel> { @GetMapping("/page") @RequiresPermissions(":read") public Object defaultPage(ModelMap modelMap, @RequestParam Map<String, Object> param) { service.beforePage(param); return setSuccessModelMap(modelMap, service.getPage(param,service.pageHandler())); } @GetMapping("/list") @RequiresPermissions(":read") public Object defaultList(ModelMap modelMap, @RequestParam Map<String, Object> param) { service.beforeList(param); return setSuccessModelMap(modelMap, service.getList(param,service.listHandler())); } @GetMapping("/detail") @RequiresPermissions(":read") public Object defaultDetail(ModelMap modelMap, Long id) { service.beforeDetail(id); M t= (M) service.getById(id); return setSuccessModelMap(modelMap,service.afterDetail(t)); } @PostMapping("/update") @RequiresPermissions(":update") public Object defaultUpdate(ModelMap modelMap, M param) { if(param==null||param.getId()==null){ service.beforeAdd(param); M t= (M) service.update(param); return setSuccessModelMap(modelMap, service.afterAdd(t)); }else{ service.beforeUpdate(param); M t= (M)service.update(param); return setSuccessModelMap(modelMap, service.afterUpdate(t)); } } @DeleteMapping("/delete") @RequiresPermissions(":del") public Object defaultDelete(ModelMap modelMap, Long id) { return setSuccessModelMap(modelMap, service.delete(id)); } @DeleteMapping("/del") @RequiresPermissions(":del") public Object defaultDel(ModelMap modelMap, Long id) { return setSuccessModelMap(modelMap, service.del(id)); } @DeleteMapping("/deletes") @RequiresPermissions(":del") public Object defaultDeletes(ModelMap modelMap, String ids) { service.deletes(ids); return setSuccessModelMap(modelMap); } } //授权动态修改 @Component public class DefaultRequiresPermissionPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { Class clazz=bean.getClass(); MController mapping= (MController) clazz.getAnnotation(MController.class); if(mapping!=null){ String[] roots=mapping.value(); if(roots.length>0){ String rootUrl= Stream.of(roots).collect(Collectors.joining()).substring(1); List<String> defaultMethods= Arrays.asList("defaultPage","defaultList","defaultDetail","defaultUpdate","defaultDelete","defaultDel","defaultDeletes"); Stream.of(clazz.getMethods()) .filter(method -> defaultMethods.contains(method.getName())) .forEach(method -> { RequiresPermissions pageAnnotation= method.getAnnotation(RequiresPermissions.class); InvocationHandler pageHandler= Proxy.getInvocationHandler(pageAnnotation); try { Field pageField = pageHandler.getClass().getDeclaredField("memberValues"); pageField.setAccessible(true); Map pageMap= (Map) pageField.get(pageHandler); String[] pageValues= (String[]) pageMap.get("value"); pageValues[0]=rootUrl+pageValues[0]; } catch (Exception e) { e.printStackTrace(); } }); } } return bean; } } //自定义注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @RestController @RequestMapping public @interface MController { @AliasFor(annotation = RequestMapping.class) String[] value() default {}; //String[] exclude() default {}; }实验中授权确实修改了,但发现所有的Controller的默认接口的权限都是同一个,意识到其实修改的自始至终都是父类方法中的注解。如是做了以下测试:
//注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Temp { String value(); } //父类 public class Animal{ @Temp("animal") public void eat(){} } //子类A public class Dog extends Animal { } //子类B public class Cat extends Animal { } //临时工具方法 //获取对象某方法上某注解的value值 public static <T extends Annotation> String getAnnotationValue(Object obj, Class<T> annotationType, String methodName) throws Exception { Method method=obj.getClass().getMethod(methodName); T annotation=method.getAnnotation(annotationType); InvocationHandler handler=Proxy.getInvocationHandler(annotation); Field field=handler.getClass().getDeclaredField("memberValues"); field.setAccessible(true); Map map= (Map) field.get(handler); Object value=map.get("value"); if(value==null)return null; return value.toString(); } //设置对象某方法上某注解的value值 public static <T extends Annotation> void setAnnotationValue(Object obj,Class<T> annotationType,String methodName,String value)throws Exception{ Method method=obj.getClass().getMethod(methodName); T annotation=method.getAnnotation(annotationType); InvocationHandler handler=Proxy.getInvocationHandler(annotation); Field field=handler.getClass().getDeclaredField("memberValues"); field.setAccessible(true); Map map= (Map) field.get(handler); map.put("value",value); } //测试方法 @Test public void test(){ Dog dog=new Dog(); Cat cat=new Cat(); System.out.println("dog value:"+getAnnotationValue(dog,Temp.class,"eat")); System.out.println("cat value:"+getAnnotationValue(cat,Temp.class,"eat")); setAnnotationValue(dog,Temp.class,"eat","dog"); System.out.println("dog value:"+getAnnotationValue(dog,Temp.class,"eat")); System.out.println("cat value:"+getAnnotationValue(cat,Temp.class,"eat")); } //结果 dog value:animal cat value:animal dog value:dog cat value:dog //A类重写父类方法 public class Dog extends Animal { @Override @Temp("animal") public void eat() { super.eat(); } } //结果 dog value:animal cat value:animal dog value:dog cat value:animal父类方法的注解有默认值,该类有多个子类。其中一个子类修改了该注解的值,则所有其他子类该方法的值也发生变化,子类不重写的前提下。已通过自定义注解实现