Filter过滤器原理和登录实现

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

Filter过滤器API

Servlet过滤器API包含了3个接口,它们都在javax.servlet包中,分别是Filter接口、FilterChain接口和FilterConfig接口。

Filter接口(源码)

publicinterfaceFilter {publicvoidinit(FilterConfig filterConfig)throwsServletException;publicvoiddoFilter(ServletRequest request, ServletResponse response, FilterChain chain)throwsIOException, ServletException;publicvoiddestroy();

}

所有的过滤器都必须实现Filter接口。该接口定义了init,doFilter0,destory()三个方法:

(1)init(FilterConfig filterConfig)

在web应用程序启动时,web服务器将根据 web.xml文件中的配置信息来创建每个注册的Filter实例对象,并将其保存在服务器的内存中。Web容器创建Filter对象实例后,将立即调用该Filter对象的init方法。Init方法在Filter生命周期中仅执行一次,web容器在调用init方法时,会传递一个包含Filter的配置和运行环境的FilterConfig对象(FilterConfig的用法和ServletConfig类似)。利用FilterConfig对象可以得到ServletContext对象,以及部署描述符中配置的过滤器的初始化参数。

(2)doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

doFilter()方法类似于Servlet接口的service()方法。当客户端请求目标资源的时候,容器就会调用与这个目标资源相关联的过滤器的 doFilter()方法。其中参数 request, response 为 web 容器或 Filter 链的上一个 Filter 传递过来的请求和相应对象;参数 chain 为代表当前 Filter 链的对象,在特定的操作完成后,可以在当前 Filter 对象的 doFilter 方法内部需要调用 FilterChain 对象的 chain.doFilter(request,response)方法才能把请求交付给 Filter 链中的下一个 Filter 或者目标 Servlet 程序去处理,也可以直接向客户端返回响应信息,或者利用RequestDispatcher的forward()和include()方法,以及 HttpServletResponse的sendRedirect()方法将请求转向到其他资源。这个方法的请求和响应参数的类型是 ServletRequest和ServletResponse,也就是说,过滤器的使用并不依赖于具体的协议。

(3)public void destroy()

在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。

FilterChain接口(源码)

publicinterfaceFilterChain {publicvoiddoFilter(ServletRequest request, ServletResponse response)throwsIOException, ServletException;

}

(1)doFilter(ServletRequest request,ServletResponse response)

此方法是由Servlet容器提供给开发者的,用于对资源请求过滤链的依次调用,通过FilterChain调用过滤链中的下一个过滤 器,如果是最后一个过滤器,则下一个就调用目标资源。

FilterConfig接口(源码) FilterConfig接口检索过滤器名、初始化参数以及活动的Servlet上下文。

publicinterfaceFilterConfig {//返回web.xml部署文件中定义的该过滤器的名称publicString getFilterName();//返回调用者所处的servlet上下文publicServletContext getServletContext();//返回过滤器初始化参数值的字符串形式,当参数不存在时,返回nul1.name是初始化参数名publicString getInitParameter(String name);//以Enumeration形式返回过滤器所有初始化参数值,如果没有初始化参数,返回为空publicEnumeration getInitParameterNames();

}

了解了Filter的基本概念和源码,下面具体使用下Filter过滤器来实现登录过滤。

需求:访问A页面(登录后才能访问的页面)-->未登录-->跳转到登录页面-->登陆成功后,跳转到A页面

自定义HttpFilter

importjava.io.IOException;importjavax.servlet.Filter;importjavax.servlet.FilterChain;importjavax.servlet.FilterConfig;importjavax.servlet.ServletException;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;/*** HttpFilter*/publicabstractclassHttpFilterimplementsFilter{//保存filterConfig对象PRivateFilterConfig filterConfig;/*** 直接返回filterConfig对象

*@return*/publicFilterConfig getFilterConfig() {returnfilterConfig;

}/*** 不建议子类直接覆盖,若直接失败,将可能导致filterConfig成员变量初始化失败*/@Overridepublicvoidinit(FilterConfig filterConfig)throwsServletException {this.filterConfig =filterConfig;

init();

}/*** 供子类继承的初始化方法,刻通过getFilterConfig()方法获得filterConfig对象*/privatevoidinit() {}/*** 原生的doFilter方法,在方法内部把ServletRequest和ServletResponse转化化为了HttpServletRequest和HttpServletResponse,

* 并调用了doFilter(HttpServletRequest request, HttpServletResponse response,FilterChain filterChain)方法*/@OverridepublicvoiddoFilter(ServletRequest req, ServletResponse res,

FilterChain filterChain)throwsIOException, ServletException {

HttpServletRequest request=(HttpServletRequest) req;

HttpServletResponse response=(HttpServletResponse) res;

doFilter(request, response, filterChain);

}/*** 抽象方法,为http请求定制,必须实现的方法

*@paramrequest

*@paramresponse

*@paramfilterChain

*@throwsIOException

*@throwsServletException*/publicabstractvoiddoFilter(HttpServletRequest request, HttpServletResponse response,

FilterChain filterChain)throwsIOException, ServletException;

@Overridepublicvoiddestroy() {}

}

