Java安全代码审计中常见的全局过滤方式

概述

Spring中常见的全局过滤方式有三种:Filter、Conterceptor、Aspect。

  1. 三者的执行顺序是:Filter > Conterceptor > Aspect。
  2. Filter在请求到达具体的Controller之前执行,所以无法获取到Controller相关的数据,仅仅能处理请求及响应数据流。
  3. Conterceptor可以获取到处理的Controller方法的相关的数据,但是此时的Controller是以静态方法(非动态)的形式出现的。
  4. Aspect可以获取到赋值给处理方法的参数值。
  5. 还有一个自定义参数解析器,可以针对某个参数实现一些特殊过滤逻辑(可能用来从cookie中解析出来真实用户)。

Java安全代码审计中常见的全局过滤方式

一、过滤器 Filter

属于 javax.servlet.Filter 技术。

1、定义具体逻辑

public class XxxxFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        /*前置过滤逻辑*/
        filterChain.doFilter(servletRequest, servletResponse);
        /*后置过滤逻辑*/
    }
}

2、配置该过滤器

// 1、 web.xml方式配置
    <filter>
        // filter名,随便起
        <filter-name>AdminFilter</filter-name>
        // 实现filter的类
        <filter-class>cn.kihyou.b2c.filter.AdminFilter</filter-class>
        // 初始化参数
        <init-param></init-param>
    </filter>
    
    <filter-mapping>
        // 对应的filter名
        <filter-name>AdminFilter</filter-name>
        // 要进行拦截过滤的目录
        <url-pattern>/api/admin/*</url-pattern>
   </filter-mapping>
   
// 2、注解的形式配置
在 Filter 的实现类上添加 @WebFilter 注解
eg. @WebFilter(filterName="log",urlPatterns={"/*"})

二、拦截器 Conterceptor

属于 org.springframework.web.servlet.HandlerInterceptor 技术。

1、定义具体逻辑:

public class XxxxInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
        /*前置过滤逻辑*/
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, ModelAndView modelAndView) throws Exception {
        // 其中 handler参数为处理该请求的 Controller 方法
        /*后置过滤逻辑1*/
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception e) throws Exception {
        /*后置过滤逻辑2,常用于清理资源*/
    }
}

2、配置该拦截器

// 1、 SpringMVC的配置文件中
<!-- 配置拦截器 -->
<mvc:interceptors>
    <!-- 配置一个全局拦截器,拦截所有请求 -->
    <bean class="interceptor.TestInterceptor" /> 
    <mvc:interceptor>
        <!-- 配置拦截器作用的路径 -->
        <mvc:mapping path="/**" />
        <!-- 配置不需要拦截作用的路径 -->
        <mvc:exclude-mapping path="" />
        <!-- 定义<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
        <bean class="interceptor.Interceptor1" />
    </mvc:interceptor>
</mvc:interceptors>
// 2、注解的形式配置
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
  @Autowired
  private XxxxInterceptor xxxxInterceptor;
  
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(xxxxInterceptor);
    registration.addPathPatterns("/**");     //所有路径都被拦截
    registration.excludePathPatterns("/static/*.html","/static/*.js",)     //添加不拦截的路径
  }
}

Tips:SpringMVC的配置文件有两种方式:

项目中到底使用的是哪一种,需要在在web.xml中查看 <servlet></servlet>配置。

// 1、 指定位置
<servlet>
    <servlet-name>dispatcherSerlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>   // 指定SpringMVC的配置文件是 classess文件夹下的springmvc.xml文件
    </init-param>
</servlet>


// 2、默认位置
<servlet>
    <servlet-name>dispatcherSerlet</servlet-name>    // 默认SpringMVC的配置文件是:在WEB-INF文件夹下的 {此处的servlet-name}-servlet.xml
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

三、切面 Aspect

属于 org.aspectj.lang.annotation.Aspect 技术。

1、定义作用标识的注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface XxxxXxx {
}

2、定义具体逻辑

@Aspect
@Component
public class XxxxAop{
    //定义切点
    @Pointcut("@annotation(com.xxx.annotation.XxxxXxx)")
    public void audit() {
    }
    
    @Around("audit()")
    public Object handleControllerMethod(ProceedingJoinPoint point) throws Throwable {
        /*前置过滤逻辑*/
        Object returnValue = point.proceed()
        /*后置过滤逻辑*/
    }
    
    @Before("audit()")
    public void before(JoinPoint joinPoint) {
        /*前置过滤逻辑*/
    }
    @After("audit()")
    public void after(JoinPoint joinPoint) {
        /*后置过滤逻辑*/
    }
    
    @AfterReturning()
    public void afterReturning(JoinPoint joinPoint, Object result) {}
    
    @AfterThrowing()
    public void afterThrowing(JoinPoint joinPoint, Throwable exception) {}
}

3、配置启动AOP

// 1、xml文件配置
 <!-- 声明类 ,此类作为切面类使用  -->
 <bean id="logInterceptor" class="com.aop.LogInterceptor" />
 <aop:config>
  <!-- 设置切面名,及切面类 -->
  <aop:aspect id="logAspect" ref="logInterceptor">
  
   <!-- 运行前方法配置,先择要执行的方法 ,并设置切入点  -->
   <aop:before method="before" pointcut="execution(public * com.userService.*.add(..))" />
   
   <!-- 先设置切入点,待使用  -->
   <aop:pointcut id="servicePointcut" expression="execution(public * com.userService.*.add(..))" />
   
   <!-- 运行后方法配置,先择要执行的方法,参考预先设置好的切入点  -->
   <aop:after method="after" pointcut-ref="servicePointcut" />
   
  </aop:aspect>
 </aop:config>


// 2、启动注解配置, Spring默认不支持@Aspect风格的切面声明,通过如下配置开启@Aspect支持
    <aop:aspectj-autoproxy/>  

四、自定以参数解析器

属于 org.springframework.web.method.support.HandlerMethodArgumentResolver 技术。

对于大型项目,通常会有很多引用包,其中封装了一些自定义注解,具体的实现逻辑就需要好好翻翻。

1、定义作用标识的注解

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface XxxxXxx {
}

2、定义具体逻辑

public class XxxxHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        // 用于判定是否需要处理该参数,返回 true 为需要,并会去调用下面的方法resolveArgument。
        return methodParameter.getParameterType().isAssignableFrom(UserParam.class)
                && methodParameter.hasParameterAnnotation(XxxxXxx.class);
    }
    
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        // 具体的得到参数值的逻辑,return 返回参数值
        return value;
    }
}

3、配置该参数解析器

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new XxxxXxxHandlerMethodArgumentResolver());
    }
}

五、寻找非当前项目实现的自定义参数的逻辑代码位置Tips

当前项目声明的自定义参数实现逻辑通过在当前项目文件夹搜索HandlerMethodArgumentResolver 便可快速定位。

第一步、首先找到一个使用位置

Java安全代码审计中常见的全局过滤方式

第二步、定位到具体的声明位置

Java安全代码审计中常见的全局过滤方式

第三步、通过 Find Usages 功能查找resolve的相关文件

Java安全代码审计中常见的全局过滤方式


关注公众号获取更多干货

Java安全代码审计中常见的全局过滤方式

上一篇:Spring AOP(面向切面编程)


下一篇:@Valid和@Validated