通过Spring aop实现注解动态绑定参数

注解绑定参数

Posted by jinguomin on December 20, 2019

前言

产品提出新的需求,操作数据库数据需要对当前用户进行校验,具有资格的用户才能操作,但是用户数据在其他的平台上,只能通过http请求发送token + caseId获取返回值来校验。

  • 为什么使用aop?
  • 项目采用restful,使用拦截器+filter这种方式无法解析全部请求中caseId,例如使用@PathVariable注解无法解析。

    编写注解

@Target(ElementType.METHOD)  // 用在方法上
@Retention(RetentionPolicy.RUNTIME)  // 运行
@Documented
public @interface AuthorityCheck {

  //注解中使用了元注解时,可以对元注解的值进行重写,并且可用多个不同的别名进行重写(其实就是一和二的结合)
  @AliasFor("name")
  String value() default "caseId";

  @AliasFor("value")
  String name() default "caseId";
}

编写切面

@Aspect  //定义切面
@Component
public class AuthorityAspect {

  // 判断是否开启权限校验
  @Value("${authority.open}")  // 从配置文件读取
  private Boolean open;

  // 用户平台地址
  @Value("${authority.authorityUrl}")
  private String authorityUrl;

  //
  @Pointcut("@annotation(com.bmsoft.evidence.aop.AuthorityCheck)")
  public void annotationPoinCut() {
  }


  @After("annotationPoinCut()")
  public void after(JoinPoint joinPoint) {
  }

  @Before("annotationPoinCut()")
  public void before(JoinPoint joinPoint) {
    //判断是否开启
    if (!open) {
      return;
    }
    RequestAttributes ra = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes sra = (ServletRequestAttributes) ra;
    // 获取request 拿到token
    HttpServletRequest request = sra.getRequest();
    String token = request.getHeader("Authorization");
    //校验token
    if (token == null) {
      throw new AppException(ErrorCode.GET_DATA_FAILED, "请求头Authorization为空!");
    }
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    // 获取接口全部参数
    Object[] args = joinPoint.getArgs();
    String[] argNames = signature.getParameterNames();
    Method method = signature.getMethod();
    AuthorityCheck action = method.getAnnotation(AuthorityCheck.class);
    String value = action.value();
    List<String> paths = Arrays.asList(value.split("\\."));
    if (paths.size() == 0) {
      throw new UnknownAnnotationValueException(null, action.value());
    }
    String firstName = paths.get(0);
    String caseId = "";
    for (int i = 0; i < argNames.length; i++) {
      String argName = argNames[i];
      Object arg = args[i];
      Class<?> aClass = arg.getClass();
      if (argName.equals(firstName)) {
        // 判断caseI这个参数实在dto 还是直接就在请求头中
        if (paths.size() > 1) {
          caseId = takeCaseId(aClass, arg, paths, 1);
        } else if (arg instanceof String) {
          caseId = (String) arg;
        } else {
          throw new RuntimeException("caseId字段类型错误");
        }
      }
    }
    token = token.replace("bearer ", "");
    JSONObject result;
    String url = String.format("%s", authorityUrl);
    Map<String, Object> checkPost = new HashMap<>();
    checkPost.put("ajbs", caseId);
    checkPost.put("accessToken", token);
    try {
      HttpClientResult httpClientResult = ClientHelp.doPost(url, checkPost);
      if (httpClientResult.getCode() != 200) {
        throw new AppException(ErrorCode.GET_DATA_FAILED, "权限接口调用出错");
      }
      String content = httpClientResult.getContent();
      result = JSON.parseObject(content);
    } catch (Exception e) {
      throw new AppException(ErrorCode.GET_DATA_FAILED, "获取权限失败");
    }
    if (result.getInteger("code") != 1000) {
      throw new AppException(ErrorCode.GET_DATA_FAILED, "权限接口调用出错");
    }
    if (!result.getBoolean("data")) {
      throw new AppException(ErrorCode.GET_DATA_FAILED, "您不是承办人,无操作权限!");
    }
  }

  // 通过反射找到caseId的值并且返回
  private String takeCaseId(Class<?> clazz, Object object, List<String> paths, Integer pathNum) {
    if (paths.get(pathNum) == null) {
      return null;
    }
    String s = paths.get(pathNum);
    try {
      Field field = clazz.getDeclaredField(s);
      field.setAccessible(true);
      Object o = field.get(object);
      if (paths.size() > pathNum + 1) {
        return takeCaseId(o.getClass(), o, paths, pathNum + 1);
      } else if (o instanceof String) {
        String caseId = (String) o;
        return caseId;
      } else {
        throw new RuntimeException("caseId字段类型错误");
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return "";
  }
}