关闭

切面功能实现

标签: aopinterceptorfilter
174人阅读 评论(0) 收藏 举报
分类:

开发过程中,碰到需要需要拦截过滤的需求,特此整理下的。
目前碰到的,也经常使用的有三种:Filter过滤器方式、Interceptor拦截器、AOP切面方式。后面两种在spring中常用。

1》 Filter:
filter的实现简单,属于servlet的 。可以实现javax.servlet.Filter即可。查看源码中,可以看到实现filter的有很多类,我们常用到的就用:
- org.springframework.web.filter.CharacterEncodingFilter
- org.springframework.web.multipart.support.MultipartFilter
等等。下面就用工作中碰到的具体业务为例说明:
SecurityUrlFilter:
这个过滤器是用于某些安全请求,某些请求路径必须是校验通过之后才能访问。

package com.zcl.filter;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.IOUtils;

import com.zcl.util.MD5Utils;

@WebFilter(displayName="filterName", urlPatterns={"/*"}, initParams={
        @WebInitParam(name="openFilter", value="true"),
        @WebInitParam(name="securityKey", value="1w3e$dr3"),
        @WebInitParam(name="includeUrls", value="/security/delid.htm,/user/add.htm")})
public class SecurityUrlFilter implements Filter {

    //是否开启过滤
    private boolean openFilter = false;
    //需要过滤的路径
    private Set<String> includeUrls;
    //安全key
    private String securityKey;

    public void init(FilterConfig filterConfig) throws ServletException {
        //获取是否开启过滤
        String openFilter = filterConfig.getInitParameter("openFilter");
        this.openFilter = openFilter != null && "true".equals(openFilter.trim());

        //获取路径
        String urls = filterConfig.getInitParameter("includeUrls");
        if(urls != null && !"".equals(urls.trim())) {
            includeUrls = new HashSet<String>(Arrays.asList(urls.split("\\s*,\\s*")));
        }
    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        String requestUrl = ((HttpServletRequest)request).getRequestURI();

        if(includeUrls != null && includeUrls.size() > 0 ) {
            boolean securityCheck = false;
            for(String url: includeUrls) {
                if(requestUrl.matches(url)) {
                    securityCheck = true;
                    break;
                }
            }

            if(securityCheck) {
                String token = ((HttpServletRequest)request).getParameter("token");
                if(token == null || "".equals(token.trim())) {
                    writerMsg(response.getWriter(), "必须传入token验证参数");
                    return;
                }

                if(!token.equals(MD5Utils.MD5(securityKey))) {
                    writerMsg(response.getWriter(), "token验证参数不正确");
                    return;
                }
            }
        }

        chain.doFilter(request, response);
    }

    private void writerMsg(PrintWriter writer, String msg) {
        try {
            writer.write(msg);
        } catch(Exception e) {

        } finally {
            IOUtils.closeQuietly(writer);
        }
    }

    public void destroy() {
        // TODO Auto-generated method stub
    }

}

初始化相关的实现在init(FilterConfig filterConfig)方法中实现;
功能处理在doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)方法中实现。
注意,我使用的是servlet3.1的,支持注解方式。

@WebFilter(filterName="filterName", urlPatterns={"/*"}, initParams={
        @WebInitParam(name="openFilter", value="true"),
        @WebInitParam(name="securityKey", value="1w3e$dr3"),
        @WebInitParam(name="includeUrls", value="/security/delid.htm,/user/add.htm")})

对于版本低的,是在配置web.xml文件中配置。

 <filter>
        <filter-name>filterName</filter-name>
        <filter-class>com.zcl.filter.SecurityUrlFilter</filter-class>
        <init-param>
            <param-name>openFilter</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>securityKey</param-name>
            <param-name>1w3e$dr3</param-name>
        </init-param>
        <init-param>
            <param-name>includeUrls</param-name>
            <param-value>
                /security/delid.htm,
                /user/add.htm
            </param-value>
        </init-param>
       </filter>

       <filter-mapping>
        <filter-name>filterName</filter-name>
        <url-pattern>*</url-pattern>
       </filter-mapping>

这是使用过滤器的方式。不过要注意他们的流程:

Created with Raphaël 2.1.0web请求web.xml中filter(按照以上到下的顺序)注解方式中的filter(按照class名称字母排序)DispatcherServlet(分配实现)具体Controller具体controller

注意:filter的注解方式没有提供排序的参数。在开发中,可以通过filter的class名称字母来排序(filterName的字母排序是不起作用的)。

