JEECMS——过滤器和拦截器

来源:互联网 发布:windows优化软件知乎 编辑:程序博客网 时间:2024/06/08 20:20

写在前面

如果图片显示不清楚的,可以右键图片,选择在新标签页中打开图片

注:过滤器和拦截器是项目的关键部分,因为每一次请求都会经过不同的过滤器和拦截器,进行一系列的包装,判断,过滤等等,只有先了解了这个部分,知道了请求过来时JEECMS都事先做了哪些事,然后再去深入项目中的功能实现,会更加快速便捷。

一.概况

JEECMS配置的过滤器

过滤器 映射关系 ProcessTimeFilter *.do *.jspx *.jhtml *.htm *.jsp CharacterEncodingFilter *.do *.jspx *.jhtml *.htm *.jsp OpenSessionInViewFilter *.do *.jspx *.jhtml *.htm *.jsp / DelegatingFilterProxy /* XssFilter *.jspx *.jhtml *.html *.jsp ResourceFilter /wenku/*

注:其配置位置在web.xml中,如果对项目入口配置文件还不太熟悉,可以先看看 JAVA开源CMS内容管理系统JEECMS—-web.xml配置

JEECMS配置的主要的拦截器

拦截器 类别 AdminContextInterceptor 后台拦截器 AdminLocaleInterceptor 后台拦截器 FireWallInterceptor 后台拦截器 FrontContextInterceptor 前台拦截器 FrontLocaleInterceptor 前台拦截器

前台拦截器配置(jeecms-servlet-front.xml中)

<!-- 这个其实已经过时了,不知道为什么还用 -->    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">        <property name="interceptors">            <list>                <ref bean="frontContextInterceptor"/>                <ref bean="frontLocaleInterceptor"/>            </list>        </property>    </bean>    <bean id="frontContextInterceptor" class="com.jeecms.cms.web.FrontContextInterceptor"/>    <bean id="frontLocaleInterceptor" class="com.jeecms.cms.web.FrontLocaleInterceptor"/>

后台拦截器配置(jeecms-servlet-admin.xml中)

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">        <property name="interceptors">            <list>                <ref bean="adminContextInterceptor"/>                <ref bean="adminLocaleIntercept"/>                <ref bean="fireWallInterceptor"/>            </list>        </property>    </bean>    <bean id="adminContextInterceptor" class="com.jeecms.cms.web.AdminContextInterceptor">        <!--<property name="adminId" value="1"/>-->        <property name="auth" value="true"/>        <property name="excludeUrls">            <list>                <value>/login.do</value>                <value>/logout.do</value>            </list>        </property>    </bean>    <bean id="adminLocaleIntercept" class="com.jeecms.cms.web.AdminLocaleInterceptor"/>    <bean id="fireWallInterceptor" class="com.jeecms.cms.web.FireWallInterceptor"/>

注:如果对目录结构还不太熟悉,可以先看看 JAVA开源CMS内容管理系统JEECMS—-项目包结构

二.过滤器细化

ProcessTimeFilter

这个是JEECMS自定义的一个Filter,计算一个请求执行的时间

package com.jeecms.common.web;import java.io.IOException;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import org.slf4j.*;/** * 执行时间过滤器 * 请求的映射:*.do *.jspx *.jhtml *.htm *.jsp * LiuChengxiang * @time 2017年11月21日下午2:52:35 * */public class ProcessTimeFilter implements Filter {    protected final Logger log = LoggerFactory.getLogger(ProcessTimeFilter.class);    /**     * 请求执行开始时间     */    public static final String START_TIME = "_start_time";    /**     * 作用:仅仅是计算程序处理一个请求所花费的时间(如果没有这方面的需求,这个过程可以剔除以优化程序响应速度)     */    public void doFilter(ServletRequest req, ServletResponse response,FilterChain chain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) req;        long time = System.currentTimeMillis();        request.setAttribute(START_TIME, time);        chain.doFilter(request, response);        time = System.currentTimeMillis() - time;        log.debug("process in {} ms: {}", time, request.getRequestURI());    }    public void init(FilterConfig arg0) throws ServletException {}    public void destroy() {}}

