From ade3e1fe5f284ac067048122334f5dcbd1a183d9 Mon Sep 17 00:00:00 2001
From: wuy <1311695042@qq.com>
Date: Sun, 23 Jun 2019 01:05:28 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=20=E6=9C=8D=E5=8A=A1?=
=?UTF-8?q?=E5=90=AF=E5=8A=A8=E4=B9=8B=E5=90=8E=E6=9D=83=E9=99=90=E8=87=AA?=
=?UTF-8?q?=E5=8A=A8=E5=85=A5=E5=BA=93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../AuthorizationWrapperController.java | 40 +++
.../example/listener/ExampleListener.java | 36 +++
.../authz/annotation/AuthorizationPrefix.java | 45 ++++
.../annotation/AuthorizationWrapper.java | 28 ++
...ionWrapperAnnotationMethodInterceptor.java | 50 ++++
...notationsAuthorizingMethodInterceptor.java | 6 +-
...omAuthorizationAttributeSourceAdvisor.java | 9 +-
...AuthorizationWrapperAnnotationHandler.java | 112 ++++++++
.../authz/storage/EnableStorageEnum.java | 19 ++
.../authz/storage/PermissionStorage.java | 52 ++++
.../bind/annotation/PermissionsPrefix.java | 45 ----
.../RequiresPermissionsWrapper.java | 44 ---
...ionWrapperAnnotationMethodInterceptor.java | 54 ----
.../PermissionWrapperAnnotationHandler.java | 164 -----------
.../com/diboot/shiro/config/ShiroConfig.java | 2 +-
.../controller/PermissionController.java | 10 +-
.../com/diboot/shiro/entity/Permission.java | 9 +
.../shiro/jwt/BaseJwtAuthenticationToken.java | 16 +-
.../com/diboot/shiro/jwt/BaseJwtRealm.java | 3 +-
.../AbstractStorageApplicationListener.java | 255 ++++++++++++++++++
.../service/impl/PermissionServiceImpl.java | 1 -
.../java/com/diboot/shiro/util/JwtHelper.java | 2 +-
.../shiro/util/ProxyToTargetObjectHelper.java | 83 ++++++
23 files changed, 754 insertions(+), 331 deletions(-)
create mode 100644 diboot-example/src/main/java/com/diboot/example/controller/AuthorizationWrapperController.java
create mode 100644 diboot-example/src/main/java/com/diboot/example/listener/ExampleListener.java
create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/authz/annotation/AuthorizationPrefix.java
create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/authz/annotation/AuthorizationWrapper.java
create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/authz/aop/AuthorizationWrapperAnnotationMethodInterceptor.java
rename diboot-shiro/src/main/java/com/diboot/shiro/{bind => authz}/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java (95%)
rename diboot-shiro/src/main/java/com/diboot/shiro/{bind => authz}/aop/CustomAuthorizationAttributeSourceAdvisor.java (93%)
create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/authz/handler/AuthorizationWrapperAnnotationHandler.java
create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/authz/storage/EnableStorageEnum.java
create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/authz/storage/PermissionStorage.java
delete mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsPrefix.java
delete mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsWrapper.java
delete mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionWrapperAnnotationMethodInterceptor.java
delete mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionWrapperAnnotationHandler.java
create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/listener/AbstractStorageApplicationListener.java
create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/util/ProxyToTargetObjectHelper.java
diff --git a/diboot-example/src/main/java/com/diboot/example/controller/AuthorizationWrapperController.java b/diboot-example/src/main/java/com/diboot/example/controller/AuthorizationWrapperController.java
new file mode 100644
index 0000000..cfdba8f
--- /dev/null
+++ b/diboot-example/src/main/java/com/diboot/example/controller/AuthorizationWrapperController.java
@@ -0,0 +1,40 @@
+package com.diboot.example.controller;
+
+import com.diboot.core.vo.JsonResult;
+import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
+import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
+import org.apache.shiro.authz.annotation.Logical;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 权限测试
+ * @author : wee
+ * @version : v 2.0
+ * @Date 2019-06-19 13:37
+ */
+@RestController
+@RequestMapping("/authorization")
+@AuthorizationPrefix(name = "测试权限", code = "authorization", prefix = "authorization")
+public class AuthorizationWrapperController {
+
+ /**此处权限为:authorization:get 或 authorization:test*/
+ @GetMapping("/get")
+ @AuthorizationWrapper(value = @RequiresPermissions(value = {"get", "test"},
+ logical = Logical.OR),
+ name = {"查看", "测试"})
+ public JsonResult get() {
+ return new JsonResult("ok");
+ }
+
+ /**此处权限为:getAll 或 test*/
+ @GetMapping("/getAll")
+ @AuthorizationWrapper(value = @RequiresPermissions({"getAll", "test"}),
+ name = {"获取所有", "测试"},
+ ignorePrefix = true)
+ public JsonResult getAll() {
+ return new JsonResult("ok");
+ }
+}
diff --git a/diboot-example/src/main/java/com/diboot/example/listener/ExampleListener.java b/diboot-example/src/main/java/com/diboot/example/listener/ExampleListener.java
new file mode 100644
index 0000000..74efa36
--- /dev/null
+++ b/diboot-example/src/main/java/com/diboot/example/listener/ExampleListener.java
@@ -0,0 +1,36 @@
+package com.diboot.example.listener;
+
+import com.diboot.shiro.authz.storage.EnableStorageEnum;
+import com.diboot.shiro.listener.AbstractStorageApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.stereotype.Component;
+
+/**
+ * 示例监听器
+ * {@link AbstractStorageApplicationListener}丰富了 {@link org.springframework.context.ApplicationListener}
+ * {@link AbstractStorageApplicationListener}中封装了将{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper}权限自动入库的操作
+ * 当你使用注解{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper},建议直接继承{@link AbstractStorageApplicationListener}
+ * 然后对{@link AbstractStorageApplicationListener#customExecute(ContextRefreshedEvent)}方法进行重写
+ * 注:需要手动设置一个默认构造函数,传递是否自动权限入库
+ * @author : wee
+ * @version : v 2.0
+ * @Date 2019-06-18 23:11
+ */
+@Component
+public class ExampleListener extends AbstractStorageApplicationListener {
+
+ /**需要手动实现构造来设置是否开启权限入库操作,默认入库*/
+ protected ExampleListener() {
+ super(EnableStorageEnum.TRUE);
+ }
+
+ /**
+ * 系统启动后,客户自定义事件
+ *
+ * @param event
+ */
+ @Override
+ protected void customExecute(ContextRefreshedEvent event) {
+ System.out.println("============00000000");
+ }
+}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/authz/annotation/AuthorizationPrefix.java b/diboot-shiro/src/main/java/com/diboot/shiro/authz/annotation/AuthorizationPrefix.java
new file mode 100644
index 0000000..5d9b8aa
--- /dev/null
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/authz/annotation/AuthorizationPrefix.java
@@ -0,0 +1,45 @@
+package com.diboot.shiro.authz.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 权限注解的前缀,用于controller注解
+ * 注:当你使用{@link AuthorizationWrapper},请在类上使用{@link AuthorizationPrefix}注解进行标记,系统启动的时候才会将权限入库
+ * @author : wee
+ * @version v 2.0
+ * @Date 2019-06-17 20:42
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface AuthorizationPrefix {
+
+ /**
+ * 名称
+ *
+ * 设置当前权限前缀名称
+ * @return
+ */
+ String name();
+
+ /**
+ * 编码
+ *
+ * 设置当前权限前缀编码
+ * @return
+ */
+ String code();
+
+ /**
+ *
{@link AuthorizationWrapper#requiresPermissions()#value()}的前缀
+ *
+ * - value = permissions
+ * - {@link AuthorizationWrapper#requiresPermissions()#value()} = {"list", "get"}
+ * - 实际权限为:{"permissions:list", "permissions:list"}
+ *
+ * 注:当前注解优先级低于{@link AuthorizationWrapper#prefix()},
+ * 如果两者都配置优先使用{@link AuthorizationWrapper#prefix()}
+ * @return
+ */
+ String prefix() default "";
+}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/authz/annotation/AuthorizationWrapper.java b/diboot-shiro/src/main/java/com/diboot/shiro/authz/annotation/AuthorizationWrapper.java
new file mode 100644
index 0000000..eb7864b
--- /dev/null
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/authz/annotation/AuthorizationWrapper.java
@@ -0,0 +1,28 @@
+package com.diboot.shiro.authz.annotation;
+
+import org.apache.shiro.authz.annotation.*;
+
+import java.lang.annotation.*;
+
+/**
+ * 权限包装:目前只包装{@link RequiresPermissions},可以根据需要包装{@link RequiresRoles}
+ * @author : wee
+ * @version v 1.0
+ * @Date 2019-06-19 00:40
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface AuthorizationWrapper {
+
+ /**RequiresPermissions包装*/
+ RequiresPermissions value();
+
+ /**权限别名:用于存储到数据库,与{@link RequiresPermissions#value()}的值一一对应*/
+ String[] name();
+
+ /**设置前缀:用于拼接,详细描述参考{@link AuthorizationPrefix#prefix()}*/
+ String prefix() default "";
+
+ /**是否忽略前缀:默认不忽略*/
+ boolean ignorePrefix() default false;
+}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/authz/aop/AuthorizationWrapperAnnotationMethodInterceptor.java b/diboot-shiro/src/main/java/com/diboot/shiro/authz/aop/AuthorizationWrapperAnnotationMethodInterceptor.java
new file mode 100644
index 0000000..bc02dda
--- /dev/null
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/authz/aop/AuthorizationWrapperAnnotationMethodInterceptor.java
@@ -0,0 +1,50 @@
+package com.diboot.shiro.authz.aop;
+
+import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
+import com.diboot.shiro.authz.handler.AuthorizationWrapperAnnotationHandler;
+import org.apache.shiro.aop.AnnotationResolver;
+import org.apache.shiro.aop.MethodInvocation;
+import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
+
+/**
+ * {@link AuthorizationWrapper} 拦截器
+ * @author : wee
+ * @version : v2.0
+ * @Date 2019-06-14 22:19
+ */
+public class AuthorizationWrapperAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {
+ /**
+ * Default no-argument constructor that ensures this interceptor looks for
+ * {@link AuthorizationWrapper AuthorizationWrapper} annotations in a method declaration.
+ */
+ public AuthorizationWrapperAnnotationMethodInterceptor() {
+ super( new AuthorizationWrapperAnnotationHandler() );
+ }
+
+ /**
+ * @param resolver
+ * @since 1.1
+ */
+ public AuthorizationWrapperAnnotationMethodInterceptor(AnnotationResolver resolver) {
+ super( new AuthorizationWrapperAnnotationHandler(), resolver);
+ }
+
+ /**
+ * 当使用AuthorizationWrapper注解进行权限验证的时候,自动的去追加前缀
+ * @param mi
+ * @throws AuthorizationException
+ */
+ @Override
+ public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
+ try {
+ ((AuthorizationWrapperAnnotationHandler)getHandler()).assertAuthorized(getResolver(), mi);
+ }
+ catch(AuthorizationException ae) {
+ if (ae.getCause() == null) {
+ ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod()));
+ }
+ throw ae;
+ }
+ }
+}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java b/diboot-shiro/src/main/java/com/diboot/shiro/authz/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java
similarity index 95%
rename from diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java
rename to diboot-shiro/src/main/java/com/diboot/shiro/authz/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java
index 929d5ef..4d6bc49 100644
--- a/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/authz/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java
@@ -1,6 +1,5 @@
-package com.diboot.shiro.bind.aop;
+package com.diboot.shiro.authz.aop;
-import com.diboot.shiro.bind.aop.PermissionWrapperAnnotationMethodInterceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.shiro.aop.AnnotationResolver;
@@ -12,6 +11,7 @@ import java.util.ArrayList;
import java.util.List;
/**
+ * 自定义AOP拦截
* @author : wee
* @version : v2.0
* @Date 2019-06-15 12:07
@@ -21,7 +21,7 @@ public class CustomAopAllianceAnnotationsAuthorizingMethodInterceptor extends An
List interceptors =
new ArrayList(6);
AnnotationResolver resolver = new SpringAnnotationResolver();
- interceptors.add(new PermissionWrapperAnnotationMethodInterceptor(resolver));
+ interceptors.add(new AuthorizationWrapperAnnotationMethodInterceptor(resolver));
interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAuthorizationAttributeSourceAdvisor.java b/diboot-shiro/src/main/java/com/diboot/shiro/authz/aop/CustomAuthorizationAttributeSourceAdvisor.java
similarity index 93%
rename from diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAuthorizationAttributeSourceAdvisor.java
rename to diboot-shiro/src/main/java/com/diboot/shiro/authz/aop/CustomAuthorizationAttributeSourceAdvisor.java
index 86c32f7..52a46e6 100644
--- a/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAuthorizationAttributeSourceAdvisor.java
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/authz/aop/CustomAuthorizationAttributeSourceAdvisor.java
@@ -1,6 +1,6 @@
-package com.diboot.shiro.bind.aop;
+package com.diboot.shiro.authz.aop;
-import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
+import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import org.apache.shiro.authz.annotation.*;
import org.apache.shiro.mgt.SecurityManager;
import org.slf4j.Logger;
@@ -12,6 +12,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
+ * 自定义切片装配器
* @author : wee
* @version : v1.0
* @Date 2019-06-15 12:27
@@ -22,7 +23,7 @@ public class CustomAuthorizationAttributeSourceAdvisor extends StaticMethodMatch
private static final Class extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
new Class[] {
- RequiresPermissionsWrapper.class,
+ AuthorizationWrapper.class,
RequiresPermissions.class, RequiresRoles.class,
RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
};
@@ -48,7 +49,7 @@ public class CustomAuthorizationAttributeSourceAdvisor extends StaticMethodMatch
* Returns true if the method or the class has any Shiro annotations, false otherwise.
* The annotations inspected are:
*
- * - {@link RequiresPermissionsWrapper RequiresPermissionsWrapper}
+ * - {@link com.diboot.shiro.authz.annotation.AuthorizationWrapper AuthorizationWrapper}
* - {@link org.apache.shiro.authz.annotation.RequiresAuthentication RequiresAuthentication}
* - {@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser}
* - {@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/authz/handler/AuthorizationWrapperAnnotationHandler.java b/diboot-shiro/src/main/java/com/diboot/shiro/authz/handler/AuthorizationWrapperAnnotationHandler.java
new file mode 100644
index 0000000..c8b6e90
--- /dev/null
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/authz/handler/AuthorizationWrapperAnnotationHandler.java
@@ -0,0 +1,112 @@
+package com.diboot.shiro.authz.handler;
+
+import com.diboot.core.util.S;
+import com.diboot.core.util.V;
+import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
+import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
+import org.apache.shiro.aop.AnnotationResolver;
+import org.apache.shiro.aop.MethodInvocation;
+import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.annotation.Logical;
+import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
+import org.apache.shiro.authz.aop.PermissionAnnotationHandler;
+import org.apache.shiro.subject.Subject;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * {@link AuthorizationWrapper} 助手类, 参考{@link PermissionAnnotationHandler}实现
+ * @author : wee
+ * @version : v2.0
+ * @Date 2019-06-14 22:19
+ */
+public class AuthorizationWrapperAnnotationHandler extends AuthorizingAnnotationHandler {
+
+ /**
+ * 标记服务的注解
+ */
+ public AuthorizationWrapperAnnotationHandler() {
+ super(AuthorizationWrapper.class);
+ }
+
+
+ /**
+ * 返回{@link AuthorizationWrapper#value()#value} value}
+ * @param a ${@link AuthorizationWrapper}
+ * @return
+ */
+ protected String[] getAnnotationValue(Annotation a) {
+ AuthorizationWrapper rpAnnotation = (AuthorizationWrapper) a;
+ return rpAnnotation.value().value();
+ }
+
+ /**
+ * 校验注解{@link AuthorizationWrapper},自定义包装类,
+ * 使用${@link AuthorizationWrapperAnnotationHandler#assertAuthorized(AnnotationResolver, MethodInvocation)}进行权限验证
+ */
+ @Override
+ public void assertAuthorized(Annotation a) throws AuthorizationException {}
+
+ /**
+ * 校验注解{@link AuthorizationWrapper}
+ */
+ public void assertAuthorized(AnnotationResolver resolver, MethodInvocation mi) throws AuthorizationException {
+ //如果方法上存在AuthorizationWrapper注解,那么resolver.getAnnotation()获取的是AuthorizationWrapper注解,会优先从缓存读取
+ AuthorizationWrapper authorizationWrapper = (AuthorizationWrapper)resolver.getAnnotation(mi, AuthorizationWrapper.class);
+ String[] perms = getAnnotationValue(authorizationWrapper);
+ Subject subject = getSubject();
+ //如果忽略前缀,那么则不拼接前缀
+ perms = addPrefix(resolver, mi, authorizationWrapper, perms);
+ if (perms.length == 1) {
+ subject.checkPermission(perms[0]);
+ return;
+ }
+ if (Logical.AND.equals(authorizationWrapper.value().logical())) {
+ getSubject().checkPermissions(perms);
+ return;
+ }
+ if (Logical.OR.equals(authorizationWrapper.value().logical())) {
+ boolean hasAtLeastOnePermission = false;
+ for (String permission : perms) {
+ if (getSubject().isPermitted(permission)) {
+ hasAtLeastOnePermission = true;
+ }
+ }
+ if (!hasAtLeastOnePermission) {
+ getSubject().checkPermission(perms[0]);
+ }
+ }
+ }
+
+ /**
+ * 向 perms 追加前缀
+ * @param resolver
+ * @param mi
+ * @param authorizationWrapper
+ * @param perms
+ * @return
+ */
+ private String[] addPrefix(AnnotationResolver resolver, MethodInvocation mi, AuthorizationWrapper authorizationWrapper, String[] perms) {
+ if (!authorizationWrapper.ignorePrefix()) {
+ String prefix = "";
+ if (V.notEmpty(authorizationWrapper.prefix())) {
+ prefix = authorizationWrapper.prefix();
+ } else {
+ //如果自身不定义,查找前缀注解,存在则设置值
+ AuthorizationPrefix authorizationPrefix = (AuthorizationPrefix)resolver.getAnnotation(mi, AuthorizationPrefix.class);
+ if (V.notEmpty(authorizationPrefix)) {
+ prefix = authorizationPrefix.prefix();
+ }
+ }
+ String [] permsTemp = new String[perms.length];
+ //前缀存在的时候,才做组装,其他情况不处理
+ if (V.notEmpty(prefix)) {
+ for (int i = 0; i < perms.length; i++) {
+ permsTemp[i] = S.join(prefix, ":", perms[i]);
+ }
+ perms = permsTemp;
+ }
+ }
+ return perms;
+ }
+}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/authz/storage/EnableStorageEnum.java b/diboot-shiro/src/main/java/com/diboot/shiro/authz/storage/EnableStorageEnum.java
new file mode 100644
index 0000000..3d47aa2
--- /dev/null
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/authz/storage/EnableStorageEnum.java
@@ -0,0 +1,19 @@
+package com.diboot.shiro.authz.storage;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 是否开启存储枚举
+ * @author : wee
+ * @version : v 1.0
+ * @Date 2019-06-18 23:06
+ */
+@Getter
+@AllArgsConstructor
+public enum EnableStorageEnum {
+ TRUE(true),
+ FALSE(false);
+ private boolean storagePermissions;
+
+}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/authz/storage/PermissionStorage.java b/diboot-shiro/src/main/java/com/diboot/shiro/authz/storage/PermissionStorage.java
new file mode 100644
index 0000000..863f4c8
--- /dev/null
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/authz/storage/PermissionStorage.java
@@ -0,0 +1,52 @@
+package com.diboot.shiro.authz.storage;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.diboot.core.entity.BaseEntity;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author Yangzhao
+ * @version v2.0
+ * @date 2019/6/6
+ */
+@Data
+@Builder
+public class PermissionStorage implements Serializable {
+
+ private static final long serialVersionUID = 147840093814049689L;
+
+ /***
+ * 默认逻辑删除标记,deleted=0有效
+ */
+ private boolean deleted = false;
+
+ /**菜单Id*/
+ private Long menuId;
+
+ /**菜单编码*/
+ private String menuCode;
+
+ /**菜单名称*/
+ private String menuName;
+
+ /**权限编码*/
+ private String permissionCode;
+
+ /**权限名称*/
+ private String permissionName;
+
+ /**是否高优先级:方法上的优先级高于类上,同时出现以方法为准*/
+ private boolean highPriority;
+
+ private Long id;
+
+}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsPrefix.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsPrefix.java
deleted file mode 100644
index 1ec6540..0000000
--- a/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsPrefix.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.diboot.shiro.bind.annotation;
-
-import java.lang.annotation.*;
-
-/**
- * 权限注解的前缀,用于controller注解
- *
- * @author : wee
- * @version v 2.0
- * @Date 2019-06-17 20:42
- */
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface PermissionsPrefix {
-
- /**
- * 名称
- *
- * 设置当前权限前缀名称
- * @return
- */
- String name();
-
- /**
- * 编码
- *
- * 设置当前权限前缀编码
- * @return
- */
- String code();
-
- /**
- * {@link RequiresPermissionsWrapper#value()}的前缀
- *
- * - value = permissions
- * - {@link RequiresPermissionsWrapper#value()} = {"list", "get"}
- * - 实际权限为:{"permissions:list", "permissions:list"}
- *
- * 注:当前注解优先级低于{@link RequiresPermissionsWrapper#prefix()},
- * 如果两者都配置优先使用{@link RequiresPermissionsWrapper#prefix()}
- * @return
- */
- String prefix() default "";
-}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsWrapper.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsWrapper.java
deleted file mode 100644
index 9ad689c..0000000
--- a/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsWrapper.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.diboot.shiro.bind.annotation;
-
-import org.apache.shiro.authz.annotation.Logical;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
-
-import java.lang.annotation.*;
-
-/**
- * 注解{@link RequiresPermissions}的包装,增加权限描述等字段
- * @author : wee
- * @version v2.0
- * @Date 2019-06-14 17:50
- */
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE, ElementType.METHOD})
-public @interface RequiresPermissionsWrapper {
-
- /**
- * 包装 {@link RequiresPermissions#value()}
- */
- String[] value();
-
- /**
- * 包装 {@link RequiresPermissions#logical()}
- */
- Logical logical() default Logical.AND;
-
- /**
- * 权限名称
- * @return
- */
- String name();
-
- /**
- * 参照{@link PermissionsPrefix#prefix()}解释
- * @return
- */
- String prefix() default "";
-
-
-
-
-}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionWrapperAnnotationMethodInterceptor.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionWrapperAnnotationMethodInterceptor.java
deleted file mode 100644
index 1a2ba4d..0000000
--- a/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionWrapperAnnotationMethodInterceptor.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.diboot.shiro.bind.aop;
-
-import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
-import com.diboot.shiro.bind.handler.PermissionWrapperAnnotationHandler;
-import org.apache.shiro.aop.AnnotationResolver;
-import org.apache.shiro.aop.MethodInvocation;
-import org.apache.shiro.authz.AuthorizationException;
-import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
-import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
-
-/**
- * {@link RequiresPermissionsWrapper} 拦截器
- * @author : wee
- * @version : v2.0
- * @Date 2019-06-14 22:19
- */
-public class PermissionWrapperAnnotationMethodInterceptor 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 PermissionWrapperAnnotationMethodInterceptor() {
- super( new PermissionWrapperAnnotationHandler() );
- }
-
- /**
- * @param resolver
- * @since 1.1
- */
- public PermissionWrapperAnnotationMethodInterceptor(AnnotationResolver resolver) {
- super( new PermissionWrapperAnnotationHandler(), resolver);
- }
-
- /**
- * 当使用RequiresPermissionsWrapper注解进行权限验证的时候,自动的去追加前缀
- * @param mi
- * @throws AuthorizationException
- */
- @Override
- public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
- try {
-
- //默认是直接调用方法上注解,现在修改成 获取类和方法上的注解
-// ((AuthorizingAnnotationHandler)getHandler()).assertAuthorized(getAnnotation(mi));
- ((PermissionWrapperAnnotationHandler)getHandler()).assertAuthorized(getResolver(), mi);
- }
- catch(AuthorizationException ae) {
- if (ae.getCause() == null) {
- ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod()));
- }
- throw ae;
- }
- }
-}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionWrapperAnnotationHandler.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionWrapperAnnotationHandler.java
deleted file mode 100644
index 7478dcf..0000000
--- a/diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionWrapperAnnotationHandler.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package com.diboot.shiro.bind.handler;
-
-import com.diboot.core.util.S;
-import com.diboot.core.util.V;
-import com.diboot.shiro.bind.annotation.PermissionsPrefix;
-import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
-import org.apache.shiro.aop.AnnotationResolver;
-import org.apache.shiro.aop.MethodInvocation;
-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.aop.AuthorizingAnnotationHandler;
-import org.apache.shiro.authz.aop.PermissionAnnotationHandler;
-import org.apache.shiro.subject.Subject;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Proxy;
-import java.util.Map;
-
-/**
- * {@link RequiresPermissionsWrapper} 助手类, 参考{@link PermissionAnnotationHandler}实现
- * @author : wee
- * @version : v2.0
- * @Date 2019-06-14 22:19
- */
-public class PermissionWrapperAnnotationHandler 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 PermissionWrapperAnnotationHandler() {
- super(RequiresPermissionsWrapper.class);
- }
-
-
- /**
- * Returns the annotation {@link RequiresPermissions#value value}, from which the Permission will be constructed.
- *
- * @param a the RequiresPermissions annotation being inspected.
- * @return the annotation's value
, from which the Permission will be constructed.
- */
- protected String[] getAnnotationValue(Annotation a) {
- RequiresPermissionsWrapper rpAnnotation = (RequiresPermissionsWrapper) a;
- return rpAnnotation.value();
- }
-
- /**
- * 校验注解{@link RequiresPermissionsWrapper}
- */
- @Override
- public void assertAuthorized(Annotation a) throws AuthorizationException {
- if (!(a instanceof RequiresPermissionsWrapper)) {
- return;
- }
- RequiresPermissionsWrapper rppAnnotation = (RequiresPermissionsWrapper) a;
- String[] perms = getAnnotationValue(a);
- Subject subject = getSubject();
-
- if (perms.length == 1) {
- subject.checkPermission(perms[0]);
- return;
- }
- if (Logical.AND.equals(rppAnnotation.logical())) {
- getSubject().checkPermissions(perms);
- return;
- }
- if (Logical.OR.equals(rppAnnotation.logical())) {
- boolean hasAtLeastOnePermission = false;
- for (String permission : perms) {
- if (getSubject().isPermitted(permission)) {
- hasAtLeastOnePermission = true;
- }
- }
- if (!hasAtLeastOnePermission) {
- getSubject().checkPermission(perms[0]);
- }
- }
- }
-
- /**
- * 校验注解{@link RequiresPermissionsWrapper}
- */
- public void assertAuthorized(AnnotationResolver resolver, MethodInvocation mi) throws AuthorizationException {
- //如果方法上存在RequiresPermissionsWrapper注解,那么resolver.getAnnotation()获取的是RequiresPermissionsWrapper注解
- //优先从缓存读取
- RequiresPermissionsWrapper requiresPermissionsWrapper = (RequiresPermissionsWrapper)resolver.getAnnotation(mi, RequiresPermissionsWrapper.class);
- String prefix = "";
- if (V.notEmpty(requiresPermissionsWrapper.prefix())) {
- prefix = requiresPermissionsWrapper.prefix();
- } else {
- //如果自身不定义,查找前缀注解,存在则设置值
- PermissionsPrefix permissionsPrefix = (PermissionsPrefix)resolver.getAnnotation(mi, PermissionsPrefix.class);
- if (V.notEmpty(permissionsPrefix)) {
- prefix = permissionsPrefix.prefix();
- }
- }
-
- String[] perms = getAnnotationValue(requiresPermissionsWrapper);
- Subject subject = getSubject();
-
- String [] permsTemp = new String[perms.length];
- //前缀存在的时候,才做组装,其他情况不处理
- if (V.notEmpty(prefix)) {
- for (int i = 0; i < perms.length; i++) {
- permsTemp[i] = S.join(prefix, ":", perms[i]);
- }
- perms = permsTemp;
- }
-
- if (perms.length == 1) {
- subject.checkPermission(perms[0]);
- return;
- }
- if (Logical.AND.equals(requiresPermissionsWrapper.logical())) {
- getSubject().checkPermissions(perms);
- return;
- }
- if (Logical.OR.equals(requiresPermissionsWrapper.logical())) {
- boolean hasAtLeastOnePermission = false;
- for (String permission : perms) {
- if (getSubject().isPermitted(permission)) {
- hasAtLeastOnePermission = true;
- }
- }
- if (!hasAtLeastOnePermission) {
- getSubject().checkPermission(perms[0]);
- }
- }
- }
-
- /**
- * 动态修改注解的值(不可用)
- * @param rppAnnotation
- */
- @Deprecated
- private void proxy(RequiresPermissionsWrapper rppAnnotation) {
- try {
- //获取RequiresPermissionsProxy上的RequiresPermissions注解
- RequiresPermissions requiresPermissions = rppAnnotation.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, rppAnnotation.value());
- memberValues.put(REQUIRES_PERMISSIONS_LOGICAL, rppAnnotation.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 634c0e3..7e6902f 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,6 +1,6 @@
package com.diboot.shiro.config;
-import com.diboot.shiro.bind.aop.CustomAuthorizationAttributeSourceAdvisor;
+import com.diboot.shiro.authz.aop.CustomAuthorizationAttributeSourceAdvisor;
import com.diboot.shiro.jwt.BaseJwtAuthenticationFilter;
import com.diboot.shiro.jwt.BaseJwtRealm;
import org.apache.shiro.mgt.SecurityManager;
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 7bdd498..f342e5b 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
@@ -7,8 +7,8 @@ import com.diboot.core.util.BeanUtils;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.Pagination;
import com.diboot.core.vo.Status;
-import com.diboot.shiro.bind.annotation.PermissionsPrefix;
-import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
+import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
+import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import com.diboot.shiro.entity.Permission;
import com.diboot.shiro.service.PermissionService;
import com.diboot.shiro.vo.PermissionVO;
@@ -30,7 +30,7 @@ import java.util.List;
* Copyright © www.dibo.ltd
*/
@RestController
-@PermissionsPrefix(prefix = "permission", code = "permission", name = "权限")
+@AuthorizationPrefix(prefix = "permission", code = "permission", name = "权限")
@RequestMapping("/permission")
public class PermissionController extends BaseCrudRestController {
@@ -46,7 +46,7 @@ public class PermissionController extends BaseCrudRestController {
* @throws Exception
*/
@GetMapping("/{id}")
- @RequiresPermissionsWrapper(prefix = "permissionSelf", value = {"get"}, name = "查看")
+ @AuthorizationWrapper(value = @RequiresPermissions("get"), name = "查看")
public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request, ModelMap modelMap)
throws Exception{
PermissionVO vo = permissionService.getViewObject(id, PermissionVO.class);
@@ -62,7 +62,7 @@ public class PermissionController extends BaseCrudRestController {
* @throws Exception
*/
@GetMapping("/list")
- @RequiresPermissionsWrapper(value = {"list"}, name = "列表")
+ @AuthorizationWrapper(value = @RequiresPermissions("list"), name = "列表")
public JsonResult getVOList(HttpServletRequest request) throws Exception{
QueryWrapper queryWrapper = buildQuery(request);
// 构建分页
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/entity/Permission.java b/diboot-shiro/src/main/java/com/diboot/shiro/entity/Permission.java
index f652a7a..74afbcd 100644
--- a/diboot-shiro/src/main/java/com/diboot/shiro/entity/Permission.java
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/entity/Permission.java
@@ -1,6 +1,8 @@
package com.diboot.shiro.entity;
+import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableLogic;
import com.diboot.core.entity.BaseEntity;
import lombok.Data;
@@ -14,6 +16,9 @@ public class Permission extends BaseEntity {
private static final long serialVersionUID = 7713768302925692987L;
+ @TableField
+ private Long menuId;
+
@TableField
private String menuCode;
@@ -26,4 +31,8 @@ public class Permission extends BaseEntity {
@TableField
private String permissionName;
+ /**此处覆盖了父类的属性,初始化权限的时候需要设置该值,父类默认不设置*/
+// @TableField
+// private boolean deleted = false;
+
}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtAuthenticationToken.java b/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtAuthenticationToken.java
index 8de06ed..4b0f659 100644
--- a/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtAuthenticationToken.java
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtAuthenticationToken.java
@@ -21,30 +21,30 @@ public class BaseJwtAuthenticationToken implements AuthenticationToken {
private static final Logger logger = LoggerFactory.getLogger(BaseJwtAuthenticationToken.class);
- // 登录用的账号(此处的这个账号是一种抽象的概念)
+ /**登录用的账号(此处的这个账号是一种抽象的概念*/
private String account;
- // 登录用的密码 (此处的这个密码也是一种抽象的概念)
+ /**登录用的密码 (此处的这个密码也是一种抽象的概念*/
private String password;
- // 登录使用方式
+ /**登录使用方式*/
private AuthType authType;
- // auth token
+ /**authz token*/
private String authtoken;
- // 申请token的密码
+ /**申请token的密码*/
private String applyTokenSecret;
- // 签名key (默认SIGN_KEY,配置signKey, 或微信state, 密码等)
+ /**签名key (默认SIGN_KEY,配置signKey, 或微信state, 密码等)*/
private String signKey = JwtHelper.SIGN_KEY;
- // 过期时间
+ /**过期时间*/
private long expiresInMinutes = JwtHelper.EXPIRES_IN_MINUTES;
private Map authWayServiceMap;
- // 默认构造函数
+ /**默认构造函数*/
public BaseJwtAuthenticationToken(){
}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtRealm.java b/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtRealm.java
index 59450f1..b62ee80 100644
--- a/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtRealm.java
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtRealm.java
@@ -31,11 +31,12 @@ public class BaseJwtRealm extends AuthorizingRealm {
@Autowired
private RoleService roleService;
+ @Override
public boolean supports(AuthenticationToken token) {
return token != null && token instanceof BaseJwtAuthenticationToken;
}
-
+ @Override
public Class> getAuthenticationTokenClass() {
return BaseJwtRealm.class;
}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/listener/AbstractStorageApplicationListener.java b/diboot-shiro/src/main/java/com/diboot/shiro/listener/AbstractStorageApplicationListener.java
new file mode 100644
index 0000000..f8e1c1a
--- /dev/null
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/listener/AbstractStorageApplicationListener.java
@@ -0,0 +1,255 @@
+package com.diboot.shiro.listener;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.diboot.core.util.BeanUtils;
+import com.diboot.core.util.S;
+import com.diboot.core.util.V;
+import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
+import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
+import com.diboot.shiro.authz.storage.EnableStorageEnum;
+import com.diboot.shiro.authz.storage.PermissionStorage;
+import com.diboot.shiro.entity.Permission;
+import com.diboot.shiro.service.impl.PermissionServiceImpl;
+import com.diboot.shiro.util.ProxyToTargetObjectHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * {@link AbstractStorageApplicationListener}实现{@link ApplicationListener}接口,
+ *
+ * 并重新对外提供抽象{@link AbstractStorageApplicationListener#customExecute},功能等同于{@link ApplicationListener#onApplicationEvent};
+ *
+ * {@link AbstractStorageApplicationListener}中封装了将{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper}权限自动入库的操作,
+ * 注:权限入库每个Controller需要加上类注解{@link AuthorizationPrefix}用于识别
+ *
+ * 当你使用注解{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper},建议直接继承{@link AbstractStorageApplicationListener},
+ *
+ * 且你的实现类需要手动设置一个默认构造函数来设置{@link AbstractStorageApplicationListener#storagePermissions},传递是否自动权限入库
+ *
+ * @author : wee
+ * @version : v 2.0
+ * @Date 2019-06-18 23:12
+ */
+@Slf4j
+public abstract class AbstractStorageApplicationListener implements ApplicationListener {
+
+ /**存储数据库中已经存在的permissionCode和ID的关系,主要用于更新数据*/
+ private static Map dbPermissionMap = new HashMap<>();
+
+ /**代码中的所有权限数据 key: {@link PermissionStorage#getPermissionCode()} value: {@link Permission}*/
+ private static Map loadCodePermissionMap = new ConcurrentHashMap<>();
+
+ private static List updateOrCreateOrDeletePermissionList = new ArrayList<>();
+ /**
+ * 默认开启存储
+ */
+ protected boolean storagePermissions;
+
+ protected AbstractStorageApplicationListener(EnableStorageEnum enableStorageEnum) {
+ this.storagePermissions = enableStorageEnum.isStoragePermissions();
+ }
+
+ /**
+ * Handle an application event.
+ *
+ * @param event the event to respond to
+ */
+ @Override
+ public void onApplicationEvent(ContextRefreshedEvent event) {
+ //防止重复执行
+ if (V.isEmpty(event.getApplicationContext().getParent())) {
+ /*自定义处理内容*/
+ customExecute(event);
+ /*判断客户是否开启存储权限,如果开启那么执行存储权限*/
+ if (storagePermissions) {
+ storagePermissions(event);
+ }
+ }
+ }
+
+ /**
+ * 系统启动后,客户自定义事件
+ *
+ * @param event
+ */
+ protected abstract void customExecute(ContextRefreshedEvent event);
+
+ /**
+ * 执行完自动权限后,自动赋值
+ *
+ * @param event
+ */
+ private void storagePermissions(ContextRefreshedEvent event) {
+ try {
+ ApplicationContext applicationContext = event.getApplicationContext();
+
+ if (V.notEmpty(applicationContext)) {
+ PermissionServiceImpl permissionService = applicationContext.getBean(PermissionServiceImpl.class);
+
+ //获取当前数据库中的有效的所有权限
+ LambdaQueryWrapper permissionLambdaQueryWrapper = Wrappers.lambdaQuery();
+ permissionLambdaQueryWrapper.eq(Permission::isDeleted, false);
+ List permissionList = permissionService.getEntityList(permissionLambdaQueryWrapper);
+ //存储数据库值
+ for (Permission permission : permissionList) {
+ dbPermissionMap.put(permission.getPermissionCode(), permission);
+ }
+
+ //初始化数据:获取所有注解为AuthPrefix的代理bean
+ Map beansWithAnnotation = applicationContext.getBeansWithAnnotation(AuthorizationPrefix.class);
+ if (V.isEmpty(beansWithAnnotation)) {
+ return;
+ }
+ for (Map.Entry entry : beansWithAnnotation.entrySet()) {
+ //获取代理对象的目标对象(代理对象无法获取类上注解,所以需要转化成目标对象)
+ Object target = ProxyToTargetObjectHelper.getTarget(entry.getValue());
+ Class> targetClass = target.getClass();
+ //获取类注解上的相关描述
+ AuthorizationPrefix authorizationPrefix = targetClass.getAnnotation(AuthorizationPrefix.class);
+ //判断Controller类上是否包含认证注解的包装
+ if (targetClass.isAnnotationPresent(AuthorizationWrapper.class)) {
+ buildPermissionStorageToMap(authorizationPrefix, targetClass.getAnnotation(AuthorizationWrapper.class), false);
+ }
+ //获取类中所有方法
+ Method[] methods = target.getClass().getMethods();
+ for (Method method : methods) {
+ //取出含有注解的方法
+ if (method.isAnnotationPresent(AuthorizationWrapper.class)) {
+ buildPermissionStorageToMap(authorizationPrefix, method.getAnnotation(AuthorizationWrapper.class), true);
+ }
+ }
+ }
+ //保存、更新、删除 权限
+ saveOrUpdateOrDeletePermission(permissionService);
+ }
+ } catch (Exception e) {
+ log.error("【初始化权限】<== 异常:", e);
+ }
+ }
+
+ /**
+ * 构建待存储的权限到map中
+ * @param authorizationPrefix {@link AuthorizationPrefix}
+ * @param authorizationWrapper {@link AuthorizationWrapper}
+ */
+ private void buildPermissionStorageToMap(AuthorizationPrefix authorizationPrefix, AuthorizationWrapper authorizationWrapper, boolean highPriority) {
+ String menuCode = authorizationPrefix.code();
+ String menuName = authorizationPrefix.name();
+ //如果不忽略前缀, 那么进行前缀补充 (如果允许,优先使用 AuthorizationWrapper注解的前缀)
+ String prefix = "";
+ if (!authorizationWrapper.ignorePrefix()) {
+ prefix = V.notEmpty(authorizationWrapper.prefix()) ? authorizationWrapper.prefix() : authorizationPrefix.prefix();
+ }
+ //获取权限
+ RequiresPermissions requiresPermissions = authorizationWrapper.value();
+ //获取所有描述的权限code和name
+ String[] value = requiresPermissions.value();
+ String[] name = authorizationWrapper.name();
+ //设置单个权限code和name
+ String permissionName = "";
+ String permissionCode = "";
+ PermissionStorage permissionStorage;
+ for (int i = 0; i < value.length; i++) {
+ //如果权限名称和值无法一一对应,那么当前权限组的所有数据的名称全部设置为最后一个name值
+ permissionName = value.length != name.length ? name[name.length - 1] : name[i];
+ //拼接权限值
+ permissionCode = V.notEmpty(prefix) ? S.join(prefix, ":", value[i]) : value[i];
+ PermissionStorage existPermission = loadCodePermissionMap.get(permissionCode);
+ //如果不存在权限构建;当前存在的权限不是是高优先级的时候替换
+ if (V.isEmpty(existPermission) || !existPermission.isHighPriority()) {
+ //组装需要存储的权限
+ permissionStorage = PermissionStorage.builder()
+ .menuId(3L).menuCode(menuCode).menuName(menuName)
+ .permissionCode(permissionCode).permissionName(permissionName)
+ .deleted(false).highPriority(highPriority).build();
+ //设置缓存
+ loadCodePermissionMap.put(permissionCode, permissionStorage);
+ }
+ }
+ }
+
+ /**
+ * 保存、更新、删除 权限
+ *
+ * 操作原则 以数据库字段为基准 匹配数据库中的权限和代码中的权限:
+ * * 如果代码中和数据库中相同,更新数据库中数据;
+ * * 如果代码中不存在,删除数据库中数据;
+ *
+ * @param permissionService
+ */
+ private void saveOrUpdateOrDeletePermission(PermissionServiceImpl permissionService) {
+ //记录修改和删除的权限数量
+ int modifyCount = 0, removeCount = 0, totalCount = 0;
+ List saveOrUpdateOrDeletePermissionList = new ArrayList<>();
+ //设置删除 or 修改
+ for (Map.Entry entry : dbPermissionMap.entrySet()) {
+ PermissionStorage permissionStorage = loadCodePermissionMap.get(entry.getKey());
+ Permission permission = entry.getValue();
+ //存在则更新(设置ID)
+ if (V.notEmpty(permissionStorage)) {
+ if (isNeedModify(permission, permissionStorage)) {
+ modifyCount++;
+ permissionStorage.setId(permission.getId());
+ //重置
+ loadCodePermissionMap.put(entry.getKey(), permissionStorage);
+ } else {
+ //数据库中不需要修改的,在加载的数据中删除
+ loadCodePermissionMap.remove(entry.getKey());
+ }
+ } else {
+ //不存在: 表示需要删除
+ removeCount++;
+ permission.setDeleted(true);
+ saveOrUpdateOrDeletePermissionList.add(permission);
+ }
+
+ }
+ //需要操作的数据=》转化为List
+ List saveOrUpdatePermissionList;
+ if (V.notEmpty(loadCodePermissionMap)) {
+ totalCount = loadCodePermissionMap.values().size();
+ List permissionStorageList = loadCodePermissionMap.values().stream().collect(Collectors.toList());
+ saveOrUpdatePermissionList = BeanUtils.convertList(permissionStorageList, Permission.class);
+ saveOrUpdateOrDeletePermissionList.addAll(saveOrUpdatePermissionList);
+ }
+ //保存、更新、删除 权限
+ boolean success = permissionService.saveOrUpdateBatch(saveOrUpdateOrDeletePermissionList);
+ if (success) {
+ log.debug("【初始化权限】<== 成功!共计权限{}个 新增【{}】个, 修改【{}】个,删除【{}】个!",
+ totalCount, (saveOrUpdateOrDeletePermissionList.size() - modifyCount - removeCount), modifyCount, removeCount);
+ } else {
+ log.debug("【初始化权限】<== 失败!");
+ }
+ }
+
+ /**
+ * 比较两个对象的属性是否相同,如果存在一处不同那么就需要修改
+ * @param permission
+ * @param permissionStorage
+ * @return
+ */
+ public boolean isNeedModify(Permission permission, PermissionStorage permissionStorage){
+ if (!V.equals(permission.getMenuId(), permissionStorage.getMenuId())
+ || !V.equals(permission.getMenuCode(), permissionStorage.getMenuCode())
+ || !V.equals(permission.getMenuName(), permissionStorage.getMenuName())
+ || !V.equals(permission.getPermissionCode(), permissionStorage.getPermissionCode())
+ || !V.equals(permission.getPermissionName(), permissionStorage.getPermissionName())
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/service/impl/PermissionServiceImpl.java b/diboot-shiro/src/main/java/com/diboot/shiro/service/impl/PermissionServiceImpl.java
index 19cb82d..dfdc4a6 100644
--- a/diboot-shiro/src/main/java/com/diboot/shiro/service/impl/PermissionServiceImpl.java
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/service/impl/PermissionServiceImpl.java
@@ -4,7 +4,6 @@ import com.diboot.core.service.impl.BaseServiceImpl;
import com.diboot.shiro.entity.Permission;
import com.diboot.shiro.mapper.PermissionMapper;
import com.diboot.shiro.service.PermissionService;
-import com.diboot.shiro.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/util/JwtHelper.java b/diboot-shiro/src/main/java/com/diboot/shiro/util/JwtHelper.java
index f7608fc..1499ba9 100644
--- a/diboot-shiro/src/main/java/com/diboot/shiro/util/JwtHelper.java
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/util/JwtHelper.java
@@ -21,7 +21,7 @@ public class JwtHelper {
private static final Logger logger = LoggerFactory.getLogger(JwtHelper.class);
private static final String ISSUER = V.notEmpty(BaseConfig.getProperty("diboot.shiro.jwt.issuer")) ? BaseConfig.getProperty("diboot.shiro.jwt.issuer") : "diboot.com";
- private static final String AUTH_HEADER = V.notEmpty(BaseConfig.getProperty("diboot.shiro.jwt.auth.header.key")) ? BaseConfig.getProperty("diboot.shiro.jwt.auth.header.key") : "authtoken";
+ private static final String AUTH_HEADER = V.notEmpty(BaseConfig.getProperty("diboot.shiro.jwt.authz.header.key")) ? BaseConfig.getProperty("diboot.shiro.jwt.authz.header.key") : "authtoken";
private static final String TOKEN_PREFIX = V.notEmpty(BaseConfig.getProperty("diboot.shiro.jwt.token.prefix")) ? BaseConfig.getProperty("diboot.shiro.jwt.token.prefix") : "Bearer ";
public static final String SIGN_KEY = V.notEmpty(BaseConfig.getProperty("diboot.shiro.jwt.signkey"))? BaseConfig.getProperty("diboot.shiro.jwt.signkey") : "Dibo2016Mazc";
diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/util/ProxyToTargetObjectHelper.java b/diboot-shiro/src/main/java/com/diboot/shiro/util/ProxyToTargetObjectHelper.java
new file mode 100644
index 0000000..3ed3600
--- /dev/null
+++ b/diboot-shiro/src/main/java/com/diboot/shiro/util/ProxyToTargetObjectHelper.java
@@ -0,0 +1,83 @@
+package com.diboot.shiro.util;
+
+import org.springframework.aop.framework.AdvisedSupport;
+import org.springframework.aop.framework.AopProxy;
+import org.springframework.aop.support.AopUtils;
+
+import java.lang.reflect.Field;
+
+/**
+ * 代理对象转成目标对象
+ * @author : wee
+ * @version : v 2.0
+ * @Date 2019-06-19 14:42
+ */
+public class ProxyToTargetObjectHelper {
+
+ /**
+ * 获取代理对象的目标对象
+ * @param proxy
+ * @return
+ * @throws Exception
+ */
+ public static Object getTarget(Object proxy) throws Exception {
+ /*判断是否是代理对象*/
+ if(!AopUtils.isAopProxy(proxy)) {
+ return proxy;
+ }
+ //判断是jdk动态代理还是cglib代理
+ if(AopUtils.isJdkDynamicProxy(proxy)) {
+ return getJdkDynamicProxyTargetObject(proxy);
+ }
+ //cglib
+ else {
+ return getCglibProxyTargetObject(proxy);
+ }
+ }
+
+
+ /**
+ * 获取cglib代理的目标对象
+ * @param proxy
+ * @return
+ * @throws Exception
+ */
+ private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
+ Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
+ return getJdkOrCglibTargetObject(h, proxy);
+ }
+
+
+ /**
+ * 获取jdk动态代理的目标对象
+ * @param proxy
+ * @return
+ * @throws Exception
+ */
+ private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
+ Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
+ return getJdkOrCglibTargetObject(h, proxy);
+ }
+
+ /**
+ * 根据不用的代理获取目标对象
+ * @param field
+ * @param proxy
+ * @return
+ * @throws Exception
+ */
+ private static Object getJdkOrCglibTargetObject(Field field, Object proxy) throws Exception{
+ field.setAccessible(true);
+ Object dynamic = field.get(proxy);
+ Field advised;
+ //如果是jdk代理
+ if (dynamic instanceof AopProxy) {
+ advised = ((AopProxy)dynamic).getClass().getDeclaredField("advised");
+ }
+ //如果是cglib代理
+ advised = dynamic.getClass().getDeclaredField("advised");
+ advised.setAccessible(true);
+ //获取目标类
+ return ((AdvisedSupport)advised.get(dynamic)).getTargetSource().getTarget();
+ }
+}