2》Interceptor:
拦截器是spring提供的过滤方式,最上层的是接口HandlerInterceptor。spring提供了很多它的实现类。平时项目中常用的是自定义class继承HandlerInterceptorAdapter。

package com.zcl.interceptor;

import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.zcl.constants.Constants;
import com.zcl.util.CSRFTokenManager;

/**
 * CSRF防御拦截处理
 * 
 * @ClassName: CSRFDefenseInterceptor
 * @Description: TODO
 * @author ****
 * @date May 5, 2016 4:21:49 PM
 * 
 *
 */
public class CSRFDefenseInterceptor extends HandlerInterceptorAdapter {

    // 需要过滤的路径
    private List<String> includedUrls;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        String requestMethod = request.getMethod();

        // 拦截post方法
        if (!"POST".equals(requestMethod))
            return true;

        String requestUrl = request.getRequestURI();

        // 拦截的urls
        if (includedUrls != null && includedUrls.size() > 0) {
            boolean interceptFlag = false;
            for (String url : includedUrls) {
                if (requestUrl.matches(url)) {
                    interceptFlag = true;
                    break;
                }
            }
            if (!interceptFlag)
                return true;

            // 不拦截的urls
        }

        boolean checkResult = CSRFTokenManager.checkToken(Constants.CSRF_TOKEN, Constants.SESSION_KEY,
                request);
        if (!checkResult) {
            String toErrorUrl = "/error?errorCode=-1&errorMsg=Cross-site request forgery";
            response.sendRedirect(request.getContextPath() + toErrorUrl);
        }
        return checkResult;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        super.postHandle(request, response, handler, modelAndView);
        if (modelAndView != null) {
            // 执行完成过后,页面添加csrfToken参数
            String csrfToken = CSRFTokenManager.createCsrfToken(Constants.CSRF_TOKEN,
                    Constants.SESSION_KEY, request);
            modelAndView.addObject(Constants.CSRF_TOKEN, csrfToken);
        }
    }

    public List<String> getIncludedUrls() {
        return includedUrls;
    }

    public void setIncludedUrls(List<String> includedUrls) {
        this.includedUrls = includedUrls;
    }

}

--------------------------------------------------------
package com.zcl.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.zcl.util.DESUtil;

public class ParamChangeInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        /**
         * 参数过滤
         */
        String secrityCode = request.getParameter("secrityCode");
        if(StringUtils.isBlank(secrityCode))
            return false;

        secrityCode = DESUtil.encrypt(secrityCode);
        request.setAttribute("secrityCode", secrityCode);
        return super.preHandle(request, response, handler);
    }
}

其中主要的是涉及的方法:

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;
//此方式在请求执行前进行处理的,通过返回值来确定是否继续进入后续执行。

@Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception;
//此方法是执行方法,可以看出多了ModelAndView的参数,这样就拓展了对页面、页面参数等的处理。

void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;
//此方式是在请求处理完成之后,

同时这个拦截器需要在.xml中配置

<bean id="csrfInterceptor" class="com.zcl.interceptor.CSRFDefenseInterceptor" >
        <property name="includedUrls">
            <list>
                <value>/log.htm</value>
            </list>
        </property>
    </bean>

   <mvc:interceptors>
    <!-- csrf拦截过滤 -->
    <mvc:interceptor>
        <mvc:mapping path="/*"/>
        <mvc:exclude-mapping path="/resource/*"/>
        <ref bean="csrfInterceptor"/>
    </mvc:interceptor>

    <!-- secrityCode参数解码处理 -->
    <mvc:interceptor>
        <mvc:mapping path="/*"/>
        <bean class="com.zcl.interceptor.ParamChangeInterceptor"></bean>
    </mvc:interceptor>

   </mvc:interceptors>

注意的是,拦截器的执行顺序是最先开始的最后执行完成。

拦截器与过滤器区别:

  1. 拦截器是基于Java的反射机制的,而过滤器是基于函数回调。
  2. 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
  3. 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
  4. 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
  5. 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
  6. 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
  7. 他们都是AOP思想的实践。

他们的执行过程:

Created with Raphaël 2.1.0web请求filter(执行chain.doFilter之前的处理)DispatcherServlet(分配实现)interceptor(执行preHandle中的处理)controller中的业务实现interceptor(执行postHandle中的处理)interceptor(执行afterCompletion中的处理)filter(执行chain.doFilter之后的处理)具体controller

3》AspectJ:

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:8303次
    • 积分:260
    • 等级:
    • 排名:千里之外
    • 原创:17篇
    • 转载:11篇
    • 译文:0篇
    • 评论:0条