From 8689848fb173c09c8063e9cd5cb84d5b7437cad1 Mon Sep 17 00:00:00 2001 From: wuy <1311695042@qq.com> Date: Fri, 14 Jun 2019 23:21:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0RequiresPermissions=E4=BB=A3?= =?UTF-8?q?=E7=90=86=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bind/annotation/PermissionsMenu.java | 25 +++++++ .../annotation/RequiresPermissionsProxy.java | 47 +++++++++++++ ...ssionProxyAnnotationMethodInterceptor.java | 30 ++++++++ .../PermissionProxyAnnotationHandler.java | 69 +++++++++++++++++++ .../com/diboot/shiro/config/ShiroConfig.java | 19 +++++ .../controller/PermissionController.java | 9 ++- 6 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsMenu.java create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsProxy.java create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionProxyAnnotationMethodInterceptor.java create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionProxyAnnotationHandler.java diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsMenu.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsMenu.java new file mode 100644 index 0000000..1307e81 --- /dev/null +++ b/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsMenu.java @@ -0,0 +1,25 @@ +package com.diboot.shiro.bind.annotation; + +import java.lang.annotation.*; + +/** + * controller上注解,用于标记权限的菜单分类 + * @author : wee + * @version v2.0 + * @Date 2019-06-14 23:00 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface PermissionsMenu { + /** + * 菜单编码 + * @return + */ + String menuCode(); + /** + * 菜单名称 + * @return + */ + String menuName(); +} diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsProxy.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsProxy.java new file mode 100644 index 0000000..f2e5347 --- /dev/null +++ b/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsProxy.java @@ -0,0 +1,47 @@ +package com.diboot.shiro.bind.annotation; + +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresPermissions; + +import java.lang.annotation.*; + +/** + * @author : wee + * @version v2.0 + * @Date 2019-06-14 17:50 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +@RequiresPermissions("default") +public @interface RequiresPermissionsProxy { + + /** + * 代理 {@link RequiresPermissions#value()} + */ + String[] value(); + + /** + * 代理 {@link RequiresPermissions#logical()} + */ + Logical logical() default Logical.AND; + + /** + * 菜单编码 + * @return + */ + String menuCode(); + /** + * 菜单名称 + * @return + */ + String menuName(); + + /** + * 权限名称 + * @return + */ + String permissionName(); + + +} diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionProxyAnnotationMethodInterceptor.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionProxyAnnotationMethodInterceptor.java new file mode 100644 index 0000000..71e7d7c --- /dev/null +++ b/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionProxyAnnotationMethodInterceptor.java @@ -0,0 +1,30 @@ +package com.diboot.shiro.bind.aop; + +import com.diboot.shiro.bind.annotation.RequiresPermissionsProxy; +import com.diboot.shiro.bind.handler.PermissionProxyAnnotationHandler; +import org.apache.shiro.aop.AnnotationResolver; +import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor; + +/** + * {@link RequiresPermissionsProxy} 拦截器 + * @author : wee + * @version : v2.0 + * @Date 2019-06-14 22:19 + */ +public class PermissionProxyAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor { + /** + * Default no-argument constructor that ensures this interceptor looks for + * {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotations in a method declaration. + */ + public PermissionProxyAnnotationMethodInterceptor() { + super( new PermissionProxyAnnotationHandler() ); + } + + /** + * @param resolver + * @since 1.1 + */ + public PermissionProxyAnnotationMethodInterceptor(AnnotationResolver resolver) { + super( new PermissionProxyAnnotationHandler(), resolver); + } +} diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionProxyAnnotationHandler.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionProxyAnnotationHandler.java new file mode 100644 index 0000000..d303482 --- /dev/null +++ b/diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionProxyAnnotationHandler.java @@ -0,0 +1,69 @@ +package com.diboot.shiro.bind.handler; + +import com.diboot.shiro.bind.annotation.RequiresPermissionsProxy; +import org.apache.shiro.aop.AnnotationResolver; +import org.apache.shiro.authz.AuthorizationException; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler; +import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor; +import org.apache.shiro.authz.aop.PermissionAnnotationHandler; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.Map; + +/** + * {@link RequiresPermissionsProxy} 助手类, 参考{@link PermissionAnnotationHandler}实现 + * @author : wee + * @version : v2.0 + * @Date 2019-06-14 22:19 + */ +public class PermissionProxyAnnotationHandler extends AuthorizingAnnotationHandler { + + private final static String REQUIRES_PERMISSIONS_VALUE = "value"; + + private final static String REQUIRES_PERMISSIONS_LOGICAL = "logical"; + + private final static String JDK_MEMBER_VALUES = "memberValues"; + + /** + * 标记服务的注解 + */ + public PermissionProxyAnnotationHandler() { + super(RequiresPermissionsProxy.class); + } + + /** + * 将{@link RequiresPermissionsProxy} 代理的内容赋值给{@link RequiresPermissions} + */ + @Override + public void assertAuthorized(Annotation a) throws AuthorizationException { + if (!(a instanceof RequiresPermissionsProxy)) { + return; + } + RequiresPermissionsProxy rrAnnotation = (RequiresPermissionsProxy) a; + try { + //获取RequiresPermissionsProxy上的RequiresPermissions注解 + RequiresPermissions requiresPermissions = rrAnnotation.annotationType().getAnnotation(RequiresPermissions.class); + + InvocationHandler invocationHandler = Proxy.getInvocationHandler(requiresPermissions); + /* memberValues 为JDK中存储所有成员变量值的Map {@link AnnotationInvocationHandler#memberValues}*/ + Field jdkValue = invocationHandler.getClass().getDeclaredField(JDK_MEMBER_VALUES); + jdkValue.setAccessible(true); + /*获取RequiresPermissions对应的代理属性值*/ + Map memberValues = (Map) jdkValue.get(invocationHandler); + /*动态设置RequiresPermissions注解的内容*/ + memberValues.put(REQUIRES_PERMISSIONS_VALUE, rrAnnotation.value()); + memberValues.put(REQUIRES_PERMISSIONS_LOGICAL, rrAnnotation.logical()); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } +} diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/config/ShiroConfig.java b/diboot-shiro/src/main/java/com/diboot/shiro/config/ShiroConfig.java index 681eed0..e755168 100644 --- a/diboot-shiro/src/main/java/com/diboot/shiro/config/ShiroConfig.java +++ b/diboot-shiro/src/main/java/com/diboot/shiro/config/ShiroConfig.java @@ -1,10 +1,15 @@ package com.diboot.shiro.config; +import com.diboot.shiro.bind.aop.PermissionProxyAnnotationMethodInterceptor; import com.diboot.shiro.jwt.BaseJwtAuthenticationFilter; import com.diboot.shiro.jwt.BaseJwtRealm; +import org.apache.shiro.aop.AnnotationResolver; +import org.apache.shiro.authz.aop.*; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.aop.SpringAnnotationResolver; +import org.apache.shiro.spring.security.interceptor.AopAllianceAnnotationsAuthorizingMethodInterceptor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.filter.authc.AnonymousFilter; @@ -17,7 +22,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import javax.servlet.Filter; +import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; /*** @@ -96,6 +103,18 @@ public class ShiroConfig { @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); + AopAllianceAnnotationsAuthorizingMethodInterceptor advice = (AopAllianceAnnotationsAuthorizingMethodInterceptor)authorizationAttributeSourceAdvisor.getAdvice(); + //重置拦截器,添加新的PermissionProxyAnnotationMethodInterceptor + List interceptors =new ArrayList<>(6); + AnnotationResolver resolver = new SpringAnnotationResolver(); + interceptors.add(new PermissionProxyAnnotationMethodInterceptor(resolver)); + interceptors.add(new RoleAnnotationMethodInterceptor(resolver)); + interceptors.add(new PermissionAnnotationMethodInterceptor(resolver)); + interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver)); + interceptors.add(new UserAnnotationMethodInterceptor(resolver)); + interceptors.add(new GuestAnnotationMethodInterceptor(resolver)); + advice.setMethodInterceptors(interceptors); + authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; } diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/controller/PermissionController.java b/diboot-shiro/src/main/java/com/diboot/shiro/controller/PermissionController.java index b4094ad..f06c135 100644 --- a/diboot-shiro/src/main/java/com/diboot/shiro/controller/PermissionController.java +++ b/diboot-shiro/src/main/java/com/diboot/shiro/controller/PermissionController.java @@ -4,10 +4,11 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.diboot.core.controller.BaseCrudRestController; import com.diboot.core.service.BaseService; import com.diboot.core.util.BeanUtils; -import com.diboot.core.util.V; import com.diboot.core.vo.JsonResult; import com.diboot.core.vo.Pagination; import com.diboot.core.vo.Status; +import com.diboot.shiro.bind.annotation.PermissionsMenu; +import com.diboot.shiro.bind.annotation.RequiresPermissionsProxy; import com.diboot.shiro.entity.Permission; import com.diboot.shiro.service.PermissionService; import com.diboot.shiro.vo.PermissionVO; @@ -28,6 +29,7 @@ import java.util.List; * @version 2018/12/23 * Copyright © www.dibo.ltd */ +@PermissionsMenu(menuName = "权限", menuCode = "permission") @RestController @RequestMapping("/permission") public class PermissionController extends BaseCrudRestController { @@ -45,8 +47,9 @@ public class PermissionController extends BaseCrudRestController { * @return * @throws Exception */ - @RequiresPermissions("permission:list") @GetMapping("/list") + @RequiresPermissionsProxy(value = {"permission:list"}, menuCode = "permission", + menuName = "权限", permissionName = "列表") public JsonResult getVOList(HttpServletRequest request) throws Exception{ QueryWrapper queryWrapper = buildQuery(request); // 构建分页 @@ -80,8 +83,8 @@ public class PermissionController extends BaseCrudRestController { * @return * @throws Exception */ - @RequiresPermissions("permission:get") @GetMapping("/{id}") + @RequiresPermissionsProxy(value = {"permission:get"}, menuCode = "permission", menuName = "权限", permissionName = "查看") public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request, ModelMap modelMap) throws Exception{ PermissionVO vo = permissionService.getViewObject(id, PermissionVO.class);