learn and grow up

自己写基于注解的参数级统一数据权限组件

字数统计: 1.3k阅读时长: 6 min
2020/06/28 Share

写在前面

接上一篇

代码片段

废话不多说,上代码片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

/**
* 随便取一个拦截实现类来展示
* @author yyt
*/
@Component
@Slf4j
public class ProjectDataAuthHandle extends DataAuthAbstractHandle {
@Override
protected Set<DataAuthDomain> getAuthDomain(Set<String> ids, String[] params,String fieldName) {
if(!fieldName.contains("projectId")){
//不是项目
return SetUtils.emptySet();
}
//直接返回原projectId,后续有需要再根据params补充
if(CollectionUtils.isEmpty(ids)){
return SetUtils.emptySet();
}
Set<DataAuthDomain> authDomains=new HashSet<>(ids.size());
for(String id:ids){
DataAuthDomain dataAuthDomain = new DataAuthDomain();
dataAuthDomain.setProjectId(id);
authDomains.add(dataAuthDomain);
}

return authDomains;
}

//覆盖父类方法,只进行projectId的校验
@Override
public Boolean checkByUserProjectSchemas(@NotNull List<Integer> sysRoleCodes,@NotNull String projectId,@NotNull String schemaId,
@NotNull UserProjectScheams userProjectScheams){
return checkByUserProjectSchemas(sysRoleCodes,projectId,userProjectScheams);
}

@Override
public @NotNull List<Integer> getAuthSysRoleCodes() {
return null;
}

@Override
public Logger getLogger() {
return log;
}

/**
* 排第一
* @return
*/
@Override
public int getOrder(){
return 1;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//服务启动时扫描需要拦截的方法和相关配置放入缓存
/**
* 数据权限aop初始化,
* 利用bean后置方法,讲所有需要进行数据权限的拦截放入缓存
* @author yyt
*/
@Component
@Slf4j
public class DataAuthHandlerAdapter implements BeanPostProcessor {

@Data
public static class DataAuthHandleBean{
private int index;
private DataAuthAnnotation dataAuthAnnotation;
private List<Class<? extends DataAuthAbstractHandle>> classes;
}

private final static Map<Method,Map<Integer,DataAuthHandleBean>> dataAuthAdapter=new HashMap();

public static Map<Method,Map<Integer,DataAuthHandleBean>> getDataAuthAdapter(){
return dataAuthAdapter;
}

@Override
@Nullable
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//对controller进行解析和注入
RestController restController = AuthUtils.getAnnotationByBean(bean,RestController.class);
if(restController == null){
return bean;
}
Class cls = bean.getClass();
List<Method> methods = findAllMethod(cls);
if(CollectionUtils.isEmpty(methods)){
return bean;
}
for(Method method : methods){
initMethod(method);

}

return bean;
}

private void initMethod(Method method){
Annotation[][] parameterAnnotations= method.getParameterAnnotations();
//获取所有的有效注解参数 并处理这些
for (Annotation[] parameterAnnotation: parameterAnnotations) {
int index= ArrayUtils.indexOf(parameterAnnotations, parameterAnnotation);
for(Annotation item:parameterAnnotation){
if(item instanceof com.lumi.iiap.interceptor.dataauth.DataAuthAnnotation){
putAdapter(method,index,(DataAuthAnnotation)item);
}
}
}
}

private void putAdapter(Method method,int index,DataAuthAnnotation dataAuthAnnotation){
DataAuthHandleBean dataAuthHandleBean = new DataAuthHandleBean();
dataAuthHandleBean.setIndex(index);
dataAuthHandleBean.setDataAuthAnnotation(dataAuthAnnotation);
dataAuthHandleBean.setClasses(getInterceptors(dataAuthAnnotation.handleCls()));
Map<Integer,DataAuthHandleBean> map;
synchronized (dataAuthAdapter){
//防止多线程调用
if(dataAuthAdapter.containsKey(method)){

map= dataAuthAdapter.get(method);
map.put(index,dataAuthHandleBean);
}else {
map=new HashMap<>();
map.put(index,dataAuthHandleBean);
dataAuthAdapter.put(method,map);
}
}
//TODO debug
log.info("init dataAuthAdapter {}-{}-{}",method,index,map);
}



private List<Method> findAllMethod(Class clazz) {
final List<Method> res = new LinkedList<>();
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
//TODO 内存泄漏的风险?应该没有
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
res.add(method);
}
}, new ReflectionUtils.MethodFilter() {
@Override
public boolean matches(Method method){
return method.getAnnotation(RequestMapping.class)!=null;
}
});
return res;
}

//按照order排序并返回
protected List<Class<? extends DataAuthAbstractHandle>> getInterceptors(Class<? extends DataAuthAbstractHandle>[] classes) {
List<Class<? extends DataAuthAbstractHandle>> resultList= new ArrayList<>(Arrays.asList(classes));
return resultList.stream()
.sorted(DATAAUTHCLASS_ORDER_COMPARATOR)
.collect(Collectors.toList());
}

