JEECMS——过滤器和拦截器
来源:互联网 发布:windows优化软件知乎 编辑:程序博客网 时间:2024/06/08 20:20
写在前面
如果图片显示不清楚的,可以右键图片,选择在新标签页中打开图片
注:过滤器和拦截器是项目的关键部分,因为每一次请求都会经过不同的过滤器和拦截器,进行一系列的包装,判断,过滤等等,只有先了解了这个部分,知道了请求过来时JEECMS都事先做了哪些事,然后再去深入项目中的功能实现,会更加快速便捷。
一.概况
JEECMS配置的过滤器
注:其配置位置在web.xml中,如果对项目入口配置文件还不太熟悉,可以先看看 JAVA开源CMS内容管理系统JEECMS—-web.xml配置
JEECMS配置的主要的拦截器
前台拦截器配置(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
- JEECMS——过滤器和拦截器
- 过滤器和拦截器
- 拦截器和过滤器
- 拦截器和过滤器
- 拦截器和过滤器
- 过滤器和拦截器
- 过滤器和拦截器
- 过滤器和拦截器
- 拦截器和过滤器
- 过滤器和拦截器
- 过滤器和拦截器
- 拦截器和过滤器
- 过滤器和拦截器
- 拦截器和过滤器
- 过滤器和拦截器
- 过滤器和拦截器
- 过滤器和拦截器
- 过滤器和拦截器异同
- LaTeX如何正确输入引号:双引号“”单引号‘’
- 回调函数的理解
- JDBC_02
- C# 编辑器怎么配置最好
- 数据结构---二叉树的基本运算
- JEECMS——过滤器和拦截器
- 最近常用的几个命令 记录一下
- 顶象加固分析和一点还原
- Android用MediaRecorder实现MPEG4视频监控
- bzoj 4568: [Scoi2016]幸运数字(树上倍增+线性基)
- 在windows下安装windows+Ubuntu16.04双系统(下)
- C++获取文件版本信息
- UE4_C++使Actor属性出现在编辑器中
- cloudera cdh 5.11 编译 启动spark thrift server spark sql