完成 服务启动之后权限自动入库

This commit is contained in:
wuy 2019-06-23 01:05:28 +08:00
parent 1d6ce88f35
commit ade3e1fe5f
23 changed files with 754 additions and 331 deletions

View File

@ -0,0 +1,40 @@
package com.diboot.example.controller;
import com.diboot.core.vo.JsonResult;
import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 权限测试
* @author : wee
* @version : v 2.0
* @Date 2019-06-19 13:37
*/
@RestController
@RequestMapping("/authorization")
@AuthorizationPrefix(name = "测试权限", code = "authorization", prefix = "authorization")
public class AuthorizationWrapperController {
/**此处权限为authorization:get 或 authorization:test*/
@GetMapping("/get")
@AuthorizationWrapper(value = @RequiresPermissions(value = {"get", "test"},
logical = Logical.OR),
name = {"查看", "测试"})
public JsonResult get() {
return new JsonResult("ok");
}
/**此处权限为getAll 或 test*/
@GetMapping("/getAll")
@AuthorizationWrapper(value = @RequiresPermissions({"getAll", "test"}),
name = {"获取所有", "测试"},
ignorePrefix = true)
public JsonResult getAll() {
return new JsonResult("ok");
}
}

View File

@ -0,0 +1,36 @@
package com.diboot.example.listener;
import com.diboot.shiro.authz.storage.EnableStorageEnum;
import com.diboot.shiro.listener.AbstractStorageApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
/**
* 示例监听器
* {@link AbstractStorageApplicationListener}丰富了 {@link org.springframework.context.ApplicationListener}
* {@link AbstractStorageApplicationListener}中封装了将{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper}权限自动入库的操作
* 当你使用注解{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper}建议直接继承{@link AbstractStorageApplicationListener}
* 然后对{@link AbstractStorageApplicationListener#customExecute(ContextRefreshedEvent)}方法进行重写
* 需要手动设置一个默认构造函数传递是否自动权限入库
* @author : wee
* @version : v 2.0
* @Date 2019-06-18 23:11
*/
@Component
public class ExampleListener extends AbstractStorageApplicationListener {
/**需要手动实现构造来设置是否开启权限入库操作,默认入库*/
protected ExampleListener() {
super(EnableStorageEnum.TRUE);
}
/**
* 系统启动后客户自定义事件
*
* @param event
*/
@Override
protected void customExecute(ContextRefreshedEvent event) {
System.out.println("============00000000");
}
}

View File

@ -0,0 +1,45 @@
package com.diboot.shiro.authz.annotation;
import java.lang.annotation.*;
/**
* 权限注解的前缀用于controller注解<br/>
* <strong>当你使用{@link AuthorizationWrapper}请在类上使用{@link AuthorizationPrefix}注解进行标记系统启动的时候才会将权限入库</strong>
* @author : wee
* @version v 2.0
* @Date 2019-06-17 20:42
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AuthorizationPrefix {
/**
* 名称
*
* 设置当前权限前缀名称
* @return
*/
String name();
/**
* 编码
*
* 设置当前权限前缀编码
* @return
*/
String code();
/**
* <h3>{@link AuthorizationWrapper#requiresPermissions()#value()}的前缀</h3>
* <ul>
* <li> value = permissions</li>
* <li>{@link AuthorizationWrapper#requiresPermissions()#value()} = {"list", "get"}</li>
* <li>实际权限为{"permissions:list", "permissions:list"}</li>
* </ul>
* 当前注解优先级低于{@link AuthorizationWrapper#prefix()},
* 如果两者都配置优先使用{@link AuthorizationWrapper#prefix()}
* @return
*/
String prefix() default "";
}

View File

@ -0,0 +1,28 @@
package com.diboot.shiro.authz.annotation;
import org.apache.shiro.authz.annotation.*;
import java.lang.annotation.*;
/**
* 权限包装目前只包装{@link RequiresPermissions},可以根据需要包装{@link RequiresRoles}
* @author : wee
* @version v 1.0
* @Date 2019-06-19 00:40
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface AuthorizationWrapper {
/**RequiresPermissions包装*/
RequiresPermissions value();
/**权限别名:用于存储到数据库,与{@link RequiresPermissions#value()}的值一一对应*/
String[] name();
/**设置前缀:用于拼接,详细描述参考{@link AuthorizationPrefix#prefix()}*/
String prefix() default "";
/**是否忽略前缀:默认不忽略*/
boolean ignorePrefix() default false;
}