private static final Comparator<Object> DATAAUTHCLASS_ORDER_COMPARATOR =
OrderComparator.INSTANCE.withSourceProvider(object -> {
Object dataAuthAbstractHandle;

try {
if (object instanceof Class && (dataAuthAbstractHandle=((Class)object).newInstance()) instanceof DataAuthAbstractHandle) {
Ordered ordered = (Ordered) ((DataAuthAbstractHandle) dataAuthAbstractHandle)::getOrder;
dataAuthAbstractHandle=null;
return ordered;
}
} catch (InstantiationException e) {
log.error("DATAAUTHCLASS_ORDER_COMPARATOR error",e);
return null;
} catch (IllegalAccessException e) {
log.error("DATAAUTHCLASS_ORDER_COMPARATOR error",e);
return null;
}
return null;
});


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//建立切面,然后利用加载好的配置进行拦截和数据校验,相关功能已经放到之前写的utils里了。
/**
* 数据权限切面类
* @author yyt
*/
@Aspect
@Component
@Slf4j
public class DataAuthAspect implements ApplicationContextAware {

private ApplicationContext applicationContext;

@Autowired
private NoFilters noFilters;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* ..匹配controller 并且参数有DataAuthAnnotation的方法
*/
@Pointcut("execution(* com.lumi.iiap.controller..*.*(..))) && " +
"args(@annotation(com.lumi.iiap.interceptor.dataauth.DataAuthAnnotation))")
public void dataAuthMethod() {

}

//数据权限校验
@Around("dataAuthMethod()")
public Object checkDataAuth(ProceedingJoinPoint joinPoint) throws Throwable {
String url= SpringRequestHolderUtil.getRequest().getServletPath();
if(AuthUtils.matches(url,noFilters.getDataAuthWhiteList())){
return joinPoint.proceed();
}
HttpCommomHeader httpCommomHeader= HttpCommomHeader.getHttpCommomHeaderForIiap();
String userId=httpCommomHeader.getUserid();
if(StringUtils.isBlank(userId)){
//非用户登录,跳过验证
return joinPoint.proceed();
}
//获得方法入参并有指定注解的参数和其对象
Method method = ((MethodSignature)joinPoint.getSignature()).getMethod();
if (!AuthUtils.checkNeedDataAuthHandleBean(method)){
//不需要拦截
return joinPoint.proceed();
}
log.info("start checkDataAuth:{}",method.getName());
Object[] args= joinPoint.getArgs();
//获取所有的有效注解参数 并处理这些
for (Object arg: args) {
int index= ArrayUtils.indexOf(args, arg);
DataAuthHandlerAdapter.DataAuthHandleBean dataAuthHandleBean = AuthUtils.findDataAuthHandleBean(method,index);
if(dataAuthHandleBean!=null){
Boolean result= checkByParam(arg,dataAuthHandleBean);
if(!result){
log.error("checkDataAuth error:{},-{}",arg,dataAuthHandleBean);
throw new CustomBusinessException(ErrorCodeUtils.PermissionErrorCode.DATA_AUTH_DENIED);
}
}
}
log.info("end checkDataAuth:{}",method.getName());
return joinPoint.proceed();
}

//乐观型判断,所有代码配置性异常都返回true
private Boolean checkByParam(Object value, DataAuthHandlerAdapter.DataAuthHandleBean dataAuthHandleBean){
//权限判断
if(value==null){
return true;
}
DataAuthAnnotation annotation = dataAuthHandleBean.getDataAuthAnnotation();
String[] ids= annotation.idKeys();
int[] roleCodes= annotation.roleCodes();
if(ids.length==0 || roleCodes.length ==0 ){
return true;
}
//封装id集合
Map<String,Set<String>> allIds = new HashMap<>();
for(String idFiled:ids){
try {
Set<String> oneIds=AuthUtils.getStringByField(value,idFiled);
allIds.put(idFiled,oneIds);
} catch (IllegalAccessException e) {
log.error("getStringByField error:{}-{}",value,idFiled);
}
}
//Class<? extends DataAuthAbstractHandle> handleCls= annotation.handleCls()[0];
//开始拦截
HttpCommomHeader httpCommomHeader= HttpCommomHeader.getHttpCommomHeaderForIiap();
String userId=httpCommomHeader.getUserid();
for(Class<? extends DataAuthAbstractHandle> handleCls:dataAuthHandleBean.getClasses()){

if(handleCls== DataAuthAbstractHandle.class){
continue;
}
DataAuthAbstractHandle dataAuthAbstractHandle= applicationContext.getBean(handleCls);
log.info("annotation value:{}-{}-{}-{}",ids,roleCodes,handleCls.getName(),value);
//开始权限校验
log.info("begin dataAuthAbstractHandle:{}-{} ",userId,allIds);
Boolean aBoolean =dataAuthAbstractHandle.checkDataAuth(annotation, userId,allIds);
log.info("end dataAuthAbstractHandle:{}-{}-{} ",userId,allIds,aBoolean);
if(!aBoolean){
return aBoolean;
}

}
return true;
}
}

实际代码使用示例如下:

demo

CATALOG
  1. 1. 写在前面
  2. 2. 代码片段