Servlet Filter
一、先说说什么是Servlet Filter。
Filter是Servlet 中广泛使用到的技术,通过它可以对服务器管理的所有web资源进行拦截,从而实现一些特殊的功能,例如用户权限控制、过滤敏感关键词、统一设置格式编码等。
Servlet Filter 又称 为 Servlet 过滤器,Filter不是Servlet,不能直接进行访问,它本身不能生成request对象和response对象,它只能对web资源提供以下的过滤功能:
- 在 Web 资源被访问前,检查 request 对象,修改请求头和请求正文,或对请求进行预处理操作。
- 将请求传递到下一个过滤器或目标资源。
- 在 Web 资源被访问后,检查 response 对象,修改响应头和响应正文。
总而言之,Filter要做的一件事就是,拦截、拦截、拦截,在这个基础之上就能完成一些来自用户敏感操作的处理。
二、再来看看Filter中接口。
开发过滤器要实现 javax.servlet.Filter 接口,并提供一个公开的不带参的构造方法。在 Filter 接口中,定义了 3 个方法
返回值类型 | 方法 | 功能描述 |
---|---|---|
void | init (FilterConfig filterConfig) | 用于户过滤器初始化 |
void | doFilter(ServletRequest request,SeivletResponse response, FilterChain chain) | 用于对用户请求进行拦截。 参数request 和 response表示请求和响应对象。 参数 chain 代表当前 Filter 链对象,在该方法内部,调用 chain.doFilter() 方法,才能把请求交付给 Filter 链中的下一个 Filter 或者 Web 资源。 |
void | destroy() | 销毁 Filter 对象之前被调用,用于释放被 Filter 对象占用的资源。 |
三、接下来说说在最近实战中遇到的问题。
在做一个Servlet小项目的时候,由于要对整个站点用户登录状态进行一个判断,所以我就采用了Servlet中的Filter对整个站点进行了一个拦截,但是我发现对所有资源进行拦截过后我甚至连静态资源都不能进行访问。所以当下要做的就是如何放行资源,怎么在Filter中进行一个资源的放行。
于是我就在网上翻阅了一下资料,最后看到一篇文章SpringBoot 中Filter的作用以及使用 - 灰信网(软件开发博客聚合) (freesion.com),这里也就相当于是做一个学习记录了。
四、最后再来说说如何去实现拦截请求的一个放行。
1. 在配置拦截器时设置默认参数(web.xml)。
这里也可以说是需要放行的接口。
<!-- 用户登录拦截 -->
<filter>
<filter-name>UserStatusFilter</filter-name>
<filter-class>com.oa.filter.UserStatusFilter</filter-class>
<init-param>
<param-name>excludeFilter</param-name>
<param-value>/user/captcha,/views/api/,/assets/,/user,/role,/dept,/leave,/travel,/note,/notice,/car,/meeting,/stamp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>UserStatusFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
上面是第一种方式,第二种方式:
//initParams 指定一组过滤器初始化参数
@WebFilter(initParams = {@WebInitParam(name = "excludeFilter",value = "/user/captcha,/views/api/,/assets/,/user,/role,/dept,/leave,/travel,/note,/notice,/car,/meeting,/stamp")})
public class UserStatusFilter implements Filter {
……省略一w行
}
以上两种方式相同,一种通过注解设置,一种通过xml配置文件设置。
对应关系:@WebInitParam(name = "nofilter",value = "/one,/two") 中 name 对应方式 init-param 标签中的param-name,value 对应init-param 标签中的param-value
2. Filter中加载需要放行的接口。
/**
* 不过滤的资源
*/
private String[] noFilter;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//将不过滤的资源存入数组中
String notifierString = filterConfig.getInitParameter("excludeFilter");
if (notifierString!=null && notifierString.length()>0){
noFilter = notifierString.split(",");
}
System.out.println(Arrays.toString(noFilter));
}
我们从Filter 取出默认配置的 放行过滤请求,并将其塞入数组noFilter之中,在进行用户请求拦截时在进行进一步处理。
3. 判断请求资源是否是放行资源
/**
*isNofilter 判断路径是否不需要过滤
*/
public boolean isNofilter(HttpServletRequest request){
String requestURI = request.getRequestURI();
for (String s : noFilter) {
System.out.println(requestURI + ":" + s + "====" + requestURI.contains(s));
if (requestURI.contains(s)){
return true;
}
}
return false;
}
通过request请求域的getRequestURI()方法获取请求行中的资源名称部分,即位于 URL 的主机和端口之后,参数部分之前的部分。最后通过遍历noFilter数组,判断请求的资源名称是否包含又放行路径,如果有返回true如果没有返回false;
4. 最后在dofilter进行放行或者拦截操作。
boolean flag = isNofilter(req);
if (flag){
filterChain.doFilter(servletRequest,servletResponse);
}else {
//如果没有拦截执行以下操作……
此处省略一w行
}
5. 完整代码
package com.oa.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.oa.common.R;
import com.oa.service.Impl.UserServiceImpl;
import com.oa.service.UserService;
import com.oa.utils.AESUtil;
import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Arrays;
/**
* 作者: Juran on 2022-04-26 23:25
* 作者博客:iit.la
*/
public class UserStatusFilter implements Filter {
/**
* 不过滤的资源
*/
private String[] noFilter;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//将不过滤的资源存入数组中
String notifierString = filterConfig.getInitParameter("excludeFilter");
if (notifierString!=null && notifierString.length()>0){
noFilter = notifierString.split(",");
}
System.out.println(Arrays.toString(noFilter));
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//向下转型
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse rep = (HttpServletResponse) servletResponse;
boolean flag = isNofilter(req);
if (flag){
filterChain.doFilter(servletRequest,servletResponse);
}else {
//如果没有拦截执行以下操作……
此处省略一w行
}
}
@Override
public void destroy() {
}
/**
*isNofilter 判断路径是否不需要过滤
*/
public boolean isNofilter(HttpServletRequest request){
String requestURI = request.getRequestURI();
for (String s : noFilter) {
System.out.println(requestURI + ":" + s + "====" + requestURI.contains(s));
if (requestURI.contains(s)){
return true;
}
}
return false;
}
}
ok基本上就这些了。
虽不能日行千里路,但能在这一日之中,静下心来做让自己满意的事,那今天的生活多加一分。