View File

@ -0,0 +1,50 @@
package com.diboot.shiro.authz.aop;
import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import com.diboot.shiro.authz.handler.AuthorizationWrapperAnnotationHandler;
import org.apache.shiro.aop.AnnotationResolver;
import org.apache.shiro.aop.MethodInvocation;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
/**
* {@link AuthorizationWrapper} 拦截器
* @author : wee
* @version : v2.0
* @Date 2019-06-14 22:19
*/
public class AuthorizationWrapperAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {
/**
* Default no-argument constructor that ensures this interceptor looks for
* {@link AuthorizationWrapper AuthorizationWrapper} annotations in a method declaration.
*/
public AuthorizationWrapperAnnotationMethodInterceptor() {
super( new AuthorizationWrapperAnnotationHandler() );
}
/**
* @param resolver
* @since 1.1
*/
public AuthorizationWrapperAnnotationMethodInterceptor(AnnotationResolver resolver) {
super( new AuthorizationWrapperAnnotationHandler(), resolver);
}
/**
* 当使用AuthorizationWrapper注解进行权限验证的时候自动的去追加前缀
* @param mi
* @throws AuthorizationException
*/
@Override
public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
try {
((AuthorizationWrapperAnnotationHandler)getHandler()).assertAuthorized(getResolver(), mi);
}
catch(AuthorizationException ae) {
if (ae.getCause() == null) {
ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod()));
}
throw ae;
}
}
}

View File

