From e020ebdc2220fbde2735a9b32ba6fb53c86cf7c0 Mon Sep 17 00:00:00 2001 From: wuy <1311695042@qq.com> Date: Mon, 17 Jun 2019 22:38:38 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=A2=9E=E5=BC=BAshiro=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 --- .../bind/annotation/PermissionsPrefix.java | 45 +++++ ...y.java => RequiresPermissionsWrapper.java} | 30 ++-- ...notationsAuthorizingMethodInterceptor.java | 100 +++++++++++ ...omAuthorizationAttributeSourceAdvisor.java | 106 +++++++++++ ...ssionProxyAnnotationMethodInterceptor.java | 30 ---- ...ionWrapperAnnotationMethodInterceptor.java | 54 ++++++ .../PermissionProxyAnnotationHandler.java | 69 -------- .../PermissionWrapperAnnotationHandler.java | 164 ++++++++++++++++++ .../com/diboot/shiro/config/ShiroConfig.java | 25 +-- .../controller/PermissionController.java | 37 ++-- .../jwt/BaseJwtAuthenticationFilter.java | 2 + 12 files changed, 505 insertions(+), 182 deletions(-) delete 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/PermissionsPrefix.java rename diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/{RequiresPermissionsProxy.java => RequiresPermissionsWrapper.java} (57%) create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAuthorizationAttributeSourceAdvisor.java delete 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/aop/PermissionWrapperAnnotationMethodInterceptor.java delete mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionProxyAnnotationHandler.java create mode 100644 diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionWrapperAnnotationHandler.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 deleted file mode 100644 index 1307e81..0000000 --- a/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsMenu.java +++ /dev/null @@ -1,25 +0,0 @@ -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/PermissionsPrefix.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsPrefix.java new file mode 100644 index 0000000..1ec6540 --- /dev/null +++ b/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/PermissionsPrefix.java @@ -0,0 +1,45 @@ +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()}的前缀

