Spring AOP

代码记录

首先要在maven中配置相关的aop配置

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

编写自己的Annotation

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DomainCheck {

    /**
     * 类或接口是否配置了domain信息
     * @return
     */
    String[] domains() default {};

    /**
     * 默认是类或者接口需是 domains 配置的 domain 才能访问
     * @return
     */
    boolean isPositive() default true;

}

编写Aspect aop 代码

@Slf4j
@Aspect
@Component
public class DomainCheckAop {

    // 方法级别的annotation 切点
    @Order(10)
    @Pointcut("execution(* com.huawei.analysis.controller..*.*(..)) && @annotation(domainCheck)")
    public void domainMethodCheckAop(DomainCheck domainCheck) {

    }

    @Before(value = "domainMethodCheckAop(domainCheck)", argNames = "joinPoint,domainCheck")
    public void domainMethodCheckBefore(JoinPoint joinPoint, DomainCheck domainCheck) throws DomainCheckException {
        log.info("domainMethodCheckBefore");
        domainCheck(joinPoint, domainCheck);
    }

    // 类级别的annotation 切点
    @Order(9)
    @Pointcut("within(com.huawei.analysis.controller..*) && @within(domainCheck)")
    public void domainClassCheckAop(DomainCheck domainCheck) {

    }

    @Before(value = "domainClassCheckAop(domainCheck)", argNames = "joinPoint,domainCheck")
    public void domainClassCheckBefore(JoinPoint joinPoint, DomainCheck domainCheck) throws DomainCheckException {
        log.info("domainClassCheckBefore");
        // 需要判断接口方法是否配置了 annotation
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        if (methodSignature.getMethod().isAnnotationPresent(DomainCheck.class)) {
            // 类上配置了 annotation 且方法上也配置了,优先使用方法上的配置
            log.info("类上配置了 DomainCheck 注解,方法上也使用了,即使用方法上的配置信息");
            DomainCheck annotation = methodSignature.getMethod().getAnnotation(DomainCheck.class);
            domainCheck(joinPoint, annotation);
        } else {
            domainCheck(joinPoint, domainCheck);
        }
    }

    private void domainCheck(JoinPoint joinPoint, DomainCheck domainCheck) throws DomainCheckException {
        String[] domains = domainCheck.domains();
        boolean positive = domainCheck.isPositive();
        if (domains.length > 0) {
            Signature signature = joinPoint.getSignature();
            String declaringTypeName = signature.getDeclaringTypeName();
            log.info("declaringTypeName : {}", declaringTypeName);
            MethodSignature methodSignature = (MethodSignature) signature;
            String methodName = methodSignature.getMethod().getName();
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
            String domain = request.getHeader("domain");    // 模拟从 request 中获取 token,解析出 domain信息
            if (StringUtils.isEmpty(domain)) {
                log.error("未从 request 中获取到 domain 信息,接口方法 : {} 校验失败!", methodName);
                throw new DomainCheckException("未从 request 中获取到 domain 信息, 校验失败");
            }
            doCheck(domains, domain, positive, methodName);
        } else {
            log.info("未配置 domain 校验,校验成功");
        }
    }

    private void doCheck(String[] domains, String domain, boolean isPositive, String methodName) throws DomainCheckException {
        if (isPositive) {
            for (String domainName : domains) {
                if (domain.equals(domainName)) {
                    log.info("request 中获取的 domain 信息为 : {}, 接口方法 : {} 检验成功!", domain, methodName);
                    return;
                }
            }
            log.error("request 中获取的 domain 信息与方法配置的不一致,接口方法 : {} 校验失败!", methodName);
            throw new DomainCheckException("request 中获取的 domain 信息与方法配置的不一致, 校验失败");
        } else {
            for (String domainName : domains) {
                if (domain.equals(domainName)) {
                    log.error("request 中获取的 domain 信息为 : {}, 访问接口方法 : {} 不能包含 : {}, 接口校验失败!", domain, methodName ,domain);
                    throw new DomainCheckException("request 中获取的 domain 信息与方法配置的不一致, 校验失败");
                }
            }
            log.info("request 中获取的 domain 信息为 : {}, 接口方法 : {} 检验成功!", domain, methodName);
        }
    }
}

Controller类的校验,如果方法上已有相应校验,那么会忽略类上的校验

@RestController
@RequestMapping(value = "/domain")
@DomainCheck(domains = {"pom", "serviceStage"})
public class DomainController {

    @GetMapping(value = "/{domain}")
    public String domain(@PathVariable("domain") String domainName) {
        return domainName;
    }

    @GetMapping(value = "/class/{domain}")
    @DomainCheck(domains = {"pom", "serviceStage"}, isPositive = false)
    public String clazz(@PathVariable("domain") String domainName) {
        return domainName;
    }
}
posted @ 2022-03-02 10:31  gkaigk  阅读(28)  评论(0编辑  收藏  举报