@ -1,6 +1,5 @@
package com.diboot.shiro.bind.aop;
package com.diboot.shiro.authz.aop;
import com.diboot.shiro.bind.aop.PermissionWrapperAnnotationMethodInterceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.shiro.aop.AnnotationResolver;
@ -12,6 +11,7 @@ import java.util.ArrayList;
import java.util.List;
/**
* 自定义AOP拦截
* @author : wee
* @version : v2.0
* @Date 2019-06-15 12:07
@ -21,7 +21,7 @@ public class CustomAopAllianceAnnotationsAuthorizingMethodInterceptor extends An
List<AuthorizingAnnotationMethodInterceptor> interceptors =
new ArrayList<AuthorizingAnnotationMethodInterceptor>(6);
AnnotationResolver resolver = new SpringAnnotationResolver();
interceptors.add(new PermissionWrapperAnnotationMethodInterceptor(resolver));
interceptors.add(new AuthorizationWrapperAnnotationMethodInterceptor(resolver));
interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));

View File

@ -1,6 +1,6 @@
package com.diboot.shiro.bind.aop;
package com.diboot.shiro.authz.aop;
import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import org.apache.shiro.authz.annotation.*;
import org.apache.shiro.mgt.SecurityManager;
import org.slf4j.Logger;
@ -12,6 +12,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* 自定义切片装配器
* @author : wee
* @version : v1.0
* @Date 2019-06-15 12:27
@ -22,7 +23,7 @@ public class CustomAuthorizationAttributeSourceAdvisor extends StaticMethodMatch
private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
new Class[] {
RequiresPermissionsWrapper.class,
AuthorizationWrapper.class,
RequiresPermissions.class, RequiresRoles.class,
RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
};
@ -48,7 +49,7 @@ public class CustomAuthorizationAttributeSourceAdvisor extends StaticMethodMatch
* Returns <tt>true</tt> if the method or the class has any Shiro annotations, false otherwise.
* The annotations inspected are:
* <ul>
* <li>{@link RequiresPermissionsWrapper RequiresPermissionsWrapper}</li>
* <li>{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper AuthorizationWrapper}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresAuthentication RequiresAuthentication}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser}</li>
* <li>{@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest}</li>

View File

@ -0,0 +1,112 @@
package com.diboot.shiro.authz.handler;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import org.apache.shiro.aop.AnnotationResolver;
import org.apache.shiro.aop.MethodInvocation;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
import org.apache.shiro.authz.aop.PermissionAnnotationHandler;
import org.apache.shiro.subject.Subject;
import java.lang.annotation.Annotation;
/**
* {@link AuthorizationWrapper} 助手类 参考{@link PermissionAnnotationHandler}实现
* @author : wee
* @version : v2.0
* @Date 2019-06-14 22:19
*/
public class AuthorizationWrapperAnnotationHandler extends AuthorizingAnnotationHandler {
/**
* 标记服务的注解
*/
public AuthorizationWrapperAnnotationHandler() {
super(AuthorizationWrapper.class);
}
/**
* 返回{@link AuthorizationWrapper#value()#value} value}
* @param a ${@link AuthorizationWrapper}
* @return
*/
protected String[] getAnnotationValue(Annotation a) {
AuthorizationWrapper rpAnnotation = (AuthorizationWrapper) a;
return rpAnnotation.value().value();
}
/**
* 校验注解{@link AuthorizationWrapper},自定义包装类
* 使用${@link AuthorizationWrapperAnnotationHandler#assertAuthorized(AnnotationResolver, MethodInvocation)}进行权限验证
*/
@Override
public void assertAuthorized(Annotation a) throws AuthorizationException {}
/**
* 校验注解{@link AuthorizationWrapper}
*/
public void assertAuthorized(AnnotationResolver resolver, MethodInvocation mi) throws AuthorizationException {
//如果方法上存在AuthorizationWrapper注解那么resolver.getAnnotation()获取的是AuthorizationWrapper注解会优先从缓存读取
AuthorizationWrapper authorizationWrapper = (AuthorizationWrapper)resolver.getAnnotation(mi, AuthorizationWrapper.class);
String[] perms = getAnnotationValue(authorizationWrapper);
Subject subject = getSubject();
//如果忽略前缀那么则不拼接前缀
perms = addPrefix(resolver, mi, authorizationWrapper, perms);
if (perms.length == 1) {
subject.checkPermission(perms[0]);
return;
}
if (Logical.AND.equals(authorizationWrapper.value().logical())) {
getSubject().checkPermissions(perms);
return;
}
if (Logical.OR.equals(authorizationWrapper.value().logical())) {
boolean hasAtLeastOnePermission = false;
for (String permission : perms) {
if (getSubject().isPermitted(permission)) {
hasAtLeastOnePermission = true;
}
}
if (!hasAtLeastOnePermission) {
getSubject().checkPermission(perms[0]);
}
}
}
/**
* perms 追加前缀
* @param resolver
* @param mi
* @param authorizationWrapper
* @param perms
* @return
*/
private String[] addPrefix(AnnotationResolver resolver, MethodInvocation mi, AuthorizationWrapper authorizationWrapper, String[] perms) {
if (!authorizationWrapper.ignorePrefix()) {
String prefix = "";
if (V.notEmpty(authorizationWrapper.prefix())) {
prefix = authorizationWrapper.prefix();
} else {
//如果自身不定义查找前缀注解存在则设置值
AuthorizationPrefix authorizationPrefix = (AuthorizationPrefix)resolver.getAnnotation(mi, AuthorizationPrefix.class);
if (V.notEmpty(authorizationPrefix)) {
prefix = authorizationPrefix.prefix();
}
}
String [] permsTemp = new String[perms.length];
//前缀存在的时候才做组装其他情况不处理
if (V.notEmpty(prefix)) {
for (int i = 0; i < perms.length; i++) {
permsTemp[i] = S.join(prefix, ":", perms[i]);
}
perms = permsTemp;
}
}
return perms;
}
}

View File

@ -0,0 +1,19 @@
package com.diboot.shiro.authz.storage;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 是否开启存储枚举
* @author : wee
* @version : v 1.0
* @Date 2019-06-18 23:06
*/
@Getter
@AllArgsConstructor
public enum EnableStorageEnum {
TRUE(true),
FALSE(false);
private boolean storagePermissions;
}

View File

@ -0,0 +1,52 @@
package com.diboot.shiro.authz.storage;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.diboot.core.entity.BaseEntity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author Yangzhao
* @version v2.0
* @date 2019/6/6
*/
@Data
@Builder
public class PermissionStorage implements Serializable {
private static final long serialVersionUID = 147840093814049689L;
/***
* 默认逻辑删除标记deleted=0有效
*/
private boolean deleted = false;
/**菜单Id*/
private Long menuId;
/**菜单编码*/
private String menuCode;
/**菜单名称*/
private String menuName;
/**权限编码*/
private String permissionCode;
/**权限名称*/
private String permissionName;
/**是否高优先级:方法上的优先级高于类上,同时出现以方法为准*/
private boolean highPriority;
private Long id;
}

View File