CharacterEncodingFilter

Spring-web自带的编码过滤器。调用过程为FilterChain—>OncePerRequestFilter.doFilter()—>CharacterEncodingFilter.doFilterInternal()

@Override    protected void doFilterInternal(            HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)            throws ServletException, IOException {        String encoding = getEncoding();        //此处设置请求和响应的编码        if (encoding != null) {            if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {                request.setCharacterEncoding(encoding);            }            if (isForceResponseEncoding()) {                response.setCharacterEncoding(encoding);            }        }        filterChain.doFilter(request, response);    }

OpenSessionInViewFilter

Spring-orm自带的过滤器,它将Hibernate会话绑定到线程,以完成请求的整个处理过程。用于“开放会话视图”模式,即允许在web视图中延迟加载,尽管最初的事务已经完成。原理:让SessionHolder持有session对象,再以sessionFactory为键,sessionHolder为值,放入TransactionSynchronizationManager的类型为ThreadLocal(线程对象的成员变量)的成员变量中,这样就实现了线程绑定,一个请求一个线程,一个请求一个会话session

@Override    protected void doFilterInternal(            HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)            throws ServletException, IOException {        SessionFactory sessionFactory = lookupSessionFactory(request);        boolean participate = false;        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);        String key = getAlreadyFilteredAttributeName();        if (TransactionSynchronizationManager.hasResource(sessionFactory)) {            // Do not modify the Session: just set the participate flag.            participate = true;        }        else {            boolean isFirstRequest = !isAsyncDispatch(request);            if (isFirstRequest || !applySessionBindingInterceptor(asyncManager, key)) {                logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");                Session session = openSession(sessionFactory);                /**                让SessionHolder持有session对象,再以sessionFactory为键,sessionHolder为值,放入                TransactionSynchronizationManager的类型为ThreadLocal(线程对象的成员变量)的成员                变量中,这样就实现了线程绑定,一个请求一个线程,一个请求一个会话session                */                SessionHolder sessionHolder = new SessionHolder(session);                TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);                AsyncRequestInterceptor interceptor = new AsyncRequestInterceptor(sessionFactory, sessionHolder);                asyncManager.registerCallableInterceptor(key, interceptor);                asyncManager.registerDeferredResultInterceptor(key, interceptor);            }        }        try {            filterChain.doFilter(request, response);        }        finally {            if (!participate) {                SessionHolder sessionHolder =                        (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);                if (!isAsyncStarted(request)) {                    logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");                    SessionFactoryUtils.closeSession(sessionHolder.getSession());                }            }        }    }

DelegatingFilterProxy

Spring的过滤器的委派代理类,本质是一种责任链模式的链式调用

这里写图片描述

XssFilter

防止XSS攻击,将请求包装成XssHttpServletRequestWrapper

package com.jeecms.common.web;import java.io.IOException;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.http.HttpServletRequest;import org.apache.commons.lang.StringUtils;import com.jeecms.core.web.util.URLHelper;/** * 请求包装过滤器(作用:防止脚本攻击) * 映射的请求:*.jspx *.jhtml *.html *.jsp * LiuChengxiang * @time 2017年11月13日上午10:57:51 * */public class XssFilter implements Filter {    private String excludeUrls;    FilterConfig filterConfig = null;    public void init(FilterConfig filterConfig) throws ServletException {        this.excludeUrls=filterConfig.getInitParameter("excludeUrls");        this.filterConfig = filterConfig;    }    public void destroy() {        this.filterConfig = null;    }    public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {        if(isExcludeUrl(request)){            chain.doFilter(request, response);        }else{            //除了/member,/flow_statistic,/search,/api开头的请求,其余请求都需要进行请求包装            chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);        }    }    private boolean isExcludeUrl(ServletRequest request){        boolean exclude=false;        if(StringUtils.isNotBlank(excludeUrls)){             String[] excludeUrl = excludeUrls.split("@");             if(excludeUrl!=null&&excludeUrl.length>0){                 for(String url:excludeUrl){                     if(URLHelper.getURI((HttpServletRequest)request).startsWith(url)){                         exclude=true;                     }                 }             }        }        return exclude;    }}

我们来看一下JEECMS这个防攻击是怎么做的

这里写图片描述

ResourceFilter

指定资源访问过滤器

package com.jeecms.common.web;import java.io.IOException;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.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.jeecms.common.util.StrUtils;/** * 资源过滤器 * 映射的路径:/wenku/* * LiuChengxiang * @time 2017年11月21日下午2:54:48 * */public class ResourceFilter implements Filter {    protected final Logger log = LoggerFactory.getLogger(ResourceFilter.class);    public void destroy() {}    @SuppressWarnings("static-access")    public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        String uri=request.getRequestURI();        String suffix=StrUtils.getSuffix(uri);        //wenku目录下只运行直接访问的资源只有swf和pdf        //swf为v8之前版本的格式 pdf为v8的版本格式        if(!suffix.equals("swf")&&!suffix.equals("pdf")){            response.sendError(response.SC_FORBIDDEN);        }else{            chain.doFilter(request, response);        }    }    public void init(FilterConfig arg0) throws ServletException {}}

三.拦截器细化

AdminContextInterceptor

CMS后台上下文信息拦截器,主要做了两件事:1.获取当前用户放入当前请求和当前线程 2.获取访问的站点放入当前线程

<bean id="adminContextInterceptor" class="com.jeecms.cms.web.AdminContextInterceptor">        <!--<property name="adminId" value="1"/>-->        <property name="auth" value="true"/>        <property name="excludeUrls">            <list>                <!-- 不拦截登录,登出 -->                <value>/login.do</value>                <value>/logout.do</value>            </list>        </property>    </bean>
@Override    public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {        // 获得用户        CmsUser user = null;        Subject subject = SecurityUtils.getSubject();        if (subject.isAuthenticated()) {            String username =  (String) subject.getPrincipal();            user = cmsUserMng.findByUsername(username);        }        // 将用户信息放入当前请求        CmsUtils.setUser(request, user);        // 将用户信息与当前线程绑定        CmsThreadVariable.setUser(user);        // 获得站点        CmsSite oldSite=getByCookie(request);        CmsSite site = getSite(user,request, response);        CmsUtils.setSite(request, site);        // 将站点信息与当前线程绑定        CmsThreadVariable.setSite(site);        String uri = getURI(request);        if (exclude(uri)) {            return true;        }        //切换站点移除shiro缓存        if(oldSite!=null&&!oldSite.equals(site)&&user!=null){            authorizingRealm.removeUserAuthorizationInfoCache(user.getUsername().toString());        }        //没有该站管理权限(则切换站点?)        if(site!=null&&user!=null&&user.getUserSite(site.getId())==null){            Set<CmsUserSite>userSites=user.getUserSites();            if(userSites!=null&&userSites.size()>0){                 CmsSite s= userSites.iterator().next().getSite();                 authorizingRealm.removeUserAuthorizationInfoCache(user.getUsername().toString());                 CmsUtils.setSite(request, s);                 CmsThreadVariable.setSite(s);                 response.sendRedirect(s.getAdminUrl());            }        }        return true;    }

AdminLocaleInterceptor

后台本地化信息拦截器

这里写图片描述

FireWallInterceptor

网站防火墙拦截器
这里写图片描述

FrontContextInterceptor

CMS前台上下文信息拦截器

package com.jeecms.cms.web;import java.io.IOException;import java.util.List;import javax.servlet.ServletException;import javax.servlet.http.*;import org.apache.commons.lang.StringUtils;import org.apache.shiro.SecurityUtils;import org.apache.shiro.subject.Subject;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import com.jeecms.common.util.CheckMobile;import com.jeecms.common.web.CookieUtils;import com.jeecms.common.web.session.SessionProvider;import com.jeecms.core.entity.*;import com.jeecms.core.manager.*;import com.jeecms.core.web.util.CmsUtils;public class FrontContextInterceptor extends HandlerInterceptorAdapter {    public static final String SITE_COOKIE = "_site_id_cookie";    /**     * 1.获取所有站点==>2.获取当前站点==>为当前站点设置cookie==>将当前请求的站点信息存入request中     *   ==>将当前请求的站点信息存入当前线程对象中==>当前登录的用户信息存入request中==>     *   ==>当前登录的用户信息存入当前线程对象中==>获取请求来源(pc/phone)     * @author LiuChengxiang     * @time 2017年11月21日下午4:39:35     * @param request     * @param response     * @param handler     * @return     * @throws ServletException     */    @Override    public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler)throws ServletException {        CmsSite site = null;        //获取数据库中的所有站点        List<CmsSite> list = cmsSiteMng.getListFromCache();        int size = list.size();        if (size == 0) {            throw new RuntimeException("no site record in database!");        } else if (size == 1) {            site = list.get(0);        } else {            String server = request.getServerName();            String alias, redirect;            for (CmsSite s : list) {                // 检查域名                if (s.getDomain().equals(server)) {                    site = s;                    break;                }                // 检查域名别名                alias = s.getDomainAlias();                if (!StringUtils.isBlank(alias)) {                    for (String a : StringUtils.split(alias, ',')) {                        if (a.equals(server)) {                            site = s;                            break;                        }                    }                }                // 检查重定向                redirect = s.getDomainRedirect();                if (!StringUtils.isBlank(redirect)) {                    for (String r : StringUtils.split(redirect, ',')) {                        if (r.equals(server)) {                            try {                                response.sendRedirect(s.getUrl());                            } catch (IOException e) {                                throw new RuntimeException(e);                            }                            return false;                        }                    }                }            }            if (site == null) {                throw new SiteNotFoundException(server);            }        }        if(site!=null){            //设置对应站点的cookie            CookieUtils.addCookie(request, response, SITE_COOKIE, site.getId().toString(), null, null);        }        //将当前请求的站点信息存入request中        CmsUtils.setSite(request, site);        //将当前请求的站点信息存入当前线程对象的局部变量ThreadLocal.ThreadLocalMap中        CmsThreadVariable.setSite(site);        Subject subject = SecurityUtils.getSubject();        CmsUser user =null;        if (subject.isAuthenticated()|| subject.isRemembered()) {            String username =  (String) subject.getPrincipal();            user= cmsUserMng.findByUsername(username);            CmsUtils.setUser(request, user);            // Site加入线程变量            CmsThreadVariable.setUser(user);        }        checkEquipment(request, response);        return true;    }    /**     * 检查访问方式     */    public void checkEquipment(HttpServletRequest request,HttpServletResponse response){        String ua=(String) session.getAttribute(request,"ua");        if(null==ua){            try{                String userAgent = request.getHeader( "USER-AGENT" ).toLowerCase();                if(null == userAgent){                      userAgent = "";                  }                if(CheckMobile.check(userAgent)){                    ua="mobile";                } else {                    ua="pc";                }                session.setAttribute(request, response, "ua",ua);            }catch(Exception e){}        }        if(StringUtils.isNotBlank((ua) )){            request.setAttribute("ua", ua);        }    }}

FrontLocaleInterceptor

前台本地化信息拦截器(与后台的处理方式一致,可参考AdminLocaleInterceptor )

四.话外音

如有错误的地方,欢迎在文章下方留言批评,博主会第一时间回复。欢迎大家与博主交流,共同进步。

系列章节链接直达

JEECMS——前言
JEECMS——源码下载及安转运行
JEECMS——项目包结构
JEECMS——web.xml配置
JEECMS——安全框架Shiro

原创粉丝点击