最近遇到一个项目,需要在其他模块开启权限,校验,无法直接使用@PreAuthorize 来进行权限校验。所以需要在其他模块开启注解来解决这个问题。
自定义注解 PreAuth
1
|
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface PreAuth { value(); }
|
定义切面类
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
|
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.MethodParameter;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
@Aspect
@Component
@Slf4j
public class PreAuthAop {
@Autowired
private ApplicationContext ctx;
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
private boolean handleAuth(ProceedingJoinPoint point) { MethodSignature ms = point.getSignature() instanceof MethodSignature? (MethodSignature) point.getSignature():null;
Method method = ms.getMethod(); // 读取权限注解,优先方法上,没有则读取类
PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class); // 判断表达式
String condition = preAuth.value(); if (StringUtil.isNotBlank(condition)) { Expression expression = EXPRESSION_PARSER.parseExpression(condition); // 方法参数值
Object[] args = point.getArgs();
StandardEvaluationContext context = getEvaluationContext(method, args); //获取解析计算的结果
return expression.getValue(context, Boolean.class);
}
return false;
} /** * 获取方法上的参数 *
* @param method 方法
* @param args 变量
* @return {SimpleEvaluationContext} */
private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) { // 初始化Sp el表达式上下文,并设置 A
uthFun StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun()); // 设置表达式支持spring bean
context.setBeanResolver(new BeanFactoryResolver(applicationContext)); for (int i = 0; i < args.length; i++) { // 读取方法参数
MethodParameter methodParam = ClassUtil.getMethodParameter(method, i); // 设置方法 参数名和值 为spel变量
context.setVariable(methodParam.getParameterName(), args[i]);
}
return context;
}
}
|
自定义解析方法
看完上面的解析处理是不是很蒙蔽,只看到了获取表达式,获取参数,设置参数,然后expression.getValue
就完事了。有的同学会问,你权限校验的逻辑呢?
别急,关键点在这:StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun());
在上文代码中找到了吧。这个 AuthFun 就是我们进行权限校验的对象。
所以呢,我们还得在定义一下这个对象。进行具体的权限校验逻辑处理,这里定的每一个方法都可以作为表达式在权限注解中使用。代码如下:
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
|
public class AuthFun {
/**
* 判断角色是否具有接口权限
*
* @return {boolean}
*/
public boolean permissionAll() {
//TODO
}
/**
* 判断角色是否具有接口权限
*
* @param permission 权限编号,对应菜单的MENU_CODE
* @return {boolean}
*/
public boolean hasPermission(String permission) {
//TODO
}
/**
* 放行所有请求
*
* @return {boolean}
*/
public boolean permitAll() {
return true;
}
/**
* 只有超管角色才可访问
*
* @return {boolean}
*/
public boolean denyAll() {
return hasRole(RoleConstant.ADMIN);
}
/**
* 是否已授权
*
* @return {boolean}
*/
public boolean hasAuth() {
if(Func.isEmpty(AuthUtil.getUser())){
// TODO 返回异常提醒
}else{
return true;
}
}
/**
* 是否有时间授权
*
* @param start 开始时间
* @param end 结束时间
* @return {boolean}
*/
public boolean hasTimeAuth(Integer start, Integer end) {
Integer hour = DateUtil.hour();
return hour >= start && hour <= end;
}
/**
* 判断是否有该角色权限
*
* @param role 单角色
* @return {boolean}
*/
public boolean hasRole(String role) {
return hasAnyRole(role);
}
/**
* 判断是否具有所有角色权限
*
* @param role 角色集合
* @return {boolean}
*/
public boolean hasAllRole(String... role) {
for (String r : role) {
if (!hasRole(r)) {
return false;
}
}
return true;
}
/**
* 判断是否有该角色权限
*
* @param role 角色集合
* @return {boolean}
*/
public boolean hasAnyRole(String... role) {
//获取当前登录用户
BladeUser user = AuthUtil.getUser();
if (user == null) {
return false;
}
String userRole = user.getRoleName();
if (StringUtil.isBlank(userRole)) {
return false;
}
String[] roles = Func.toStrArray(userRole);
for (String r : role) {
if (CollectionUtil.contains(roles, r)) {
return true;
}
}
return false;
}
}
|
如果想 Valid 的优先于 AOP 执行,我们得让做如下设置,通过 BindResult 来接受校验。
在 controller 参数中加入 BindingResult,使得@Valid 的验证结果封装到其中,而后在切面中再进行处理
![[image-20240826111947407.png]]