使用spring的AOP时候,想要过滤掉某些路径或者忽略指定注解,怎么做?
- shiro学习系列
- 时间:2024-05-30 16:01
- 1007人已阅读
🔔🔔🔔好消息!好消息!🔔🔔🔔
有需要的朋友👉:联系凯哥
背景:
项目使用shiro做安全框架的。现在需要使用Spring AOP切面记录日志。
切入点:
@Pointcut("execution(public * com.kaigejava.business..controller..*.*(..))")
这样配置会有问题。因为有些地方需要忽略shiro认证。比如在shiroConfig中,我配置了很多。如下:
filterChainDefinitionMap.put("/diagram-viewer/**", "anon");
filterChainDefinitionMap.put("/editor-app/**", "anon");
filterChainDefinitionMap.put("/riskNotificationLetter/download/**", "anon"); //草稿箱下载权限放开
filterChainDefinitionMap.put("/login/toLogin", "anon");
filterChainDefinitionMap.put("/homePageSimulationData/**", "anon");
filterChainDefinitionMap.put("/synchronizationEdataController/**", "anon");
filterChainDefinitionMap.put("/dayReportController/**", "anon");
同时我在JwtFilter中配置了
//校验类上有没有此注解。如果类类存在此注解,整个类的url都不会被拦截
ShiroIgnoreAuth anonymousAccess = AnnotationUtils.getAnnotation(nowClass, ShiroIgnoreAuth.class);
那么我上面的的切入点表达式应该怎么写?
1. 基于路径排除
使用within
来指定包,并且使用@annotation
或!@annotation
来排除带有特定注解的方法。由于你列出的路径在AOP切面中无法直接匹配(因为路径是URL模式,而不是Java包路径),你需要在AOP切面中手动添加排除逻辑。
2. 排除带有注解的类或方法
你可以在AOP中检查方法或类是否带有特定注解,然后根据需要进行处理。
切入点表达式
要只切入controller
包中的方法,同时排除特定路径和注解,你可以结合使用多个切入点表达式。
示例代码:
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; @Aspect @Component public class MyAspect { @Autowired private HttpServletRequest request; // 切入com.kaigejava.business包及其子包下的controller包中的所有public方法 @Pointcut("execution(public * com.kaigejava.business..controller..*.*(..))") public void controllerMethods() { } // 排除带有ShiroIgnoreAuth注解的类或方法 @Pointcut("!@within(com.kaigejava.shiro.ShiroIgnoreAuth) && !@annotation(com.kaigejava.shiro.ShiroIgnoreAuth)") public void excludeAnnotatedMethods() { } // 组合切入点:排除注解的方法 @Pointcut("controllerMethods() && excludeAnnotatedMethods()") public void combinedPointcut() { } @Before("combinedPointcut()") public void before(JoinPoint joinPoint) { // 获取当前请求的URI HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String requestUri = request.getRequestURI(); // 定义需要排除的路径 String[] excludePaths = { "/diagram-viewer/**", "/editor-app/**", "/riskNotificationLetter/download/**", "/login/toLogin", "/homePageSimulationData/**", "/synchronizationEdataController/**", "/dayReportController/**", "/safetyRegulationsFile/**", "/risk/file/download/**", "/gis/sensorLogTrendExcel", "/gis/download", "/login/login", "/**/fileDownload", "/specialKnowledge/export", "/risk/camera/importexcel", "/workticket/ticketListExcel", "/breakrule/export", "/projectRisk/fileDownload", "/riskLeanManagement/export", "/login/initUserPassword", "/", "/doc.html", "/**/*.js", "/**/*.css", "/**/*.html", "/**/*.svg", "/**/*.pdf", "/**/*.jpg", "/**/*.png", "/**/*.txt", "/**/*.ico", "/*", "/druid/**", "/swagger-ui.html", "/swagger**/**", "/v3/**", "/v2/**" }; // 检查当前请求是否在排除路径中 for (String excludePath : excludePaths) { if (pathMatches(requestUri, excludePath)) { return; // 排除路径,直接返回,不执行后续逻辑 } } // TODO 执行切面逻辑 } private boolean pathMatches(String requestUri, String excludePath) { // 简单的路径匹配实现,具体可以根据需要进行增强 if (excludePath.endsWith("/**")) { String basePath = excludePath.substring(0, excludePath.length() - 3); return requestUri.startsWith(basePath); } else { return requestUri.equals(excludePath); } } }
解释
切入点表达式:
@Pointcut("execution(public * com.kaigejava.business..controller..*.*(..))")
:匹配controller
包及其子包中的所有public
方法。@Pointcut("!@within(com.kaigejava.shiro.ShiroIgnoreAuth) && !@annotation(com.kaigejava.shiro.ShiroIgnoreAuth)")
:排除带有ShiroIgnoreAuth
注解的类或方法。动态排除路径:
在
before
方法中,获取当前请求的URI。定义需要排除的路径列表。
检查当前请求是否在排除路径中,如果在,则直接返回,不执行后续逻辑。
路径匹配实现:
pathMatches
方法实现了简单的路径匹配逻辑,支持/**
通配符。
知识点:
在 Spring AOP 中,直接获取 HttpServletRequest
是不容易的,因为切面方法并不直接处理 HTTP 请求。为了解决这个问题,我们可以通过 Spring 的 RequestContextHolder
来获取当前的请求。
以下是修改后的完整示例,包括使用 RequestContextHolder
来获取 HttpServletRequest
,