@ -1,45 +0,0 @@
package com.diboot.shiro.bind.annotation;
import java.lang.annotation.*;
/**
* 权限注解的前缀用于controller注解
*
* @author : wee
* @version v 2.0
* @Date 2019-06-17 20:42
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PermissionsPrefix {
/**
* 名称
*
* 设置当前权限前缀名称
* @return
*/
String name();
/**
* 编码
*
* 设置当前权限前缀编码
* @return
*/
String code();
/**
* <h3>{@link RequiresPermissionsWrapper#value()}的前缀</h3>
* <ul>
* <li> value = permissions</li>
* <li>{@link RequiresPermissionsWrapper#value()} = {"list", "get"}</li>
* <li>实际权限为{"permissions:list", "permissions:list"}</li>
* </ul>
* 当前注解优先级低于{@link RequiresPermissionsWrapper#prefix()},
* 如果两者都配置优先使用{@link RequiresPermissionsWrapper#prefix()}
* @return
*/
String prefix() default "";
}

View File

@ -1,44 +0,0 @@
package com.diboot.shiro.bind.annotation;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import java.lang.annotation.*;
/**
* 注解{@link RequiresPermissions}的包装增加权限描述等字段
* @author : wee
* @version v2.0
* @Date 2019-06-14 17:50
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface RequiresPermissionsWrapper {
/**
* 包装 {@link RequiresPermissions#value()}
*/
String[] value();
/**
* 包装 {@link RequiresPermissions#logical()}
*/
Logical logical() default Logical.AND;
/**
* 权限名称
* @return
*/
String name();
/**
* 参照{@link PermissionsPrefix#prefix()}解释
* @return
*/
String prefix() default "";
}

View File

@ -1,54 +0,0 @@
package com.diboot.shiro.bind.aop;
import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
import com.diboot.shiro.bind.handler.PermissionWrapperAnnotationHandler;
import org.apache.shiro.aop.AnnotationResolver;
import org.apache.shiro.aop.MethodInvocation;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
/**
* {@link RequiresPermissionsWrapper} 拦截器
* @author : wee
* @version : v2.0
* @Date 2019-06-14 22:19
*/
public class PermissionWrapperAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {
/**
* Default no-argument constructor that ensures this interceptor looks for
* {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotations in a method declaration.
*/
public PermissionWrapperAnnotationMethodInterceptor() {
super( new PermissionWrapperAnnotationHandler() );
}
/**
* @param resolver
* @since 1.1
*/
public PermissionWrapperAnnotationMethodInterceptor(AnnotationResolver resolver) {
super( new PermissionWrapperAnnotationHandler(), resolver);
}
/**
* 当使用RequiresPermissionsWrapper注解进行权限验证的时候自动的去追加前缀
* @param mi
* @throws AuthorizationException
*/
@Override
public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
try {
//默认是直接调用方法上注解现在修改成 获取类和方法上的注解
// ((AuthorizingAnnotationHandler)getHandler()).assertAuthorized(getAnnotation(mi));
((PermissionWrapperAnnotationHandler)getHandler()).assertAuthorized(getResolver(), mi);
}
catch(AuthorizationException ae) {
if (ae.getCause() == null) {
ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod()));
}
throw ae;
}
}
}

View File

