使用Spring实现权限控制动态为注解赋值
📅 2026/7/28 16:48:43
👁️ 阅读次数
📝 编程学习
首先这不是一个介绍或者使用SpringSecurity的博客。他是使用自定义注解和拦截器实现的权限管理(只供学习不可用于生产环境)
技术栈:
SpringBoot 2.1.6
MySQL5.7
大体思路:
- 使用拦截器拦截请求,在拦截器中使用 HandlerMethod 类获取当前请求方法上的自定义权限注解。判断是否有此访问权限。
- 动态改变注解值(实际生产环境中不同角色拥有的权限不可能一直不发生改变)
首先实现第一步:
创建两个注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public@interfaceMyClass{// 放置在类上}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public@interfaceMyTest{String[]v()default{"-1"};// 防止在方法上// v() 中保存的是权限ID 默认-1 即没有任何角色可以访问}编写一个Controller并添加注解
@RestController@RequestMapping("/myController")@MyClasspublicclassMyController{@RequestMapping("/get")@MyTest(v={"1","2","3"})// 赋值上角色ID为 1,2,3才能访问publicStringget(){return"Hello,world";}@RequestMapping("/set")@MyTestpublicStringset(){return"Hello,world";}}编写拦截器(不要忘记使用这个拦截器)
packagecom.annie.interceptor;importcom.alibaba.fastjson.JSON;importcom.annie.annotation.MyTest;importorg.springframework.stereotype.Component;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.HandlerInterceptor;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.lang.reflect.Method;@ComponentpublicclassMyInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{HandlerMethod handlerMethod=(HandlerMethod)handler;Method method=handlerMethod.getMethod();MyTest annotation=method.getAnnotation(MyTest.class);if(annotation!=null){// 从 request 或者 session 中获取当前请求角色String roleId="1";// 假定请求角色的权限ID为1String[]v=annotation.v();// 获取权限for(String s:v){if(s.equals(roleId)){// 判断是否拥有访问权限returntrue;}}returnfalse;}returnfalse;}}到此一个简单的权限判断就算是完成了但是我们不能预知一个方法可以使用的所有角色。所以我们要在使用一个方法让这个注解可以动态赋值。因为我们使用的是Spring框架,Spring在项目启动初期就将所有的Bean收集起来了。我们这要拿到那个容器就可以拿到当前项目中所有方法。在通过去数据库类比。为每个自定义注解动态设置值。
packagecom.annie.task;importcom.alibaba.fastjson.JSON;importcom.annie.annotation.MyClass;importcom.annie.annotation.MyTest;importorg.springframework.beans.BeansException;importorg.springframework.context.ApplicationContext;importorg.springframework.context.ApplicationContextAware;importorg.springframework.scheduling.annotation.Scheduled;importorg.springframework.stereotype.Component;importjava.lang.reflect.*;importjava.util.Map;@ComponentpublicclassMyTaskimplementsApplicationContextAware{privateApplicationContext applicationContext;// 保存所有Bean的容器@Scheduled(fixedRate=1000)publicvoidtask01()throwsClassNotFoundException,NoSuchFieldException,IllegalAccessException{Map<String,Object>beansWithAnnotation=this.applicationContext.getBeansWithAnnotation(MyClass.class);for(String key:beansWithAnnotation.keySet()){// 反射获取类,这里有一个问题。我在使用 Swagger2 时添加了注解。用于Swagger2使用了动态代理我拿不到注解数据。// 于是这个我提供一个备用方案。 获取这个类的名称然后 通过 Class.forName() 获取注解值Object testController=beansWithAnnotation.get(key);Class<?>aClass=testController.getClass();Method[]methods=aClass.getDeclaredMethods();for(Method m:methods){MyTest annotation=m.getAnnotation(MyTest.class);if(annotation==null){continue;}// 获取代理处理器InvocationHandler invocationHandler=Proxy.getInvocationHandler(annotation);// 过去私有 memberValues 属性Field f=invocationHandler.getClass().getDeclaredField("memberValues");f.setAccessible(true);// 获取实例的属性mapMap<String,Object>memberValues=(Map<String,Object>)f.get(invocationHandler);// 修改属性值String arr[]={"1","2"};memberValues.put("v",arr);System.out.println(JSON.toJSONString(annotation.v()));}}}@OverridepublicvoidsetApplicationContext(ApplicationContext applicationContext)throwsBeansException{this.applicationContext=applicationContext;}}我这里懒得获取数据库中的值进行类比。直接写的死值。
项目下载:下载
扩展内容
github
个人博客
编程学习
技术分享
实战经验