springboot自定义校验

最近遇到一个项目,需要在其他模块开启权限,校验,无法直接使用@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.png ![[image-20240826111947407.png]]

Licensed under CC BY-NC-SA 4.0
最后更新于 Jan 06, 2025 05:52 UTC
comments powered by Disqus
Built with Hugo
主题 StackJimmy 设计
Caret Up