View Code

web.xml配置CommonFilter

<filter>

<filter-name>commonFilter</filter-name>

<filter-class>com.gcx.emall.Filter.CommonFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>commonFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

登录过滤器CommonFilter

importjava.io.IOException;importjavax.servlet.FilterChain;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;publicclassCommonFilterextendsHttpFilter {privatefinalLogger log = LoggerFactory.getLogger(CommonFilter.class);

@OverridepublicvoiddoFilter(HttpServletRequest request,

HttpServletResponse response, FilterChain filterChain)throwsIOException, ServletException {

log.info("==============拦截get请求================");if("GET".equalsIgnoreCase(request.getMethod())) {

RequestUtil.saveRequest(request);

}

String requestUri=request.getRequestURI();

String contextPath=request.getContextPath();

String url=requestUri.substring(contextPath.length());if("/login".equals(url)) {

filterChain.doFilter(request, response);return;

}else{

String username= (String) request.getsession().getAttribute("user");if(username ==null) {

log.info("被拦截:跳转到login页面!");

request.getRequestDispatcher("/page/index1.jsp").forward(request, response);

}elsefilterChain.doFilter(request, response);

}

}

}

RequestUtil 保存、获取request并加密请求页面

publicclassRequestUtil {privatestaticfinalLogger logger = LoggerFactory.getLogger(RequestUtil.class);privatestaticfinalBase64 base64 =newBase64(true);publicstaticfinalString LAST_PAGE = "lastPage";//未登录时访问的页面publicstaticfinalString REDIRECT_HOME = "/";//未登录时跳转到首页publicstaticfinalString LOGIN_HOME = "/index.jsp";//登录成功后进入的页面/*** 保存当前请求*/publicstaticvoidsaveRequest(HttpServletRequest request) {

request.getSession().setAttribute(LAST_PAGE, RequestUtil.hashRequestPage(request));

logger.debug("被拦截的url的sessionID:{}", request.getSession().getId());

logger.debug("save request for {}", request.getRequestURI());

}/*** 加密请求页面

*@paramrequest

*@return*/publicstaticString hashRequestPage(HttpServletRequest request) {

String reqUri=request.getRequestURI();

String query=request.getQueryString();if(query !=null) {

reqUri+= "?" +query;

}

String targetPage=null;try{

targetPage= base64.encodeAsString(reqUri.getBytes("UTF-8"));

}catch(UnsupportedEncodingException ex) {//this does not happen}returntargetPage;

}/*** 取出之前保存的请求

*@return*/publicstaticString retrieveSavedRequest(HttpServletRequest request) {

HttpSession session=request.getSession();if(session ==null) {returnREDIRECT_HOME;

}

String HashedlastPage=(String) session.getAttribute(LAST_PAGE);if(HashedlastPage ==null) {returnLOGIN_HOME;

}else{returnretrieve(HashedlastPage);

}

}/*** 解密请求的页面

*@paramtargetPage

*@return*/publicstaticString retrieve(String targetPage) {byte[] decode =base64.decode(targetPage);try{

String requestUri=newString(decode, "UTF-8");inti = requestUri.indexOf("/", 1);returnrequestUri.substring(i);

}catch(UnsupportedEncodingException ex) {//this does not happenreturnnull;

}

}

}

View Code

LoginCOntroller

@RequestMapping(value = "/hello",method = RequestMethod.GET)

public String testHello( String test) {

log.info("执行了Hello方法!");

return "loginSuccess";

}

@RequestMapping(value = "/login",method =RequestMethod.POST)publicString login(HttpServletRequest request,String userName,String passWord){

log.info("执行了login方法!");

password=DigestUtils.md5Hex(password);

User user=userService.findUser(userName,password);if(user!=null){

request.getSession().setAttribute("userId", user.getId());

request.getSession().setAttribute("user", userName);return"redirect:" + RequestUtil.retrieveSavedRequest(request);//跳转至访问页面}else{

log.info("用户不存在");

request.getSession().setAttribute("message", "用户名不存在,请重新登录");return"index";

}

}

最后需要几个jsp页面login.jsp,index.jsp(首页面,任何人都能访问的),loginSuccess.jsp,还需要在controller中加上一个测试testHello方法用于满足之前说的需求。

注意事项:我们过滤的是所有请求,但对于静态资源CSS,js,image我们应该不拦截,对其放行。我们可以在web.xml中进行指定

<!-- 不拦截静态文件 -->

<servlet-mapping>

<servlet-name>default</servlet-name>

<url-pattern>/js/*</url-pattern>

<url-pattern>/css/*</url-pattern>

<url-pattern>/image/*</url-pattern>

<url-pattern>/fonts/*</url-pattern>

</servlet-mapping>

写在后面:本来想把Filter和SpringMVC的interceptor拦截器一起写总结了,但感觉篇幅有些长打算下篇在介绍。

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航