Filter
概念
- Filter过滤器,时Javaweb三大组件之一(Servlet、Filter、Listener)。
- 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
- 过滤器一般完成一些通用的操作,比如:权限控制、统一编码处理、敏感字符处理等等。
使用方法
- 定义类,并实现Filter接口
public class MyFilter implements Filter {
public void init(FilterConfig filterConfig){}
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain){}
public void destroy(){}
}
- 配置Filter拦截资源的路径:在类上定义
@WebFilter("/路径")
注解 - 在doFilter方法中写入需求代码并放行
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain) {
//在此处添加需求代码
System.out.println("Hello Filter ! ")
//放行
chain.doFilter(request,response);
}
执行流程
- 放行后访问对应资源,资源访问完成后,会回到Filter中
- 并且从放行后的位置开始执行逻辑
所以一般我们
在放行前的位置,对request数据进行处理
在放行后的位置,对response数据进行处理
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain) {
//在此处添加对request数据进行处理
//放行
chain.doFilter(request,response);
//在此处对response数据进行处理
}
使用细节
拦截路径的配置
Filter可以根据需求,写多个拦截器,分别配置不同的拦截资源路径
@WebFilter("/*")
拦截方式 | 路径 | 说明 |
---|---|---|
拦截具体的资源 | /index.jsp | 只有访问index.jsp时才会被拦截 |
目录拦截 | /user/* | 访问/user下的所有资源,都会被拦截 |
后缀名拦截 | *.jsp | 访问后缀名为jsp的资源,都会被拦截 |
拦截所有 | /* | 访问所有资源,都会被拦截 |
过滤器链
一个Web应用,可以配置多个过滤器,这些过滤器被称为过滤器链
以两个Filter举例
客户端发送请求,首先被Filter1拦截
,放行后会被Filter2拦截
,Filter2也放行后,客户端会访问到服务端。并且服务端会返回响应
,响应首先会经过Filter2的放行后逻辑
,然后到Filter1的放行后逻辑
,最终返回到客户端
注:注解配置的Filter,优先级按照过滤器类名的自然排序
扫描式实现过滤器
filter有两种实现方式:
- 使用原始的声明过滤器(现在很少使用)通过web.xml进行配置
- 使用扫描式,通过注解来实现。
此处直接使用扫描式来实现
创建一共自定义拦截器类,并且实现Filter接口,
import jakarta.servlet.*;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@WebFilter(filterName = 'public',urlPatterns = '/*')
public class filter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
//设置请求和响应的编码
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
//处理跨域请求
httpServletResponse.setHeader("Access-control-Allow-Origin",httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader ("Access-Control-Allow-Methods","GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-control-Allow-Headers",httpServletRequest.getHeader("Access-control-Request-Headers"));
//跨域时首先发送一共option请求,这里直接给他返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
chain.doFilter(request,response);
}
chain.doFilter(request,response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
我们需要将他加载到Spring容器中,所以需要先将他声明为Bean
然后在SpringMvcConfig中添加这个过滤器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private filter filter;
@Bean
public FilterRegistrationBean<Filter> someFilterRegistration() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>(); //这里添加过滤器
registration.setFilter(filter);
//过滤的URL
registration.addUrlPatterns("/*");
//过滤器的名称
registration.setName("registrationFilter");
//过滤器的优先级
registration.setOrder(99);
}
}
到此扫描式过滤器就完成了
案例:登录验证
分析:
访问我们服务端所有的资源,大多数需要进行登录验证,所以我们创建的Filter拦截器的拦截路径直接写成@WebFilter("/*")
判断是否登录,前面我们学习了会话跟踪技术,此处我们使用Session
实现
- 首先按照上面模板创建一个Filter类
/**
*登录验证的过滤器
*/
@WebFilter("/*")
public class LoginFilter implements Filter {
public void init(FilterConfig filterConfig){}
public void destroy(){}
@Overrride
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain){}
}
- 按照分析中的方法,在放行前检查Session中是否有对象
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain) throws Session{
HttpServletRequest req = (HttpServletRequest) request;
//判断session中是否有user
HttpSession session = req.getSession();
Object user = session.getAttribute("user");
if(user != null){
//不为空,已登录,放行
chain.doFilter(request,response);
}else{
//为空,没有登录,跳转到登录页面
req.setAttribute("login_msg","您尚未登录");
req.getRequestDispatcher("/login.jsp").forward(req,response);
}
}
- 优化
我们测试发现CSS样式文件以及注册等不需要登录验证的也被拦截了,所以我们需要在此基础上进行优化
在登录验证之前,检测是否是必须登录的资源,选择放行
我们可以直接将需要放行的文件写在urls的数组中
//判断访问资源路径是否和登录注册有关
String[] urls = {"/login.jsp","/imgs","/css/","loginServlet","register"};
//获取当前访问的资源路径
String url = req.getRequestURL().toString();
//循环遍历,查看请求路径是否为以上需要放行的路径
for(String u : urls){
if(url.contains(u)){
chain.doFilter(request,response);
//结束方法
return;
}
}