@ -1,164 +0,0 @@
package com.diboot.shiro.bind.handler;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import com.diboot.shiro.bind.annotation.PermissionsPrefix;
import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
import org.apache.shiro.aop.AnnotationResolver;
import org.apache.shiro.aop.MethodInvocation;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
import org.apache.shiro.authz.aop.PermissionAnnotationHandler;
import org.apache.shiro.subject.Subject;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Map;
/**
* {@link RequiresPermissionsWrapper} 助手类 参考{@link PermissionAnnotationHandler}实现
* @author : wee
* @version : v2.0
* @Date 2019-06-14 22:19
*/
public class PermissionWrapperAnnotationHandler extends AuthorizingAnnotationHandler {
private final static String REQUIRES_PERMISSIONS_VALUE = "value";
private final static String REQUIRES_PERMISSIONS_LOGICAL = "logical";
private final static String JDK_MEMBER_VALUES = "memberValues";
/**
* 标记服务的注解
*/
public PermissionWrapperAnnotationHandler() {
super(RequiresPermissionsWrapper.class);
}
/**
* Returns the annotation {@link RequiresPermissions#value value}, from which the Permission will be constructed.
*
* @param a the RequiresPermissions annotation being inspected.
* @return the annotation's <code>value</code>, 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<String, Object> memberValues = (Map<String, Object>) 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();
}
}
}

View File

@ -1,6 +1,6 @@
package com.diboot.shiro.config;
import com.diboot.shiro.bind.aop.CustomAuthorizationAttributeSourceAdvisor;
import com.diboot.shiro.authz.aop.CustomAuthorizationAttributeSourceAdvisor;
import com.diboot.shiro.jwt.BaseJwtAuthenticationFilter;
import com.diboot.shiro.jwt.BaseJwtRealm;
import org.apache.shiro.mgt.SecurityManager;

View File

@ -7,8 +7,8 @@ import com.diboot.core.util.BeanUtils;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.Pagination;
import com.diboot.core.vo.Status;
import com.diboot.shiro.bind.annotation.PermissionsPrefix;
import com.diboot.shiro.bind.annotation.RequiresPermissionsWrapper;
import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import com.diboot.shiro.entity.Permission;
import com.diboot.shiro.service.PermissionService;
import com.diboot.shiro.vo.PermissionVO;
@ -30,7 +30,7 @@ import java.util.List;
* Copyright © www.dibo.ltd
*/
@RestController
@PermissionsPrefix(prefix = "permission", code = "permission", name = "权限")
@AuthorizationPrefix(prefix = "permission", code = "permission", name = "权限")
@RequestMapping("/permission")
public class PermissionController extends BaseCrudRestController {
@ -46,7 +46,7 @@ public class PermissionController extends BaseCrudRestController {
* @throws Exception
*/
@GetMapping("/{id}")
@RequiresPermissionsWrapper(prefix = "permissionSelf", value = {"get"}, name = "查看")
@AuthorizationWrapper(value = @RequiresPermissions("get"), name = "查看")
public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request, ModelMap modelMap)
throws Exception{
PermissionVO vo = permissionService.getViewObject(id, PermissionVO.class);
@ -62,7 +62,7 @@ public class PermissionController extends BaseCrudRestController {
* @throws Exception
*/
@GetMapping("/list")
@RequiresPermissionsWrapper(value = {"list"}, name = "列表")
@AuthorizationWrapper(value = @RequiresPermissions("list"), name = "列表")
public JsonResult getVOList(HttpServletRequest request) throws Exception{
QueryWrapper<Permission> queryWrapper = buildQuery(request);
// 构建分页

View File

@ -1,6 +1,8 @@
package com.diboot.shiro.entity;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.diboot.core.entity.BaseEntity;
import lombok.Data;
@ -14,6 +16,9 @@ public class Permission extends BaseEntity {
private static final long serialVersionUID = 7713768302925692987L;
@TableField
private Long menuId;
@TableField
private String menuCode;
@ -26,4 +31,8 @@ public class Permission extends BaseEntity {
@TableField
private String permissionName;
/**此处覆盖了父类的属性,初始化权限的时候需要设置该值,父类默认不设置*/
// @TableField
// private boolean deleted = false;
}

View File

@ -21,30 +21,30 @@ public class BaseJwtAuthenticationToken implements AuthenticationToken {
private static final Logger logger = LoggerFactory.getLogger(BaseJwtAuthenticationToken.class);
// 登录用的账号此处的这个账号是一种抽象的概念
/**登录用的账号(此处的这个账号是一种抽象的概念*/
private String account;
// 登录用的密码 此处的这个密码也是一种抽象的概念
/**登录用的密码 (此处的这个密码也是一种抽象的概念*/
private String password;
// 登录使用方式
/**登录使用方式*/
private AuthType authType;
// auth token
/**authz token*/
private String authtoken;
// 申请token的密码
/**申请token的密码*/
private String applyTokenSecret;
// 签名key (默认SIGN_KEY配置signKey, 或微信state, 密码等)
/**签名key (默认SIGN_KEY配置signKey, 或微信state, 密码等)*/
private String signKey = JwtHelper.SIGN_KEY;
// 过期时间
/**过期时间*/
private long expiresInMinutes = JwtHelper.EXPIRES_IN_MINUTES;
private Map<String, AuthWayService> authWayServiceMap;
// 默认构造函数
/**默认构造函数*/
public BaseJwtAuthenticationToken(){
}

View File

@ -31,11 +31,12 @@ public class BaseJwtRealm extends AuthorizingRealm {
@Autowired
private RoleService roleService;
@Override
public boolean supports(AuthenticationToken token) {
return token != null && token instanceof BaseJwtAuthenticationToken;
}
@Override
public Class<?> getAuthenticationTokenClass() {
return BaseJwtRealm.class;
}

View File

@ -0,0 +1,255 @@
package com.diboot.shiro.listener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import com.diboot.shiro.authz.annotation.AuthorizationPrefix;
import com.diboot.shiro.authz.annotation.AuthorizationWrapper;
import com.diboot.shiro.authz.storage.EnableStorageEnum;
import com.diboot.shiro.authz.storage.PermissionStorage;
import com.diboot.shiro.entity.Permission;
import com.diboot.shiro.service.impl.PermissionServiceImpl;
import com.diboot.shiro.util.ProxyToTargetObjectHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* {@link AbstractStorageApplicationListener}实现{@link ApplicationListener}接口,
* <br/>
* 并重新对外提供抽象{@link AbstractStorageApplicationListener#customExecute}功能等同于{@link ApplicationListener#onApplicationEvent};
* <br/>
* {@link AbstractStorageApplicationListener}中封装了将{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper}权限自动入库的操作,
* <strong>权限入库每个Controller需要加上类注解{@link AuthorizationPrefix}用于识别</strong>
* <br/>
* 当你使用注解{@link com.diboot.shiro.authz.annotation.AuthorizationWrapper}建议直接继承{@link AbstractStorageApplicationListener},
* <br/>
* 且你的实现类需要手动设置一个默认构造函数来设置{@link AbstractStorageApplicationListener#storagePermissions}传递是否自动权限入库
*
* @author : wee
* @version : v 2.0
* @Date 2019-06-18 23:12
*/
@Slf4j
public abstract class AbstractStorageApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
/**存储数据库中已经存在的permissionCode和ID的关系主要用于更新数据*/
private static Map<String, Permission> dbPermissionMap = new HashMap<>();
/**代码中的所有权限数据 key: {@link PermissionStorage#getPermissionCode()} value: {@link Permission}*/
private static Map<String, PermissionStorage> loadCodePermissionMap = new ConcurrentHashMap<>();
private static List<Permission> updateOrCreateOrDeletePermissionList = new ArrayList<>();
/**
* 默认开启存储
*/
protected boolean storagePermissions;
protected AbstractStorageApplicationListener(EnableStorageEnum enableStorageEnum) {
this.storagePermissions = enableStorageEnum.isStoragePermissions();
}
/**
* Handle an application event.
*
* @param event the event to respond to
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//防止重复执行
if (V.isEmpty(event.getApplicationContext().getParent())) {
/*自定义处理内容*/
customExecute(event);
/*判断客户是否开启存储权限,如果开启那么执行存储权限*/
if (storagePermissions) {
storagePermissions(event);
}
}
}
/**
* 系统启动后客户自定义事件
*
* @param event
*/
protected abstract void customExecute(ContextRefreshedEvent event);
/**
* 执行完自动权限后自动赋值
*
* @param event
*/
private void storagePermissions(ContextRefreshedEvent event) {
try {
ApplicationContext applicationContext = event.getApplicationContext();
if (V.notEmpty(applicationContext)) {
PermissionServiceImpl permissionService = applicationContext.getBean(PermissionServiceImpl.class);
//获取当前数据库中的有效的所有权限
LambdaQueryWrapper<Permission> permissionLambdaQueryWrapper = Wrappers.lambdaQuery();
permissionLambdaQueryWrapper.eq(Permission::isDeleted, false);
List<Permission> permissionList = permissionService.getEntityList(permissionLambdaQueryWrapper);
//存储数据库值
for (Permission permission : permissionList) {
dbPermissionMap.put(permission.getPermissionCode(), permission);
}
//初始化数据获取所有注解为AuthPrefix的代理bean<bean名称bean的代理对象>
Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(AuthorizationPrefix.class);
if (V.isEmpty(beansWithAnnotation)) {
return;
}
for (Map.Entry<String, Object> entry : beansWithAnnotation.entrySet()) {
//获取代理对象的目标对象代理对象无法获取类上注解所以需要转化成目标对象
Object target = ProxyToTargetObjectHelper.getTarget(entry.getValue());
Class<?> targetClass = target.getClass();
//获取类注解上的相关描述
AuthorizationPrefix authorizationPrefix = targetClass.getAnnotation(AuthorizationPrefix.class);
//判断Controller类上是否包含认证注解的包装
if (targetClass.isAnnotationPresent(AuthorizationWrapper.class)) {
buildPermissionStorageToMap(authorizationPrefix, targetClass.getAnnotation(AuthorizationWrapper.class), false);
}
//获取类中所有方法
Method[] methods = target.getClass().getMethods();
for (Method method : methods) {
//取出含有注解的方法
if (method.isAnnotationPresent(AuthorizationWrapper.class)) {
buildPermissionStorageToMap(authorizationPrefix, method.getAnnotation(AuthorizationWrapper.class), true);
}
}
}
//保存更新删除 权限
saveOrUpdateOrDeletePermission(permissionService);
}
} catch (Exception e) {
log.error("【初始化权限】<== 异常:", e);
}
}
/**
* 构建待存储的权限到map中
* @param authorizationPrefix {@link AuthorizationPrefix}
* @param authorizationWrapper {@link AuthorizationWrapper}
*/
private void buildPermissionStorageToMap(AuthorizationPrefix authorizationPrefix, AuthorizationWrapper authorizationWrapper, boolean highPriority) {
String menuCode = authorizationPrefix.code();
String menuName = authorizationPrefix.name();
//如果不忽略前缀 那么进行前缀补充 (如果允许优先使用 AuthorizationWrapper注解的前缀)
String prefix = "";
if (!authorizationWrapper.ignorePrefix()) {
prefix = V.notEmpty(authorizationWrapper.prefix()) ? authorizationWrapper.prefix() : authorizationPrefix.prefix();
}
//获取权限
RequiresPermissions requiresPermissions = authorizationWrapper.value();
//获取所有描述的权限code和name
String[] value = requiresPermissions.value();
String[] name = authorizationWrapper.name();
//设置单个权限code和name
String permissionName = "";
String permissionCode = "";
PermissionStorage permissionStorage;
for (int i = 0; i < value.length; i++) {
//如果权限名称和值无法一一对应那么当前权限组的所有数据的名称全部设置为最后一个name值
permissionName = value.length != name.length ? name[name.length - 1] : name[i];
//拼接权限值
permissionCode = V.notEmpty(prefix) ? S.join(prefix, ":", value[i]) : value[i];
PermissionStorage existPermission = loadCodePermissionMap.get(permissionCode);
//如果不存在权限构建当前存在的权限不是是高优先级的时候替换
if (V.isEmpty(existPermission) || !existPermission.isHighPriority()) {
//组装需要存储的权限
permissionStorage = PermissionStorage.builder()
.menuId(3L).menuCode(menuCode).menuName(menuName)
.permissionCode(permissionCode).permissionName(permissionName)
.deleted(false).highPriority(highPriority).build();
//设置缓存
loadCodePermissionMap.put(permissionCode, permissionStorage);
}
}
}
/**
* <h3>保存更新删除 权限</h3>
* <p>
* 操作原则 以数据库字段为基准 匹配数据库中的权限和代码中的权限<br>
* * 如果代码中和数据库中相同更新数据库中数据<br>
* * 如果代码中不存在删除数据库中数据<br>
* </p>
* @param permissionService
*/
private void saveOrUpdateOrDeletePermission(PermissionServiceImpl permissionService) {
//记录修改和删除的权限数量
int modifyCount = 0, removeCount = 0, totalCount = 0;
List<Permission> saveOrUpdateOrDeletePermissionList = new ArrayList<>();
//设置删除 or 修改
for (Map.Entry<String, Permission> entry : dbPermissionMap.entrySet()) {
PermissionStorage permissionStorage = loadCodePermissionMap.get(entry.getKey());
Permission permission = entry.getValue();
//存在则更新设置ID
if (V.notEmpty(permissionStorage)) {
if (isNeedModify(permission, permissionStorage)) {
modifyCount++;
permissionStorage.setId(permission.getId());
//重置
loadCodePermissionMap.put(entry.getKey(), permissionStorage);
} else {
//数据库中不需要修改的在加载的数据中删除
loadCodePermissionMap.remove(entry.getKey());
}
} else {
//不存在: 表示需要删除
removeCount++;
permission.setDeleted(true);
saveOrUpdateOrDeletePermissionList.add(permission);
}
}
//需要操作的数据=转化为List<Permission>
List<Permission> saveOrUpdatePermissionList;
if (V.notEmpty(loadCodePermissionMap)) {
totalCount = loadCodePermissionMap.values().size();
List<PermissionStorage> permissionStorageList = loadCodePermissionMap.values().stream().collect(Collectors.toList());
saveOrUpdatePermissionList = BeanUtils.convertList(permissionStorageList, Permission.class);
saveOrUpdateOrDeletePermissionList.addAll(saveOrUpdatePermissionList);
}
//保存更新删除 权限
boolean success = permissionService.saveOrUpdateBatch(saveOrUpdateOrDeletePermissionList);
if (success) {
log.debug("【初始化权限】<== 成功!共计权限{}个 新增【{}】个, 修改【{}】个,删除【{}】个!",
totalCount, (saveOrUpdateOrDeletePermissionList.size() - modifyCount - removeCount), modifyCount, removeCount);
} else {
log.debug("【初始化权限】<== 失败!");
}
}
/**
* 比较两个对象的属性是否相同如果存在一处不同那么就需要修改
* @param permission
* @param permissionStorage
* @return
*/
public boolean isNeedModify(Permission permission, PermissionStorage permissionStorage){
if (!V.equals(permission.getMenuId(), permissionStorage.getMenuId())
|| !V.equals(permission.getMenuCode(), permissionStorage.getMenuCode())
|| !V.equals(permission.getMenuName(), permissionStorage.getMenuName())
|| !V.equals(permission.getPermissionCode(), permissionStorage.getPermissionCode())
|| !V.equals(permission.getPermissionName(), permissionStorage.getPermissionName())
) {
return true;
}
return false;
}
}

View File

@ -4,7 +4,6 @@ import com.diboot.core.service.impl.BaseServiceImpl;
import com.diboot.shiro.entity.Permission;
import com.diboot.shiro.mapper.PermissionMapper;
import com.diboot.shiro.service.PermissionService;
import com.diboot.shiro.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

View File

@ -21,7 +21,7 @@ public class JwtHelper {
private static final Logger logger = LoggerFactory.getLogger(JwtHelper.class);
private static final String ISSUER = V.notEmpty(BaseConfig.getProperty("diboot.shiro.jwt.issuer")) ? BaseConfig.getProperty("diboot.shiro.jwt.issuer") : "diboot.com";
private static final String AUTH_HEADER = V.notEmpty(BaseConfig.getProperty("diboot.shiro.jwt.auth.header.key")) ? BaseConfig.getProperty("diboot.shiro.jwt.auth.header.key") : "authtoken";
private static final String AUTH_HEADER = V.notEmpty(BaseConfig.getProperty("diboot.shiro.jwt.authz.header.key")) ? BaseConfig.getProperty("diboot.shiro.jwt.authz.header.key") : "authtoken";
private static final String TOKEN_PREFIX = V.notEmpty(BaseConfig.getProperty("diboot.shiro.jwt.token.prefix")) ? BaseConfig.getProperty("diboot.shiro.jwt.token.prefix") : "Bearer ";
public static final String SIGN_KEY = V.notEmpty(BaseConfig.getProperty("diboot.shiro.jwt.signkey"))? BaseConfig.getProperty("diboot.shiro.jwt.signkey") : "Dibo2016Mazc";

View File

@ -0,0 +1,83 @@
package com.diboot.shiro.util;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
import java.lang.reflect.Field;
/**
* 代理对象转成目标对象
* @author : wee
* @version : v 2.0
* @Date 2019-06-19 14:42
*/
public class ProxyToTargetObjectHelper {
/**
* 获取代理对象的目标对象
* @param proxy
* @return
* @throws Exception
*/
public static Object getTarget(Object proxy) throws Exception {
/*判断是否是代理对象*/
if(!AopUtils.isAopProxy(proxy)) {
return proxy;
}
//判断是jdk动态代理还是cglib代理
if(AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy);
}
//cglib
else {
return getCglibProxyTargetObject(proxy);
}
}
/**
* 获取cglib代理的目标对象
* @param proxy
* @return
* @throws Exception
*/
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
return getJdkOrCglibTargetObject(h, proxy);
}
/**
* 获取jdk动态代理的目标对象
* @param proxy
* @return
* @throws Exception
*/
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
return getJdkOrCglibTargetObject(h, proxy);
}
/**
* 根据不用的代理获取目标对象
* @param field
* @param proxy
* @return
* @throws Exception
*/
private static Object getJdkOrCglibTargetObject(Field field, Object proxy) throws Exception{
field.setAccessible(true);
Object dynamic = field.get(proxy);
Field advised;
//如果是jdk代理
if (dynamic instanceof AopProxy) {
advised = ((AopProxy)dynamic).getClass().getDeclaredField("advised");
}
//如果是cglib代理
advised = dynamic.getClass().getDeclaredField("advised");
advised.setAccessible(true);
//获取目标类
return ((AdvisedSupport)advised.get(dynamic)).getTargetSource().getTarget();
}
}