+ * + * 注:当前注解优先级低于{@link RequiresPermissionsWrapper#prefix()}, + * 如果两者都配置优先使用{@link RequiresPermissionsWrapper#prefix()} + * @return + */ + String prefix() default ""; +} 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/RequiresPermissionsWrapper.java similarity index 57% rename from diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsProxy.java rename to diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsWrapper.java index 9577887..9ad689c 100644 --- a/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsProxy.java +++ b/diboot-shiro/src/main/java/com/diboot/shiro/bind/annotation/RequiresPermissionsWrapper.java @@ -6,7 +6,7 @@ import org.apache.shiro.authz.annotation.RequiresPermissions; import java.lang.annotation.*; /** - * 注解{@link RequiresPermissions}的增强注解,增加权限描述等字段 + * 注解{@link RequiresPermissions}的包装,增加权限描述等字段 * @author : wee * @version v2.0 * @Date 2019-06-14 17:50 @@ -14,35 +14,31 @@ import java.lang.annotation.*; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) -@RequiresPermissions("default") -public @interface RequiresPermissionsProxy { +public @interface RequiresPermissionsWrapper { /** - * 代理 {@link RequiresPermissions#value()} + * 包装 {@link RequiresPermissions#value()} */ String[] value(); /** - * 代理 {@link RequiresPermissions#logical()} + * 包装 {@link RequiresPermissions#logical()} */ Logical logical() default Logical.AND; - /** - * 菜单编码 - * @return - */ - String menuCode(); - /** - * 菜单名称 - * @return - */ - String menuName(); - /** * 权限名称 * @return */ - String permissionName(); + String name(); + + /** + * 参照{@link PermissionsPrefix#prefix()}解释 + * @return + */ + String prefix() default ""; + + } diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java new file mode 100644 index 0000000..929d5ef --- /dev/null +++ b/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAopAllianceAnnotationsAuthorizingMethodInterceptor.java @@ -0,0 +1,100 @@ +package com.diboot.shiro.bind.aop; + +import com.diboot.shiro.bind.aop.PermissionWrapperAnnotationMethodInterceptor; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.shiro.aop.AnnotationResolver; +import org.apache.shiro.authz.aop.*; +import org.apache.shiro.spring.aop.SpringAnnotationResolver; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * @author : wee + * @version : v2.0 + * @Date 2019-06-15 12:07 + */ +public class CustomAopAllianceAnnotationsAuthorizingMethodInterceptor extends AnnotationsAuthorizingMethodInterceptor implements MethodInterceptor { + public CustomAopAllianceAnnotationsAuthorizingMethodInterceptor() { + List interceptors = + new ArrayList(6); + AnnotationResolver resolver = new SpringAnnotationResolver(); + interceptors.add(new PermissionWrapperAnnotationMethodInterceptor(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)); + + setMethodInterceptors(interceptors); + } + /** + * Creates a {@link MethodInvocation MethodInvocation} that wraps an + * {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation} instance, + * enabling Shiro Annotations in AOP Alliance environments + * (Spring, etc). + * + * @param implSpecificMethodInvocation AOP Alliance {@link org.aopalliance.intercept.MethodInvocation MethodInvocation} + * @return a Shiro {@link MethodInvocation MethodInvocation} instance that wraps the AOP Alliance instance. + */ + protected org.apache.shiro.aop.MethodInvocation createMethodInvocation(Object implSpecificMethodInvocation) { + final MethodInvocation mi = (MethodInvocation) implSpecificMethodInvocation; + + return new org.apache.shiro.aop.MethodInvocation() { + @Override + public Method getMethod() { + return mi.getMethod(); + } + + @Override + public Object[] getArguments() { + return mi.getArguments(); + } + + @Override + public String toString() { + return "Method invocation [" + mi.getMethod() + "]"; + } + + @Override + public Object proceed() throws Throwable { + return mi.proceed(); + } + + @Override + public Object getThis() { + return mi.getThis(); + } + }; + } + + /** + * Simply casts the method argument to an + * {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation} and then + * calls methodInvocation.{@link org.aopalliance.intercept.MethodInvocation#proceed proceed}() + * + * @param aopAllianceMethodInvocation the {@link org.aopalliance.intercept.MethodInvocation org.aopalliance.intercept.MethodInvocation} + * @return the {@link org.aopalliance.intercept.MethodInvocation#proceed() org.aopalliance.intercept.MethodInvocation.proceed()} method call result. + * @throws Throwable if the underlying AOP Alliance proceed() call throws a Throwable. + */ + protected Object continueInvocation(Object aopAllianceMethodInvocation) throws Throwable { + MethodInvocation mi = (MethodInvocation) aopAllianceMethodInvocation; + return mi.proceed(); + } + + /** + * Creates a Shiro {@link MethodInvocation MethodInvocation} instance and then immediately calls + * {@link org.apache.shiro.authz.aop.AuthorizingMethodInterceptor#invoke super.invoke}. + * + * @param methodInvocation the AOP Alliance-specific methodInvocation instance. + * @return the return value from invoking the method invocation. + * @throws Throwable if the underlying AOP Alliance method invocation throws a Throwable. + */ + @Override + public Object invoke(MethodInvocation methodInvocation) throws Throwable { + org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation); + return super.invoke(mi); + } +} diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAuthorizationAttributeSourceAdvisor.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAuthorizationAttributeSourceAdvisor.java new file mode 100644 index 0000000..86c32f7 --- /dev/null +++ b/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/CustomAuthorizationAttributeSourceAdvisor.java @@ -0,0 +1,106 @@ +package com.diboot.shiro.bind.aop; + +import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper; +import org.apache.shiro.authz.annotation.*; +import org.apache.shiro.mgt.SecurityManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; +import org.springframework.core.annotation.AnnotationUtils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + * @author : wee + * @version : v1.0 + * @Date 2019-06-15 12:27 + */ +public class CustomAuthorizationAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor { + + private static final Logger log = LoggerFactory.getLogger(CustomAuthorizationAttributeSourceAdvisor.class); + + private static final Class[] AUTHZ_ANNOTATION_CLASSES = + new Class[] { + RequiresPermissionsWrapper.class, + RequiresPermissions.class, RequiresRoles.class, + RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class + }; + + protected SecurityManager securityManager = null; + + /** + * Create a new AuthorizationAttributeSourceAdvisor. + */ + public CustomAuthorizationAttributeSourceAdvisor() { + setAdvice(new CustomAopAllianceAnnotationsAuthorizingMethodInterceptor()); + } + + public SecurityManager getSecurityManager() { + return securityManager; + } + + public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) { + this.securityManager = securityManager; + } + + /** + * Returns true if the method or the class has any Shiro annotations, false otherwise. + * The annotations inspected are: + * + * + * @param method the method to check for a Shiro annotation + * @param targetClass the class potentially declaring Shiro annotations + * @return true if the method has a Shiro annotation, false otherwise. + * @see org.springframework.aop.MethodMatcher#matches(java.lang.reflect.Method, Class) + */ + @Override + public boolean matches(Method method, Class targetClass) { + Method m = method; + + if ( isAuthzAnnotationPresent(m) ) { + return true; + } + + //The 'method' parameter could be from an interface that doesn't have the annotation. + //Check to see if the implementation has it. + if ( targetClass != null) { + try { + m = targetClass.getMethod(m.getName(), m.getParameterTypes()); + return isAuthzAnnotationPresent(m) || isAuthzAnnotationPresent(targetClass); + } catch (NoSuchMethodException ignored) { + //default return value is false. If we can't find the method, then obviously + //there is no annotation, so just use the default return value. + } + } + + return false; + } + + private boolean isAuthzAnnotationPresent(Class targetClazz) { + for( Class annClass : AUTHZ_ANNOTATION_CLASSES ) { + Annotation a = AnnotationUtils.findAnnotation(targetClazz, annClass); + if ( a != null ) { + return true; + } + } + return false; + } + + private boolean isAuthzAnnotationPresent(Method method) { + for( Class annClass : AUTHZ_ANNOTATION_CLASSES ) { + Annotation a = AnnotationUtils.findAnnotation(method, annClass); + if ( a != null ) { + return true; + } + } + return false; + } +} 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 deleted file mode 100644 index 71e7d7c..0000000 --- a/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionProxyAnnotationMethodInterceptor.java +++ /dev/null @@ -1,30 +0,0 @@ -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/aop/PermissionWrapperAnnotationMethodInterceptor.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionWrapperAnnotationMethodInterceptor.java new file mode 100644 index 0000000..1a2ba4d --- /dev/null +++ b/diboot-shiro/src/main/java/com/diboot/shiro/bind/aop/PermissionWrapperAnnotationMethodInterceptor.java @@ -0,0 +1,54 @@ +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/PermissionProxyAnnotationHandler.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionProxyAnnotationHandler.java deleted file mode 100644 index d303482..0000000 --- a/diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionProxyAnnotationHandler.java +++ /dev/null @@ -1,69 +0,0 @@ -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/bind/handler/PermissionWrapperAnnotationHandler.java b/diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionWrapperAnnotationHandler.java new file mode 100644 index 0000000..7478dcf --- /dev/null +++ b/diboot-shiro/src/main/java/com/diboot/shiro/bind/handler/PermissionWrapperAnnotationHandler.java @@ -0,0 +1,164 @@ +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 e755168..6b9c662 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,16 +1,11 @@ package com.diboot.shiro.config; -import com.diboot.shiro.bind.aop.PermissionProxyAnnotationMethodInterceptor; +import com.diboot.shiro.bind.aop.CustomAuthorizationAttributeSourceAdvisor; 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; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; @@ -22,9 +17,7 @@ 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; /*** @@ -101,20 +94,8 @@ 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); - + public CustomAuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { + CustomAuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new CustomAuthorizationAttributeSourceAdvisor(); 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 f06c135..7bdd498 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.PermissionsMenu; -import com.diboot.shiro.bind.annotation.RequiresPermissionsProxy; +import com.diboot.shiro.bind.annotation.PermissionsPrefix; +import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper; import com.diboot.shiro.entity.Permission; import com.diboot.shiro.service.PermissionService; import com.diboot.shiro.vo.PermissionVO; @@ -29,8 +29,8 @@ import java.util.List; * @version 2018/12/23 * Copyright © www.dibo.ltd */ -@PermissionsMenu(menuName = "权限", menuCode = "permission") @RestController +@PermissionsPrefix(prefix = "permission", code = "permission", name = "权限") @RequestMapping("/permission") public class PermissionController extends BaseCrudRestController { @@ -39,6 +39,20 @@ public class PermissionController extends BaseCrudRestController { @Autowired private PermissionService permissionService; + /*** + * 查询Entity + * @param id ID + * @return + * @throws Exception + */ + @GetMapping("/{id}") + @RequiresPermissionsWrapper(prefix = "permissionSelf", value = {"get"}, name = "查看") + public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request, ModelMap modelMap) + throws Exception{ + PermissionVO vo = permissionService.getViewObject(id, PermissionVO.class); + return new JsonResult(vo); + } + /*** * 查询ViewObject的分页数据 (此为非继承的自定义使用案例,更简化的调用父类案例请参考UserController) *

@@ -48,8 +62,7 @@ public class PermissionController extends BaseCrudRestController { * @throws Exception */ @GetMapping("/list") - @RequiresPermissionsProxy(value = {"permission:list"}, menuCode = "permission", - menuName = "权限", permissionName = "列表") + @RequiresPermissionsWrapper(value = {"list"}, name = "列表") public JsonResult getVOList(HttpServletRequest request) throws Exception{ QueryWrapper queryWrapper = buildQuery(request); // 构建分页 @@ -77,20 +90,6 @@ public class PermissionController extends BaseCrudRestController { return super.createEntity(entity, result, modelMap); } - /*** - * 查询Entity - * @param id ID - * @return - * @throws Exception - */ - @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); - return new JsonResult(vo); - } - /*** * 更新Entity * @param id ID diff --git a/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtAuthenticationFilter.java b/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtAuthenticationFilter.java index 2e53855..dd5447a 100644 --- a/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtAuthenticationFilter.java +++ b/diboot-shiro/src/main/java/com/diboot/shiro/jwt/BaseJwtAuthenticationFilter.java @@ -56,6 +56,8 @@ public class BaseJwtAuthenticationFilter extends BasicHttpAuthenticationFilter { @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { logger.debug("Token认证: onAccessDenied"); + HttpServletRequest httpRequest = (HttpServletRequest) request; + JsonResult jsonResult = new JsonResult(Status.FAIL_INVALID_TOKEN); this.responseJson((HttpServletResponse) response, jsonResult); return false; From 72b0bd703cadc94974e3b0cc5ee60c04a99e9316 Mon Sep 17 00:00:00 2001 From: mazhicheng Date: Thu, 20 Jun 2019 18:48:39 +0800 Subject: [PATCH 2/5] =?UTF-8?q?+=E4=BC=98=E5=8C=96V.isEmpty(Object),=20V.n?= =?UTF-8?q?otEmpty(Object)=E6=A0=A1=E9=AA=8C=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../diboot/core/mapper/BaseCrudMapper.java | 4 +-- .../diboot/core/mapper/MetadataMapper.java | 4 +-- .../src/main/java/com/diboot/core/util/V.java | 32 +++++++++++++++++-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java b/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java index 6f66a21..635856b 100644 --- a/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java +++ b/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java @@ -5,8 +5,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** * 基础CRUD的父类Mapper * @author Mazhicheng - * @version 2018/12/22 - * Copyright © www.dibo.ltd + * @version v2.0 + * @date 2018/12/22 */ public interface BaseCrudMapper extends BaseMapper { diff --git a/diboot-core/src/main/java/com/diboot/core/mapper/MetadataMapper.java b/diboot-core/src/main/java/com/diboot/core/mapper/MetadataMapper.java index 41bca4d..17c0c25 100644 --- a/diboot-core/src/main/java/com/diboot/core/mapper/MetadataMapper.java +++ b/diboot-core/src/main/java/com/diboot/core/mapper/MetadataMapper.java @@ -5,8 +5,8 @@ import com.diboot.core.entity.Metadata; /** * 元数据Mapper * @author Mazhicheng - * @version 2018/12/22 - * Copyright © www.dibo.ltd + * @version v2.0 + * @date 2018/12/22 */ public interface MetadataMapper extends BaseCrudMapper { diff --git a/diboot-core/src/main/java/com/diboot/core/util/V.java b/diboot-core/src/main/java/com/diboot/core/util/V.java index 91b0a22..91493f4 100644 --- a/diboot-core/src/main/java/com/diboot/core/util/V.java +++ b/diboot-core/src/main/java/com/diboot/core/util/V.java @@ -22,7 +22,21 @@ public class V { * @return */ public static boolean isEmpty(Object obj){ - return obj == null; + if(obj instanceof String){ + return isEmpty((String)obj); + } + else if(obj instanceof Collection){ + return isEmpty((Collection)obj); + } + else if(obj instanceof Map){ + return isEmpty((Map)obj); + } + else if(obj instanceof String[]){ + return isEmpty((String[])obj); + } + else{ + return obj == null; + } } /*** @@ -67,7 +81,21 @@ public class V { * @return */ public static boolean notEmpty(Object obj){ - return obj != null; + if(obj instanceof String){ + return notEmpty((String)obj); + } + else if(obj instanceof Collection){ + return notEmpty((Collection)obj); + } + else if(obj instanceof Map){ + return notEmpty((Map)obj); + } + else if(obj instanceof String[]){ + return notEmpty((String[])obj); + } + else{ + return obj != null; + } } /*** From 60053c83ef39c566dd009bd4faf88557baa927d3 Mon Sep 17 00:00:00 2001 From: godchao Date: Sat, 22 Jun 2019 12:08:27 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=85=AC=E4=BC=97=E5=8F=B7=E8=AE=A4=E8=AF=81=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- diboot-example/build.gradle | 1 + .../controller/AuthTokenController.java | 102 ++++++++++++++++++ diboot-shiro-wx-mp/build.gradle | 23 ++++ .../diboot/shiro/wx/mp/entity/WxMpMember.java | 20 ++++ .../shiro/wx/mp/mapper/WxMpMemberMapper.java | 14 +++ .../shiro/wx/mp/mapper/WxMpMemberMapper.xml | 5 + .../wx/mp/service/WxMpMemberService.java | 13 +++ .../shiro/wx/mp/service/WxMpServiceExt.java | 61 +++++++++++ .../service/impl/WxMpAuthWayServiceImpl.java | 74 +++++++++++++ .../service/impl/WxMpMemberServiceImpl.java | 19 ++++ .../com/diboot/shiro/config/ShiroConfig.java | 27 +++-- settings.gradle | 1 + 12 files changed, 350 insertions(+), 10 deletions(-) create mode 100755 diboot-shiro-wx-mp/build.gradle create mode 100755 diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/entity/WxMpMember.java create mode 100755 diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/mapper/WxMpMemberMapper.java create mode 100755 diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/mapper/WxMpMemberMapper.xml create mode 100755 diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/WxMpMemberService.java create mode 100755 diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/WxMpServiceExt.java create mode 100755 diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/impl/WxMpAuthWayServiceImpl.java create mode 100755 diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/impl/WxMpMemberServiceImpl.java diff --git a/diboot-example/build.gradle b/diboot-example/build.gradle index 1663ce7..02fd3c5 100644 --- a/diboot-example/build.gradle +++ b/diboot-example/build.gradle @@ -2,6 +2,7 @@ dependencies { compile project(":diboot-core") compile project(":diboot-shiro") + compile project(":diboot-shiro-wx-mp") testCompile group: 'junit', name: 'junit', version: '4.12' } \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/controller/AuthTokenController.java b/diboot-example/src/main/java/com/diboot/example/controller/AuthTokenController.java index d4c05f2..7c31a53 100644 --- a/diboot-example/src/main/java/com/diboot/example/controller/AuthTokenController.java +++ b/diboot-example/src/main/java/com/diboot/example/controller/AuthTokenController.java @@ -1,11 +1,18 @@ package com.diboot.example.controller; +import com.diboot.core.config.BaseConfig; +import com.diboot.core.util.V; import com.diboot.core.vo.JsonResult; import com.diboot.core.vo.Status; import com.diboot.shiro.jwt.BaseJwtAuthenticationToken; import com.diboot.shiro.config.AuthType; import com.diboot.shiro.entity.SysUser; import com.diboot.shiro.service.AuthWayService; +import com.diboot.shiro.util.JwtHelper; +import com.diboot.shiro.wx.mp.service.WxMpServiceExt; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; +import me.chanjar.weixin.mp.bean.result.WxMpUser; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; @@ -23,9 +30,14 @@ public class AuthTokenController { private static final Logger logger = LoggerFactory.getLogger(AuthTokenController.class); + private static final String STATE = BaseConfig.getProperty("wechat.state"); + @Autowired private Map authWayServiceMap; + @Autowired + private WxMpServiceExt wxMpService; + /*** * 用户名密码登录接口 * @param sysUser @@ -57,6 +69,95 @@ public class AuthTokenController { return new JsonResult(Status.FAIL_INVALID_TOKEN, errorMsg); } + + /** + * 创建OAuth认证链接(为了获取授权所需code) + * @param request + * @return + * @throws Exception + */ + @GetMapping("/buildOAuthUrl") + public JsonResult buildOAuthUrl4mp(HttpServletRequest request) throws Exception{ + String url = request.getParameter("url"); + if (V.isEmpty(url)){ + return new JsonResult(Status.FAIL_OPERATION, new String[]{"url为空,获取OAuth链接失败"}); + } + + String oauthUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAuth2Scope.SNSAPI_USERINFO, STATE); + return new JsonResult(Status.OK, oauthUrl, new String[]{"获取OAuth链接成功"}); + } + + /** + * 微信公众号的回调授权登录认证 + * @param request + * @return + * @throws Exception + */ + @PostMapping("/apply") + public JsonResult applyTokenByOAuth2mp(HttpServletRequest request) throws Exception{ + String code = request.getParameter("code"); + String state = request.getParameter("state"); + String openid = ""; + if (JwtHelper.isRequestTokenEffective(request)){ + String account = JwtHelper.getAccountFromToken(JwtHelper.getRequestToken(request)); + if (account == null){ + // 如果有code并且token已过期,则使用code获取openid + if (V.isEmpty(code)){ + return new JsonResult(Status.FAIL_INVALID_TOKEN, new String[]{"token已过期"}); + } + } else { + openid = account; + } + } + + // 如果openid没有通过token获取到,则通过code获取 + if (V.isEmpty(openid)){ + // 校验STATE + if (V.notEmpty(STATE) && !STATE.equals(state)){ + return new JsonResult(Status.FAIL_OPERATION, new String[]{"非法来源"}); + } + // 获取wxMpService + WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code); + if (!wxMpService.oauth2validateAccessToken(wxMpOAuth2AccessToken)){ + wxMpOAuth2AccessToken = wxMpService.oauth2refreshAccessToken(wxMpOAuth2AccessToken.getRefreshToken()); + } + WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null); + + openid = wxMpUser.getOpenId(); + } + + // 如果没有获取到wechat,则返回提示信息 + if (V.isEmpty(openid)){ + return new JsonResult(Status.FAIL_INVALID_TOKEN, new String[]{"获取信息失败"}); + } + + // 设置token + BaseJwtAuthenticationToken authToken = new BaseJwtAuthenticationToken(authWayServiceMap, openid, AuthType.WX_MP); + // 获取当前的Subject + Subject subject = SecurityUtils.getSubject(); + String token = null; + String errorMsg = null; + + try { + subject.login(authToken); + //验证是否登录成功 + if(subject.isAuthenticated()){ + token = (String)authToken.getCredentials(); + logger.debug("openid[" + openid + "]申请token成功!authtoken="+token); + } + } + catch(Exception e){ + logger.error("登录失败", e); + } + + if (V.isEmpty(token)){ + String msg = V.notEmpty(errorMsg) ? errorMsg : "申请token失败"; + return new JsonResult(Status.FAIL_INVALID_TOKEN, new String[]{msg}); + } + + return new JsonResult(Status.OK, token, new String[]{"申请token成功"}); + } + @PostMapping("/logout") public JsonResult logout(HttpServletRequest request, HttpServletResponse response) throws Exception{ Subject subject = SecurityUtils.getSubject(); @@ -66,4 +167,5 @@ public class AuthTokenController { return new JsonResult(Status.OK, new String[]{"退出登录成功"}); } + } diff --git a/diboot-shiro-wx-mp/build.gradle b/diboot-shiro-wx-mp/build.gradle new file mode 100755 index 0000000..b749ebe --- /dev/null +++ b/diboot-shiro-wx-mp/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'java' +} + +group 'com.diboot' +version '2.0-alpha' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + + compile project(":diboot-core") + compile project(":diboot-shiro") + + // 微信开发组件 + compile("com.github.binarywang:weixin-java-mp:3.2.0") + + testCompile group: 'junit', name: 'junit', version: '4.12' +} diff --git a/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/entity/WxMpMember.java b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/entity/WxMpMember.java new file mode 100755 index 0000000..e17ae49 --- /dev/null +++ b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/entity/WxMpMember.java @@ -0,0 +1,20 @@ +package com.diboot.shiro.wx.mp.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.entity.BaseEntity; +import lombok.Data; + +/*** + * @author Wangyl + * @version v2.0 + * @date 2019/6/10 + */ +@Data +public class WxMpMember extends BaseEntity { + + private static final long serialVersionUID = -106928701430810778L; + + @TableField + private String openid; + +} diff --git a/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/mapper/WxMpMemberMapper.java b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/mapper/WxMpMemberMapper.java new file mode 100755 index 0000000..a57f832 --- /dev/null +++ b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/mapper/WxMpMemberMapper.java @@ -0,0 +1,14 @@ +package com.diboot.shiro.wx.mp.mapper; + +import com.diboot.core.mapper.BaseCrudMapper; +import com.diboot.shiro.wx.mp.entity.WxMpMember; + +/*** + * @author Wangyl + * @version v2.0 + * @date 2019/6/10 + */ +public interface WxMpMemberMapper extends BaseCrudMapper { + +} + diff --git a/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/mapper/WxMpMemberMapper.xml b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/mapper/WxMpMemberMapper.xml new file mode 100755 index 0000000..d9de206 --- /dev/null +++ b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/mapper/WxMpMemberMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/WxMpMemberService.java b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/WxMpMemberService.java new file mode 100755 index 0000000..4fd804a --- /dev/null +++ b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/WxMpMemberService.java @@ -0,0 +1,13 @@ +package com.diboot.shiro.wx.mp.service; + +import com.diboot.core.service.BaseService; +import com.diboot.shiro.wx.mp.entity.WxMpMember; + +/*** + * @author Wangyl + * @version v2.0 + * @date 2019/6/10 + */ +public interface WxMpMemberService extends BaseService { + +} diff --git a/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/WxMpServiceExt.java b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/WxMpServiceExt.java new file mode 100755 index 0000000..bd51d6e --- /dev/null +++ b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/WxMpServiceExt.java @@ -0,0 +1,61 @@ +package com.diboot.shiro.wx.mp.service; + +import com.diboot.core.config.BaseConfig; +import com.diboot.core.util.V; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage; +import me.chanjar.weixin.mp.api.WxMpMessageHandler; +import me.chanjar.weixin.mp.api.WxMpMessageRouter; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.Map; + +@Service +public class WxMpServiceExt extends WxMpServiceImpl { + private static final Logger logger = LoggerFactory.getLogger(WxMpServiceExt.class); + + private WxMpMessageRouter wxMpMessageRouter; + + private static final String appId = BaseConfig.getProperty("wechat.appId"); + private static final String secret = BaseConfig.getProperty("wechat.secret"); + private static final String token = BaseConfig.getProperty("wechat.token"); + private static final String aesKey = BaseConfig.getProperty("wechat.aesKey"); + + @PostConstruct + public void init(){ + WxMpInMemoryConfigStorage inMemoryConfigStorage = new WxMpInMemoryConfigStorage(); + + if (V.isEmpty(appId) || V.isEmpty(secret)){ + logger.warn("服务号相关的appid或secret为空,请检查application.properties配置文件"); + } + + inMemoryConfigStorage.setAppId(appId); + inMemoryConfigStorage.setSecret(secret); + inMemoryConfigStorage.setToken(token); + inMemoryConfigStorage.setAesKey(aesKey); + + setWxMpConfigStorage(inMemoryConfigStorage); + + wxMpMessageRouter = new WxMpMessageRouter(this); + wxMpMessageRouter.rule().handler(new WxMpMessageHandler() { + @Override + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException { + logger.info("\n接收到 {} 公众号请求消息,内容:{}", wxMpService.getWxMpConfigStorage().getAppId(), wxMpXmlMessage); + return null; + } + }).next(); + } + + public WxMpMessageRouter getWxMpMessageRouter(){ + return wxMpMessageRouter; + } + +} diff --git a/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/impl/WxMpAuthWayServiceImpl.java b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/impl/WxMpAuthWayServiceImpl.java new file mode 100755 index 0000000..8f7a329 --- /dev/null +++ b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/impl/WxMpAuthWayServiceImpl.java @@ -0,0 +1,74 @@ +package com.diboot.shiro.wx.mp.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.entity.BaseEntity; +import com.diboot.core.util.V; +import com.diboot.shiro.config.AuthType; +import com.diboot.shiro.jwt.BaseJwtAuthenticationToken; +import com.diboot.shiro.service.AuthWayService; +import com.diboot.shiro.wx.mp.entity.WxMpMember; +import com.diboot.shiro.wx.mp.service.WxMpMemberService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/*** + * 微信公众号认证实现 + * @author Wangyl + * @version v2.0 + * @date 2019/6/10 + */ +@Service +public class WxMpAuthWayServiceImpl implements AuthWayService { + + @Autowired + private WxMpMemberService wxMpMemberService; + + private AuthType authType = AuthType.WX_MP; + + private BaseJwtAuthenticationToken token; + + @Override + public AuthType authType() { + return authType; + } + + @Override + public void initByToken(BaseJwtAuthenticationToken token) { + this.token = token; + } + + @Override + public BaseEntity getUser() { + QueryWrapper query = new QueryWrapper(); + query.lambda() + .eq(WxMpMember::getOpenid, token.getAccount()); + + List wxMpMemberList = wxMpMemberService.getEntityList(query); + if (V.isEmpty(wxMpMemberList)){ + return null; + } + return wxMpMemberList.get(0); + } + + @Override + public boolean requirePassword() { + return authType.isRequirePassword(); + } + + @Override + public boolean isPasswordMatch() { + return true; + } + + @Override + public boolean isPreliminaryVerified() { + return false; + } + + @Override + public Long getExpiresInMinutes() { + return null; + } +} diff --git a/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/impl/WxMpMemberServiceImpl.java b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/impl/WxMpMemberServiceImpl.java new file mode 100755 index 0000000..cf29c44 --- /dev/null +++ b/diboot-shiro-wx-mp/src/main/java/com/diboot/shiro/wx/mp/service/impl/WxMpMemberServiceImpl.java @@ -0,0 +1,19 @@ +package com.diboot.shiro.wx.mp.service.impl; + +import com.diboot.core.service.impl.BaseServiceImpl; +import com.diboot.shiro.wx.mp.entity.WxMpMember; +import com.diboot.shiro.wx.mp.mapper.WxMpMemberMapper; +import com.diboot.shiro.wx.mp.service.WxMpMemberService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/*** + * @author Wangyl + * @version v2.0 + * @date 2019/6/10 + */ +@Service +@Slf4j +public class WxMpMemberServiceImpl extends BaseServiceImpl implements WxMpMemberService { + +} 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..6eeac91 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 @@ -53,16 +53,7 @@ public class ShiroConfig { //用户访问未对其授权的资源时的错误提示页面 shiroFilterFactoryBean.setUnauthorizedUrl("/error"); - Map filterChainDefinitionMap = new LinkedHashMap<>(); - - filterChainDefinitionMap.put("/", "anon"); - filterChainDefinitionMap.put("/static/**", "anon"); - filterChainDefinitionMap.put("/auth/login", "anon"); - filterChainDefinitionMap.put("/error", "anon"); - filterChainDefinitionMap.put("/auth/logout", "logout"); - filterChainDefinitionMap.put("/**", "jwt"); - - shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); + shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinition()); // 设置过滤器 Map filters = new LinkedHashMap<>(); @@ -73,6 +64,22 @@ public class ShiroConfig { return shiroFilterFactoryBean; } + @Bean + public Map filterChainDefinition(){ + Map filterChainDefinitionMap = new LinkedHashMap<>(); + + filterChainDefinitionMap.put("/", "anon"); + filterChainDefinitionMap.put("/static/**", "anon"); + filterChainDefinitionMap.put("/auth/login", "anon"); + filterChainDefinitionMap.put("/auth/buildOAuthUrl", "anon"); + filterChainDefinitionMap.put("/auth/apply", "anon"); + filterChainDefinitionMap.put("/error", "anon"); + filterChainDefinitionMap.put("/auth/logout", "logout"); + filterChainDefinitionMap.put("/**", "jwt"); + + return filterChainDefinitionMap; + } + /** * Shiro生命周期处理器 */ diff --git a/settings.gradle b/settings.gradle index c167669..ddeadc4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,5 +11,6 @@ rootProject.name = 'diboot-v2' include 'diboot-core' include 'diboot-example' include 'diboot-shiro' +include 'diboot-shiro-wx-mp' include 'diboot-docs' From f73ac7ecfec168de076ead06a28f3c7656d8b41f Mon Sep 17 00:00:00 2001 From: mazhicheng Date: Sat, 22 Jun 2019 15:35:31 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E7=BB=91=E5=AE=9A=E7=9B=B8=E5=85=B3=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/diboot/core/service/BaseService.java | 3 +- .../core/service/impl/BaseServiceImpl.java | 1 - .../core/test/binder/TestEntityBinder.java | 54 ++++++++++++ .../test/binder/TestEntityListBinder.java | 83 ++++++++++++++++++ .../core/test/binder/TestFieldBinder.java | 57 ++++++++++++ .../core/test/binder/entity/Department.java | 47 ++++++++++ .../core/test/binder/entity/Organization.java | 47 ++++++++++ .../diboot/core/test/binder/entity/Role.java | 48 ++++++++++ .../diboot/core/test/binder/entity/User.java | 46 ++++++++++ .../test/binder/mapper/DepartmentMapper.java | 14 +++ .../binder/mapper/OrganizationMapper.java | 15 ++++ .../core/test/binder/mapper/RoleMapper.java | 16 ++++ .../core/test/binder/mapper/UserMapper.java | 15 ++++ .../binder/service/DepartmentService.java | 14 +++ .../binder/service/OrganizationService.java | 14 +++ .../core/test/binder/service/RoleService.java | 15 ++++ .../core/test/binder/service/UserService.java | 14 +++ .../service/impl/DepartmentServiceImpl.java | 18 ++++ .../service/impl/OrganizationServiceImpl.java | 18 ++++ .../binder/service/impl/RoleServiceImpl.java | 18 ++++ .../binder/service/impl/UserServiceImpl.java | 18 ++++ .../core/test/binder/vo/EntityBinderVO.java | 37 ++++++++ .../binder/vo/EntityListComplexBinderVO.java | 29 +++++++ .../binder/vo/EntityListSimpleBinderVO.java | 27 ++++++ .../core/test/binder/vo/FieldBinderVO.java | 60 +++++++++++++ .../core/test/config/SpringMvcConfig.java | 4 +- diboot-core/src/test/resources/init-mysql.sql | 87 +++++++++++++++++++ .../example/mapper/DepartmentMapper.xml | 5 -- .../example/mapper/OrganizationMapper.xml | 5 -- .../java/com/diboot/example/vo/UserVO.java | 6 +- .../resources/META-INF/sql/init-mysql.sql | 8 +- diboot-example/src/test/java/MainTest.java | 9 -- .../com/diboot/example/ApplicationTest.java | 16 ---- .../service/DepartmentServiceTest.java | 31 ------- 34 files changed, 821 insertions(+), 78 deletions(-) create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/TestEntityBinder.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/TestEntityListBinder.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/TestFieldBinder.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/entity/Department.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/entity/Organization.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/entity/Role.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/entity/User.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/mapper/DepartmentMapper.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/mapper/OrganizationMapper.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/mapper/RoleMapper.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/mapper/UserMapper.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/service/DepartmentService.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/service/OrganizationService.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/service/RoleService.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/service/UserService.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/service/impl/DepartmentServiceImpl.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/service/impl/OrganizationServiceImpl.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/service/impl/RoleServiceImpl.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/service/impl/UserServiceImpl.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/vo/EntityBinderVO.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/vo/EntityListComplexBinderVO.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/vo/EntityListSimpleBinderVO.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/vo/FieldBinderVO.java create mode 100644 diboot-core/src/test/resources/init-mysql.sql delete mode 100644 diboot-example/src/main/java/com/diboot/example/mapper/DepartmentMapper.xml delete mode 100644 diboot-example/src/main/java/com/diboot/example/mapper/OrganizationMapper.xml delete mode 100644 diboot-example/src/test/java/MainTest.java delete mode 100644 diboot-example/src/test/java/com/diboot/example/ApplicationTest.java delete mode 100644 diboot-example/src/test/java/com/diboot/example/service/DepartmentServiceTest.java diff --git a/diboot-core/src/main/java/com/diboot/core/service/BaseService.java b/diboot-core/src/main/java/com/diboot/core/service/BaseService.java index 9e55160..d5b9ba5 100644 --- a/diboot-core/src/main/java/com/diboot/core/service/BaseService.java +++ b/diboot-core/src/main/java/com/diboot/core/service/BaseService.java @@ -1,6 +1,7 @@ package com.diboot.core.service; import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.extension.service.IService; import com.diboot.core.binding.EntityBinder; import com.diboot.core.binding.EntityListBinder; import com.diboot.core.binding.FieldBinder; @@ -18,7 +19,7 @@ import java.util.Map; * @version 2.0 * @date 2019/01/01 */ -public interface BaseService{ +public interface BaseService { /** * 获取Entity实体 diff --git a/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java b/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java index b5167c9..e11f47d 100644 --- a/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java +++ b/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java @@ -12,7 +12,6 @@ import com.diboot.core.config.BaseConfig; import com.diboot.core.config.Cons; import com.diboot.core.mapper.BaseCrudMapper; import com.diboot.core.service.BaseService; -import com.diboot.core.util.BeanUtils; import com.diboot.core.util.S; import com.diboot.core.util.V; import com.diboot.core.vo.KeyValue; diff --git a/diboot-core/src/test/java/diboot/core/test/binder/TestEntityBinder.java b/diboot-core/src/test/java/diboot/core/test/binder/TestEntityBinder.java new file mode 100644 index 0000000..f54c430 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/TestEntityBinder.java @@ -0,0 +1,54 @@ +package diboot.core.test.binder; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.diboot.core.binding.manager.AnnotationBindingManager; +import com.diboot.core.util.JSON; +import com.diboot.core.util.V; +import diboot.core.test.StartupApplication; +import diboot.core.test.binder.entity.User; +import diboot.core.test.binder.service.UserService; +import diboot.core.test.binder.vo.EntityBinderVO; +import diboot.core.test.binder.vo.FieldBinderVO; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.List; + +/** + * 测试字段绑定 + * @author Mazhicheng + * @version v2.0 + * @date 2019/06/22 + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = {StartupApplication.class}) +public class TestEntityBinder { + + @Autowired + UserService userService; + + @Test + public void testBinder(){ + // 加载测试数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(User::getId, 1001L, 1002L); + List userList = userService.getEntityList(queryWrapper); + // 自动绑定 + List voList = AnnotationBindingManager.autoConvertAndBind(userList, EntityBinderVO.class); + // 验证绑定结果 + if(V.notEmpty(voList)){ + for(EntityBinderVO vo : voList){ + // 验证直接关联和通过中间表间接关联的绑定 + Assert.assertEquals(vo.getDepartmentId(), vo.getDepartment().getId()); + Assert.assertNotNull(vo.getDepartment().getOrgId()); + Assert.assertNotNull(vo.getOrganization()); + System.out.println(JSON.stringify(vo)); + } + } + } + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/TestEntityListBinder.java b/diboot-core/src/test/java/diboot/core/test/binder/TestEntityListBinder.java new file mode 100644 index 0000000..953a970 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/TestEntityListBinder.java @@ -0,0 +1,83 @@ +package diboot.core.test.binder; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.binding.manager.AnnotationBindingManager; +import com.diboot.core.util.JSON; +import com.diboot.core.util.V; +import diboot.core.test.StartupApplication; +import diboot.core.test.binder.entity.Department; +import diboot.core.test.binder.entity.User; +import diboot.core.test.binder.service.DepartmentService; +import diboot.core.test.binder.service.UserService; +import diboot.core.test.binder.vo.EntityBinderVO; +import diboot.core.test.binder.vo.EntityListComplexBinderVO; +import diboot.core.test.binder.vo.EntityListSimpleBinderVO; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.List; + +/** + * 测试字段绑定 + * @author Mazhicheng + * @version v2.0 + * @date 2019/06/22 + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = {StartupApplication.class}) +public class TestEntityListBinder { + + @Autowired + UserService userService; + + @Autowired + DepartmentService departmentService; + + /** + * 验证直接关联的绑定 + */ + @Test + public void testSimpleBinder(){ + // 加载测试数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Department::getId, 10001L); + List entityList = departmentService.getEntityList(queryWrapper); + // 自动绑定 + List voList = AnnotationBindingManager.autoConvertAndBind(entityList, EntityListSimpleBinderVO.class); + // 验证绑定结果 + if(V.notEmpty(voList)){ + for(EntityListSimpleBinderVO vo : voList){ + // 验证直接关联的绑定 + Assert.assertTrue(V.notEmpty(vo.getChildren())); + System.out.println(JSON.stringify(vo)); + } + } + } + + /** + * 验证通过中间表间接关联的绑定 + */ + @Test + public void testComplexBinder(){ + // 加载测试数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(User::getId, 1001L, 1002L); + List userList = userService.getEntityList(queryWrapper); + // 自动绑定 + List voList = AnnotationBindingManager.autoConvertAndBind(userList, EntityListComplexBinderVO.class); + // 验证绑定结果 + if(V.notEmpty(voList)){ + for(EntityListComplexBinderVO vo : voList){ + // 验证通过中间表间接关联的绑定 + Assert.assertTrue(V.notEmpty(vo.getRoleList())); + System.out.println(JSON.stringify(vo)); + } + } + } + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/TestFieldBinder.java b/diboot-core/src/test/java/diboot/core/test/binder/TestFieldBinder.java new file mode 100644 index 0000000..897bb27 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/TestFieldBinder.java @@ -0,0 +1,57 @@ +package diboot.core.test.binder; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.diboot.core.binding.manager.AnnotationBindingManager; +import com.diboot.core.util.JSON; +import com.diboot.core.util.V; +import com.diboot.core.vo.Pagination; +import diboot.core.test.StartupApplication; +import diboot.core.test.binder.entity.User; +import diboot.core.test.binder.service.UserService; +import diboot.core.test.binder.vo.FieldBinderVO; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.List; + +/** + * 测试字段绑定 + * @author Mazhicheng + * @version v2.0 + * @date 2019/06/22 + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = {StartupApplication.class}) +public class TestFieldBinder { + + @Autowired + UserService userService; + + @Test + public void testBinder(){ + // 加载测试数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(User::getId, 1001L, 1002L); + List userList = userService.getEntityList(queryWrapper); + // 自动绑定 + List voList = AnnotationBindingManager.autoConvertAndBind(userList, FieldBinderVO.class); + // 验证绑定结果 + if(V.notEmpty(voList)){ + for(FieldBinderVO vo : voList){ + // 验证直接关联和通过中间表间接关联的绑定 + Assert.assertNotNull(vo.getDeptName()); + Assert.assertNotNull(vo.getOrgName()); + Assert.assertNotNull(vo.getOrgTelphone()); + // 验证枚举值已绑定 + Assert.assertNotNull(vo.getGenderLabel()); + + System.out.println(JSON.stringify(vo)); + } + } + } + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/entity/Department.java b/diboot-core/src/test/java/diboot/core/test/binder/entity/Department.java new file mode 100644 index 0000000..31709f2 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/entity/Department.java @@ -0,0 +1,47 @@ +package diboot.core.test.binder.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.entity.BaseEntity; + +/** + * 定时任务 + * @author Mazhicheng + * @version v2.0 + * @date 2018/12/27 + */ +public class Department extends BaseEntity { + private static final long serialVersionUID = -4849732665419794547L; + + @TableField + private Long parentId; + + @TableField + private Long orgId; + + @TableField + private String name; + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public Long getOrgId() { + return orgId; + } + + public void setOrgId(Long orgId) { + this.orgId = orgId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/diboot-core/src/test/java/diboot/core/test/binder/entity/Organization.java b/diboot-core/src/test/java/diboot/core/test/binder/entity/Organization.java new file mode 100644 index 0000000..493d24f --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/entity/Organization.java @@ -0,0 +1,47 @@ +package diboot.core.test.binder.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.entity.BaseEntity; + +/** + * 单位Entity + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +public class Organization extends BaseEntity { + private static final long serialVersionUID = -5889309041570465909L; + + @TableField + private Long parentId; + + @TableField + private String name; + + @TableField + private String telphone; + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTelphone() { + return telphone; + } + + public void setTelphone(String telphone) { + this.telphone = telphone; + } +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/entity/Role.java b/diboot-core/src/test/java/diboot/core/test/binder/entity/Role.java new file mode 100644 index 0000000..ea0fa3c --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/entity/Role.java @@ -0,0 +1,48 @@ +package diboot.core.test.binder.entity; + +import com.alibaba.fastjson.annotation.JSONField; +import com.diboot.core.entity.BaseEntity; + +import java.util.Date; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +public class Role extends BaseEntity { + private static final long serialVersionUID = 3701095453152116088L; + + private String name; + + private String code; + + @JSONField(serialize = false) + public Date createTime; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/entity/User.java b/diboot-core/src/test/java/diboot/core/test/binder/entity/User.java new file mode 100644 index 0000000..0b6fe45 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/entity/User.java @@ -0,0 +1,46 @@ +package diboot.core.test.binder.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.entity.BaseEntity; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +public class User extends BaseEntity { + private static final long serialVersionUID = 3050761344045195972L; + + @TableField + private Long departmentId; + + @TableField + private String username; + + @TableField + private String gender; + + public Long getDepartmentId() { + return departmentId; + } + + public void setDepartmentId(Long departmentId) { + this.departmentId = departmentId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/mapper/DepartmentMapper.java b/diboot-core/src/test/java/diboot/core/test/binder/mapper/DepartmentMapper.java new file mode 100644 index 0000000..a55a225 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/mapper/DepartmentMapper.java @@ -0,0 +1,14 @@ +package diboot.core.test.binder.mapper; + +import com.diboot.core.mapper.BaseCrudMapper; +import diboot.core.test.binder.entity.Department; + +/** + * 部门Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface DepartmentMapper extends BaseCrudMapper { + +} \ No newline at end of file diff --git a/diboot-core/src/test/java/diboot/core/test/binder/mapper/OrganizationMapper.java b/diboot-core/src/test/java/diboot/core/test/binder/mapper/OrganizationMapper.java new file mode 100644 index 0000000..9ecb5eb --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/mapper/OrganizationMapper.java @@ -0,0 +1,15 @@ +package diboot.core.test.binder.mapper; + +import com.diboot.core.mapper.BaseCrudMapper; +import diboot.core.test.binder.entity.Organization; + +/** + * 单位Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface OrganizationMapper extends BaseCrudMapper { + +} + diff --git a/diboot-core/src/test/java/diboot/core/test/binder/mapper/RoleMapper.java b/diboot-core/src/test/java/diboot/core/test/binder/mapper/RoleMapper.java new file mode 100644 index 0000000..9bb7a10 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/mapper/RoleMapper.java @@ -0,0 +1,16 @@ +package diboot.core.test.binder.mapper; + +import com.diboot.core.mapper.BaseCrudMapper; +import diboot.core.test.binder.entity.Role; +import diboot.core.test.binder.entity.User; + +/** + * 员工Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface RoleMapper extends BaseCrudMapper { + +} + diff --git a/diboot-core/src/test/java/diboot/core/test/binder/mapper/UserMapper.java b/diboot-core/src/test/java/diboot/core/test/binder/mapper/UserMapper.java new file mode 100644 index 0000000..4c19bb0 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/mapper/UserMapper.java @@ -0,0 +1,15 @@ +package diboot.core.test.binder.mapper; + +import com.diboot.core.mapper.BaseCrudMapper; +import diboot.core.test.binder.entity.User; + +/** + * 员工Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface UserMapper extends BaseCrudMapper { + +} + diff --git a/diboot-core/src/test/java/diboot/core/test/binder/service/DepartmentService.java b/diboot-core/src/test/java/diboot/core/test/binder/service/DepartmentService.java new file mode 100644 index 0000000..056612a --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/service/DepartmentService.java @@ -0,0 +1,14 @@ +package diboot.core.test.binder.service; + +import com.diboot.core.service.BaseService; +import diboot.core.test.binder.entity.Department; + +/** + * 部门相关Service + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +public interface DepartmentService extends BaseService { + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/service/OrganizationService.java b/diboot-core/src/test/java/diboot/core/test/binder/service/OrganizationService.java new file mode 100644 index 0000000..307f67f --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/service/OrganizationService.java @@ -0,0 +1,14 @@ +package diboot.core.test.binder.service; + +import com.diboot.core.service.BaseService; +import diboot.core.test.binder.entity.Organization; + +/** + * 单位相关Service + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +public interface OrganizationService extends BaseService { + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/service/RoleService.java b/diboot-core/src/test/java/diboot/core/test/binder/service/RoleService.java new file mode 100644 index 0000000..5aca866 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/service/RoleService.java @@ -0,0 +1,15 @@ +package diboot.core.test.binder.service; + +import com.diboot.core.service.BaseService; +import diboot.core.test.binder.entity.Role; +import diboot.core.test.binder.entity.User; + +/** + * 角色相关Service + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +public interface RoleService extends BaseService { + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/service/UserService.java b/diboot-core/src/test/java/diboot/core/test/binder/service/UserService.java new file mode 100644 index 0000000..8ccb7b3 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/service/UserService.java @@ -0,0 +1,14 @@ +package diboot.core.test.binder.service; + +import com.diboot.core.service.BaseService; +import diboot.core.test.binder.entity.User; + +/** + * 员工相关Service + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +public interface UserService extends BaseService { + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/service/impl/DepartmentServiceImpl.java b/diboot-core/src/test/java/diboot/core/test/binder/service/impl/DepartmentServiceImpl.java new file mode 100644 index 0000000..4cadf69 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/service/impl/DepartmentServiceImpl.java @@ -0,0 +1,18 @@ +package diboot.core.test.binder.service.impl; + +import com.diboot.core.service.impl.BaseServiceImpl; +import diboot.core.test.binder.entity.Department; +import diboot.core.test.binder.mapper.DepartmentMapper; +import diboot.core.test.binder.service.DepartmentService; +import org.springframework.stereotype.Service; + +/** + * 部门相关Service实现 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +@Service +public class DepartmentServiceImpl extends BaseServiceImpl implements DepartmentService { + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/service/impl/OrganizationServiceImpl.java b/diboot-core/src/test/java/diboot/core/test/binder/service/impl/OrganizationServiceImpl.java new file mode 100644 index 0000000..c36a16f --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/service/impl/OrganizationServiceImpl.java @@ -0,0 +1,18 @@ +package diboot.core.test.binder.service.impl; + +import com.diboot.core.service.impl.BaseServiceImpl; +import diboot.core.test.binder.entity.Organization; +import diboot.core.test.binder.mapper.OrganizationMapper; +import diboot.core.test.binder.service.OrganizationService; +import org.springframework.stereotype.Service; + +/** + * 单位相关Service实现 + * @author Mazhicheng + * @version 2018/12/23 + * Copyright © www.dibo.ltd + */ +@Service +public class OrganizationServiceImpl extends BaseServiceImpl implements OrganizationService { + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/service/impl/RoleServiceImpl.java b/diboot-core/src/test/java/diboot/core/test/binder/service/impl/RoleServiceImpl.java new file mode 100644 index 0000000..3ec8977 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/service/impl/RoleServiceImpl.java @@ -0,0 +1,18 @@ +package diboot.core.test.binder.service.impl; + +import com.diboot.core.service.impl.BaseServiceImpl; +import diboot.core.test.binder.entity.Role; +import diboot.core.test.binder.mapper.RoleMapper; +import diboot.core.test.binder.service.RoleService; +import org.springframework.stereotype.Service; + +/** + * 员工相关Service + * @author Mazhicheng + * @version 2018/12/23 + * Copyright © www.dibo.ltd + */ +@Service +public class RoleServiceImpl extends BaseServiceImpl implements RoleService { + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/service/impl/UserServiceImpl.java b/diboot-core/src/test/java/diboot/core/test/binder/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..78d924e --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/service/impl/UserServiceImpl.java @@ -0,0 +1,18 @@ +package diboot.core.test.binder.service.impl; + +import com.diboot.core.service.impl.BaseServiceImpl; +import diboot.core.test.binder.entity.User; +import diboot.core.test.binder.mapper.UserMapper; +import diboot.core.test.binder.service.UserService; +import org.springframework.stereotype.Service; + +/** + * 员工相关Service + * @author Mazhicheng + * @version 2018/12/23 + * Copyright © www.dibo.ltd + */ +@Service +public class UserServiceImpl extends BaseServiceImpl implements UserService { + +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/vo/EntityBinderVO.java b/diboot-core/src/test/java/diboot/core/test/binder/vo/EntityBinderVO.java new file mode 100644 index 0000000..2d377f5 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/vo/EntityBinderVO.java @@ -0,0 +1,37 @@ +package diboot.core.test.binder.vo; + +import com.diboot.core.binding.annotation.BindEntity; +import diboot.core.test.binder.entity.Department; +import diboot.core.test.binder.entity.Organization; +import diboot.core.test.binder.entity.User; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +public class EntityBinderVO extends User { + private static final long serialVersionUID = 3526115343377985725L; + + // 字段关联,相同条件的entity+condition将合并为一条SQL查询 + @BindEntity(entity= Department.class, condition="this.department_id=id AND parent_id >= 0") + private Department department; + + // 通过中间表关联Entity + @BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id") // AND ... + private Organization organization; + + + public Department getDepartment() { + return department; + } + public void setDepartment(Department department) { + this.department = department; + } + public Organization getOrganization() { + return organization; + } + public void setOrganization(Organization organization) { + this.organization = organization; + } +} \ No newline at end of file diff --git a/diboot-core/src/test/java/diboot/core/test/binder/vo/EntityListComplexBinderVO.java b/diboot-core/src/test/java/diboot/core/test/binder/vo/EntityListComplexBinderVO.java new file mode 100644 index 0000000..6e110f2 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/vo/EntityListComplexBinderVO.java @@ -0,0 +1,29 @@ +package diboot.core.test.binder.vo; + +import com.diboot.core.binding.annotation.BindEntityList; +import diboot.core.test.binder.entity.Role; +import diboot.core.test.binder.entity.User; + +import java.util.List; + +/** + * + * + * @author Mazhicheng + * @version v2.0 + * @date 2019/06/22 + */ +public class EntityListComplexBinderVO extends User { + + // 支持通过中间表的多-多Entity实体关联 + @BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id") + private List roleList; + + public List getRoleList() { + return roleList; + } + + public void setRoleList(List roleList) { + this.roleList = roleList; + } +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/vo/EntityListSimpleBinderVO.java b/diboot-core/src/test/java/diboot/core/test/binder/vo/EntityListSimpleBinderVO.java new file mode 100644 index 0000000..8829232 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/vo/EntityListSimpleBinderVO.java @@ -0,0 +1,27 @@ +package diboot.core.test.binder.vo; + +import com.diboot.core.binding.annotation.BindEntityList; +import diboot.core.test.binder.entity.Department; + +import java.util.List; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +public class EntityListSimpleBinderVO extends Department { + private static final long serialVersionUID = -362116388664907913L; + + // 直接关联多个Entity + @BindEntityList(entity = Department.class, condition = "this.id=parent_id") + private List children; + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} \ No newline at end of file diff --git a/diboot-core/src/test/java/diboot/core/test/binder/vo/FieldBinderVO.java b/diboot-core/src/test/java/diboot/core/test/binder/vo/FieldBinderVO.java new file mode 100644 index 0000000..47be943 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/vo/FieldBinderVO.java @@ -0,0 +1,60 @@ +package diboot.core.test.binder.vo; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.binding.annotation.BindField; +import com.diboot.core.binding.annotation.BindMetadata; +import diboot.core.test.binder.entity.Department; +import diboot.core.test.binder.entity.Organization; +import diboot.core.test.binder.entity.User; + +/** + * + * + * @author Mazhicheng + * @version v2.0 + * @date 2019/06/22 + */ +public class FieldBinderVO extends User{ + private static final long serialVersionUID = 3526115343377985725L; + + // 字段关联,相同条件的entity+condition将合并为一条SQL查询 + @BindField(entity= Department.class, field="name", condition="this.department_id=id AND parent_id IS NOT NULL") + private String deptName; + + // 支持级联字段关联,相同条件的entity+condition将合并为一条SQL查询 + @BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id") + private String orgName; + @BindField(entity = Organization.class, field="telphone", condition="this.department_id=department.id AND department.org_id=id") + private String orgTelphone; + + // 绑定元数据枚举 + @BindMetadata(type="GENDER", field = "gender") + private String genderLabel; + + public String getDeptName() { + return deptName; + } + public void setDeptName(String deptName) { + this.deptName = deptName; + } + public String getOrgName() { + return orgName; + } + public void setOrgName(String orgName) { + this.orgName = orgName; + } + public String getOrgTelphone() { + return orgTelphone; + } + public void setOrgTelphone(String orgTelphone) { + this.orgTelphone = orgTelphone; + } + + public String getGenderLabel() { + return genderLabel; + } + + public void setGenderLabel(String genderLabel) { + this.genderLabel = genderLabel; + } +} diff --git a/diboot-core/src/test/java/diboot/core/test/config/SpringMvcConfig.java b/diboot-core/src/test/java/diboot/core/test/config/SpringMvcConfig.java index f22963e..25aac14 100644 --- a/diboot-core/src/test/java/diboot/core/test/config/SpringMvcConfig.java +++ b/diboot-core/src/test/java/diboot/core/test/config/SpringMvcConfig.java @@ -32,8 +32,8 @@ import java.util.List; @Configuration @EnableAutoConfiguration @EnableTransactionManagement(proxyTargetClass=true) -@ComponentScan(basePackages={"com.diboot"}) -@MapperScan({"com.diboot.**.mapper"}) +@ComponentScan(basePackages={"com.diboot", "diboot.core"}) +@MapperScan({"com.diboot.**.mapper", "diboot.**.mapper"}) public class SpringMvcConfig implements WebMvcConfigurer{ private static final Logger log = LoggerFactory.getLogger(SpringMvcConfig.class); diff --git a/diboot-core/src/test/resources/init-mysql.sql b/diboot-core/src/test/resources/init-mysql.sql new file mode 100644 index 0000000..9ef6180 --- /dev/null +++ b/diboot-core/src/test/resources/init-mysql.sql @@ -0,0 +1,87 @@ +-- create schema diboot_example collate utf8_general_ci; +-- 初始化表 +create table department +( + id bigint unsigned not null comment 'ID' + primary key, + parent_id bigint default 0 not null comment '上级部门ID', + org_id bigint not null comment '单位ID', + name varchar(50) not null comment '名称', + extdata varchar(100) null comment '扩展字段', + deleted tinyint(1) default 0 not null comment '已删除', + create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间' +) + comment '组织单位' charset=utf8mb4; + +create table metadata +( + id int unsigned auto_increment comment 'ID' + primary key, + parent_id int unsigned not null comment '父ID', + type varchar(50) not null comment '元数据类型', + item_name varchar(100) not null comment '元数据项显示名', + item_value varchar(100) null comment '元数据项存储值', + comment varchar(200) null comment '备注', + extdata varchar(200) null comment '扩展属性', + sort_id smallint(6) default 99 not null comment '排序号', + `system` tinyint(1) default 0 not null comment '是否是系统预置', + editable tinyint(1) default 1 not null comment '是否可编辑', + deleted tinyint(1) default 0 not null comment '已删除', + create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间' +); + +create table organization +( + id int auto_increment comment 'ID' + primary key, + parent_id int default 0 not null comment '上级单位ID', + name varchar(100) not null comment '名称', + telphone varchar(20) null comment '电话', + deleted tinyint(1) default 0 not null comment '是否有效', + create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间' +) + comment '组织单位' charset=utf8mb4; + +create table role +( + id int auto_increment comment 'ID' + primary key, + name varchar(20) null, + code varchar(20) null, + deleted tinyint(1) default 0 null, + create_time timestamp default CURRENT_TIMESTAMP null comment '创建时间' +); + +create table user +( + id int auto_increment comment 'ID' + primary key, + department_id int default 0 not null, + username varchar(20) null, + gender varchar(20) null, + deleted tinyint(1) default 0 null, + create_time timestamp default CURRENT_TIMESTAMP null comment '创建时间' +); + +create table user_role +( + user_id int not null comment '用户ID', + role_id int not null comment '角色ID', + primary key (user_id, role_id) +); + +-- 初始化样例数据 +INSERT INTO department (id, parent_id, org_id, name) +VALUES (10001, 0, 100001, '产品部'), (10002, 10001, 100001, '研发组'), (10003, 10001, 100001, '测试组'); + +INSERT INTO metadata (id, parent_id, type, item_name, item_value, comment, extdata, sort_id, `system`, editable) +VALUES (1, 0, 'GENDER', '性别', null, '', null, 99, 1, 1), (2, 1, 'GENDER', '男', 'M', null, null, 99, 1, 0), (3, 1, 'GENDER', '女', 'F', null, null, 99, 1, 0); + +INSERT INTO organization (id, parent_id, name, telphone) VALUES (100001, 0, '苏州帝博', '0512-62988949'); + +INSERT INTO role (id, name, code) VALUES (101, '管理员', 'ADMIN'), (102, '操作员', 'OPERATOR'); + +INSERT INTO user (id, department_id, username, gender) +VALUES (1001, 10002, '张三', 'M'), (1002, 10002, '李四', 'F'); + +INSERT INTO user_role (user_id, role_id) VALUES (1001, 101),(1001, 102),(1002, 102); \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/DepartmentMapper.xml b/diboot-example/src/main/java/com/diboot/example/mapper/DepartmentMapper.xml deleted file mode 100644 index 6157bb0..0000000 --- a/diboot-example/src/main/java/com/diboot/example/mapper/DepartmentMapper.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/OrganizationMapper.xml b/diboot-example/src/main/java/com/diboot/example/mapper/OrganizationMapper.xml deleted file mode 100644 index 9f6531a..0000000 --- a/diboot-example/src/main/java/com/diboot/example/mapper/OrganizationMapper.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/vo/UserVO.java b/diboot-example/src/main/java/com/diboot/example/vo/UserVO.java index 48bc41d..65677a5 100644 --- a/diboot-example/src/main/java/com/diboot/example/vo/UserVO.java +++ b/diboot-example/src/main/java/com/diboot/example/vo/UserVO.java @@ -26,7 +26,7 @@ public class UserVO extends User { private String genderLabel; // 字段关联,相同条件的entity+condition将合并为一条SQL查询 - @BindField(entity=Department.class, field="name", condition="this.department_id=id AND code IS NOT NULL") + @BindField(entity=Department.class, field="name", condition="this.department_id=id AND parent_id IS NOT NULL") private String deptName; @BindField(entity=Department.class, field="code", condition="this.department_id=id") private String deptCode; @@ -42,7 +42,7 @@ public class UserVO extends User { private Organization organization; // 支持通过中间表的多-多Entity实体关联 - @BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id") - private List roleList; + //@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id") + //private List roleList; } \ No newline at end of file diff --git a/diboot-example/src/main/resources/META-INF/sql/init-mysql.sql b/diboot-example/src/main/resources/META-INF/sql/init-mysql.sql index 90c8cd0..9ef6180 100644 --- a/diboot-example/src/main/resources/META-INF/sql/init-mysql.sql +++ b/diboot-example/src/main/resources/META-INF/sql/init-mysql.sql @@ -7,7 +7,6 @@ create table department parent_id bigint default 0 not null comment '上级部门ID', org_id bigint not null comment '单位ID', name varchar(50) not null comment '名称', - code varchar(20) null comment '编码', extdata varchar(100) null comment '扩展字段', deleted tinyint(1) default 0 not null comment '已删除', create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间' @@ -38,7 +37,6 @@ create table organization parent_id int default 0 not null comment '上级单位ID', name varchar(100) not null comment '名称', telphone varchar(20) null comment '电话', - address varchar(255) null comment '地址', deleted tinyint(1) default 0 not null comment '是否有效', create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间' ) @@ -73,13 +71,13 @@ create table user_role ); -- 初始化样例数据 -INSERT INTO department (id, parent_id, org_id, name, code) -VALUES (10001, 0, 100001, '产品部', 'PROD'), (10002, 10001, 100001, '研发组', 'DEV'), (10003, 10001, 100001, '测试组', 'TST'); +INSERT INTO department (id, parent_id, org_id, name) +VALUES (10001, 0, 100001, '产品部'), (10002, 10001, 100001, '研发组'), (10003, 10001, 100001, '测试组'); INSERT INTO metadata (id, parent_id, type, item_name, item_value, comment, extdata, sort_id, `system`, editable) VALUES (1, 0, 'GENDER', '性别', null, '', null, 99, 1, 1), (2, 1, 'GENDER', '男', 'M', null, null, 99, 1, 0), (3, 1, 'GENDER', '女', 'F', null, null, 99, 1, 0); -INSERT INTO organization (id, parent_id, name, telphone, address) VALUES (100001, 0, '苏州帝博', '0512-62988949', '江苏苏州'); +INSERT INTO organization (id, parent_id, name, telphone) VALUES (100001, 0, '苏州帝博', '0512-62988949'); INSERT INTO role (id, name, code) VALUES (101, '管理员', 'ADMIN'), (102, '操作员', 'OPERATOR'); diff --git a/diboot-example/src/test/java/MainTest.java b/diboot-example/src/test/java/MainTest.java deleted file mode 100644 index 5075edc..0000000 --- a/diboot-example/src/test/java/MainTest.java +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @author : wee - * @Description: todo - * @Date 2019-05-17 19:15 - */ -public class MainTest { - public static void main(String[] args) { - } -} diff --git a/diboot-example/src/test/java/com/diboot/example/ApplicationTest.java b/diboot-example/src/test/java/com/diboot/example/ApplicationTest.java deleted file mode 100644 index 63a28fa..0000000 --- a/diboot-example/src/test/java/com/diboot/example/ApplicationTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.diboot.example; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest -public class ApplicationTest { - - @Test - public void contextLoads(){ - - } -} diff --git a/diboot-example/src/test/java/com/diboot/example/service/DepartmentServiceTest.java b/diboot-example/src/test/java/com/diboot/example/service/DepartmentServiceTest.java deleted file mode 100644 index 3642572..0000000 --- a/diboot-example/src/test/java/com/diboot/example/service/DepartmentServiceTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.diboot.example.service; - -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.diboot.core.util.BeanUtils; -import com.diboot.core.util.V; -import com.diboot.example.ApplicationTest; -import com.diboot.example.entity.Department; -import org.junit.Assert; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.List; - -import static org.junit.Assert.*; - -@Component -public class DepartmentServiceTest extends ApplicationTest { - - @Autowired - private DepartmentService departmentService; - - @Test - public void getEntityListTest() throws Exception{ - QueryWrapper query = new QueryWrapper<>(); - query.eq(BeanUtils.convertToFieldName(Department::getName), "研发部"); - List departmentList = departmentService.getEntityList(query); - Assert.assertTrue(V.notEmpty(departmentList)); - } - -} \ No newline at end of file From e22bb9f723b3a7307fc3d67e49450e359a0385dd Mon Sep 17 00:00:00 2001 From: mazhicheng Date: Sat, 22 Jun 2019 16:01:36 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E4=BC=98=E5=8C=96README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 57 +++++++++++----------------------------- diboot-core/README.md | 46 ++++++++++++++++++++++++++++++++ diboot-example/README.md | 2 ++ 3 files changed, 63 insertions(+), 42 deletions(-) create mode 100644 diboot-core/README.md create mode 100644 diboot-example/README.md diff --git a/README.md b/README.md index f962a5e..04ba3d1 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,30 @@ # diboot-v2 diboot 2.0版本项目,实现: diboot-core全新内核 + diboot-devtools代码生成。 -### diboot-core: 优化内核 +### diboot-core: 精简优化内核 全新精简内核,主要实现单表CRUD和多表关联绑定的无SQL实现方案,并提供其他常用开发场景的简单封装。 #### 单表CRUD无SQL > 基于Mybatis-Plus实现(Mybatis-Plus具备通用Mapper方案和灵活的查询构造器) #### 多表关联查询无SQL(适用于大多数场景,拆分成单表查询自动实现结果绑定) - > 通过注解实现多数场景下的关联查询无SQL -##### 1. 注解自动绑定元数据(枚举值)的显示值Label -~~~java -@BindMetadata(type="GENDER", field = "gender") -private String genderLabel; -~~~ -##### 2. 注解自动绑定其他表的字段 -~~~java -// 支持关联条件+附加条件绑定字段 -@BindField(entity=Department.class, field="name", condition="department_id=id AND code IS NOT NULL") -private String deptName; + > 通过注解实现多数场景下的关联查询无SQL化自动绑定 + +##### 1. @BindMetadata 注解自动绑定元数据(枚举值)的显示值Label +##### 2. @BindField 注解自动绑定其他表的字段 +##### 3. @BindEntity 注解自动绑定单个其他表实体Entity +##### 4. @BindEntityList 注解自动绑定其他表实体集合List -// 支持通过中间表的级联关联绑定字段 -@BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id") -private String orgName; -~~~ -##### 3. 注解自动绑定其他表实体Entity -~~~java -// 支持关联条件+附加条件绑定Entity -@BindEntity(entity = Department.class, condition="department_id=id") -private Department department; +具体请查看: [diboot-core 注解自动绑定多表关联](https://github.com/dibo-software/diboot-v2/tree/master/diboot-core "注解自动绑定多表关联"). -// 通过中间表的级联关联绑定Entity(支持附加条件) -@BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id AND department.deleted=0") -private Organization organization; -~~~ -##### 4. 注解自动绑定其他表实体集合List -~~~java -// 支持关联条件+附加条件绑定多个Entity -@BindEntityList(entity = Department.class, condition = "id=parent_id") -private List children; -// 通过中间表的 多对多关联 绑定Entity(支持附加条件) -@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id") -private List roleList; -~~~ - - > 本地运行example需先执行/resources/init-mysql.sql到数据库。 - -### diboot-devtools 代码生成工具 -提供数据库表管理功能 - -提供CRUD及关联绑定的相关代码生成(Entity,VO,Service,Mapper,Controller...) +### diboot-shiro 基于RBAC+Shiro的权限认证模块 +RBAC的角色权限+基于Shiro的细粒度权限控制 ### diboot-example: 示例 各组件使用示例项目 - + > 运行example需先执行/resources/init-mysql.sql到数据库。 + +### diboot-devtools 代码生成工具 + > 比 1.x 版本更强大的代码生成工具 ... + ... \ No newline at end of file diff --git a/diboot-core/README.md b/diboot-core/README.md new file mode 100644 index 0000000..56dcc99 --- /dev/null +++ b/diboot-core/README.md @@ -0,0 +1,46 @@ +## diboot-core: 全新优化内核 +全新精简内核,主要实现: +1. 多表关联的自动绑定, 实现单表CRUD和多表关联的无SQL化 +2. 提供其他常用开发场景的简单封装。 + +#### 单表CRUD无SQL + > 基于Mybatis-Plus实现(Mybatis-Plus具备通用Mapper方案和灵活的查询构造器) +#### 多表关联查询无SQL(适用于大多数场景,拆分成单表查询自动实现结果绑定) + > 通过注解实现多数场景下的关联查询无SQL +##### 1. 注解自动绑定元数据(枚举值)的显示值Label +~~~java +@BindMetadata(type="GENDER", field = "gender") +private String genderLabel; +~~~ +##### 2. 注解自动绑定其他表的字段 +~~~java +// 支持关联条件+附加条件绑定字段 +@BindField(entity=Department.class, field="name", condition="department_id=id AND parent_id>=0") +private String deptName; + +// 支持通过中间表的级联关联绑定字段 +@BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id") +private String orgName; +~~~ +##### 3. 注解自动绑定其他表实体Entity +~~~java +// 支持关联条件+附加条件绑定Entity +@BindEntity(entity = Department.class, condition="department_id=id") +private Department department; + +// 通过中间表的级联关联绑定Entity(支持附加条件) +@BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id AND department.deleted=0") +private Organization organization; +~~~ +##### 4. 注解自动绑定其他表实体集合List +~~~java +// 支持关联条件+附加条件绑定多个Entity +@BindEntityList(entity = Department.class, condition = "id=parent_id") +private List children; + +// 通过中间表的 多对多关联 绑定Entity(支持附加条件) +@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id") +private List roleList; +~~~ + + > 本地运行test单元测试需先执行/test/resources/init-{db}.sql到你的数据库(暂提供Mysql脚本)。 diff --git a/diboot-example/README.md b/diboot-example/README.md new file mode 100644 index 0000000..39506da --- /dev/null +++ b/diboot-example/README.md @@ -0,0 +1,2 @@ +## diboot-example: 各组件/模块的使用样例 +本地运行test单元测试需先执行/test/resources/init-{db}.sql到你的数据库(暂提供Mysql脚本)。