javaweb 中的 filter 详解
1. filter 基本概念
1.1 什么是 filter
filter(过滤器)是 javaweb 中的一种组件,用于在请求到达 servlet 之前或响应发送到客户端之前对请求和响应进行预处理和后处理。
1.2 filter 的作用
- 请求预处理:在请求到达 servlet 之前进行处理
- 响应后处理:在响应发送到客户端之前进行处理
- 链式处理:多个 filter 可以形成处理链
- 动态拦截:根据条件决定是否继续执行
2. filter 的工作原理
2.1 filter 生命周期
public class myfilter implements filter {
// 1. 初始化方法 - 容器启动时调用
@override
public void init(filterconfig filterconfig) throws servletexception {
system.out.println("filter 初始化");
}
// 2. 过滤方法 - 每次请求时调用
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
// 前置处理
system.out.println("请求到达 filter");
// 传递给下一个 filter 或 servlet
chain.dofilter(request, response);
// 后置处理
system.out.println("响应经过 filter");
}
// 3. 销毁方法 - 容器关闭时调用
@override
public void destroy() {
system.out.println("filter 销毁");
}
}2.2 filter 执行流程
客户端请求 → filter1 → filter2 → ... → servlet → filter2 → filter1 → 客户端响应
3. filter 的核心接口和类
3.1 filter 接口
public interface filter {
void init(filterconfig filterconfig);
void dofilter(servletrequest request, servletresponse response,
filterchain chain);
void destroy();
}
3.2 filterconfig 接口
public interface filterconfig {
string getfiltername();
servletcontext getservletcontext();
string getinitparameter(string name);
enumeration<string> getinitparameternames();
}
3.3 filterchain 接口
public interface filterchain {
void dofilter(servletrequest request, servletresponse response);
}
4. filter 的配置方式
4.1 注解配置(推荐)
@webfilter(
filtername = "myfilter",
urlpatterns = {"/*"},
initparams = {
@webinitparam(name = "encoding", value = "utf-8"),
@webinitparam(name = "excludepaths", value = "/static/*")
}
)
public class myfilter implements filter {
// filter 实现
}
4.2 web.xml 配置
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
version="3.1">
<filter>
<filter-name>myfilter</filter-name>
<filter-class>com.example.myfilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myfilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>request</dispatcher>
<dispatcher>forward</dispatcher>
</filter-mapping>
</web-app>5. 常用 filter 实现示例
5.1 字符编码过滤器
@webfilter(
urlpatterns = "/*",
initparams = @webinitparam(name = "encoding", value = "utf-8")
)
public class characterencodingfilter implements filter {
private string encoding = "utf-8";
@override
public void init(filterconfig filterconfig) {
string encodingparam = filterconfig.getinitparameter("encoding");
if (encodingparam != null && !encodingparam.isempty()) {
this.encoding = encodingparam;
}
}
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
// 设置请求编码
request.setcharacterencoding(encoding);
// 设置响应编码
response.setcharacterencoding(encoding);
response.setcontenttype("text/html;charset=" + encoding);
system.out.println("设置字符编码: " + encoding);
chain.dofilter(request, response);
}
@override
public void destroy() {
// 清理资源
}
}5.2 身份认证过滤器
@webfilter(urlpatterns = {"/admin/*", "/user/*"})
public class authenticationfilter implements filter {
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
httpservletrequest httprequest = (httpservletrequest) request;
httpservletresponse httpresponse = (httpservletresponse) response;
httpsession session = httprequest.getsession(false);
// 检查用户是否登录
if (session == null || session.getattribute("user") == null) {
// 未登录,重定向到登录页面
httpresponse.sendredirect(httprequest.getcontextpath() + "/login");
return;
}
// 检查用户权限
user user = (user) session.getattribute("user");
string requesturi = httprequest.getrequesturi();
if (requesturi.contains("/admin/") && !user.isadmin()) {
httpresponse.senderror(httpservletresponse.sc_forbidden, "权限不足");
return;
}
system.out.println("用户 " + user.getusername() + " 访问: " + requesturi);
chain.dofilter(request, response);
}
}5.3 日志记录过滤器
@webfilter("/*")
public class loggingfilter implements filter {
private static final logger logger = loggerfactory.getlogger(loggingfilter.class);
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
httpservletrequest httprequest = (httpservletrequest) request;
long starttime = system.currenttimemillis();
// 记录请求信息
string clientip = getclientip(httprequest);
string requesturi = httprequest.getrequesturi();
string method = httprequest.getmethod();
logger.info("请求开始: {} {} from {}", method, requesturi, clientip);
try {
chain.dofilter(request, response);
} finally {
// 记录响应信息
long endtime = system.currenttimemillis();
long duration = endtime - starttime;
httpservletresponse httpresponse = (httpservletresponse) response;
int status = httpresponse.getstatus();
logger.info("请求完成: {} {} - 状态: {} - 耗时: {}ms",
method, requesturi, status, duration);
}
}
private string getclientip(httpservletrequest request) {
string xforwardedfor = request.getheader("x-forwarded-for");
if (xforwardedfor != null && !xforwardedfor.isempty()) {
return xforwardedfor.split(",")[0];
}
return request.getremoteaddr();
}
}5.4 跨域过滤器 (cors)
@webfilter("/*")
public class corsfilter implements filter {
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
httpservletresponse httpresponse = (httpservletresponse) response;
httpservletrequest httprequest = (httpservletrequest) request;
// 设置 cors 头
httpresponse.setheader("access-control-allow-origin", "*");
httpresponse.setheader("access-control-allow-methods",
"get, post, put, delete, options");
httpresponse.setheader("access-control-allow-headers",
"content-type, authorization, x-requested-with");
httpresponse.setheader("access-control-max-age", "3600");
// 处理预检请求
if ("options".equalsignorecase(httprequest.getmethod())) {
httpresponse.setstatus(httpservletresponse.sc_ok);
return;
}
chain.dofilter(request, response);
}
}5.5 xss 防护过滤器
@webfilter("/*")
public class xssfilter implements filter {
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
// 包装请求对象,对参数进行 xss 过滤
xssrequestwrapper wrappedrequest = new xssrequestwrapper(
(httpservletrequest) request);
chain.dofilter(wrappedrequest, response);
}
}
// xss 请求包装器
class xssrequestwrapper extends httpservletrequestwrapper {
public xssrequestwrapper(httpservletrequest request) {
super(request);
}
@override
public string getparameter(string name) {
string value = super.getparameter(name);
return cleanxss(value);
}
@override
public string[] getparametervalues(string name) {
string[] values = super.getparametervalues(name);
if (values == null) return null;
string[] cleanedvalues = new string[values.length];
for (int i = 0; i < values.length; i++) {
cleanedvalues[i] = cleanxss(values[i]);
}
return cleanedvalues;
}
@override
public string getheader(string name) {
string value = super.getheader(name);
return cleanxss(value);
}
private string cleanxss(string value) {
if (value == null) return null;
// 简单的 xss 过滤
return value.replaceall("<", "<")
.replaceall(">", ">")
.replaceall("\"", """)
.replaceall("'", "'")
.replaceall("/", "/")
.replaceall("\\(", "(")
.replaceall("\\)", ")")
.replaceall("script", "scr_ipt")
.replaceall("javascript", "java_script");
}
}6. filter 的配置参数详解
6.1 url 模式匹配
@webfilter(
urlpatterns = {
"*.html", // 所有 html 文件
"/api/*", // /api 路径下的所有请求
"/admin/*", // /admin 路径下的所有请求
"/*" // 所有请求
}
)6.2 dispatcher 类型
@webfilter(
urlpatterns = "/*",
dispatchertypes = {
dispatchertype.request, // 直接请求
dispatchertype.forward, // 转发请求
dispatchertype.include, // 包含请求
dispatchertype.error, // 错误处理
dispatchertype.async // 异步请求
}
)6.3 初始化参数
@webfilter(
urlpatterns = "/*",
initparams = {
@webinitparam(name = "excludepaths", value = "/static/,/public/"),
@webinitparam(name = "maxfilesize", value = "10485760"), // 10mb
@webinitparam(name = "allowedtypes", value = "jpg,png,pdf")
}
)
7. filter 的执行顺序
7.1 执行顺序规则
- web.xml 配置顺序:按照配置文件中定义的顺序执行
- 注解配置顺序:使用
@webfilter的filtername字母顺序 - 混合配置:web.xml 优先于注解
7.2 控制执行顺序的方法
// 方法1: 使用 web.xml 明确指定顺序
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
// 方法2: 使用 @order 注解(需要框架支持)
@order(1)
@webfilter("/*")
public class firstfilter implements filter { }
@order(2)
@webfilter("/*")
public class secondfilter implements filter { }8. filter 的高级用法
8.1 条件过滤
@webfilter("/*")
public class conditionalfilter implements filter {
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
httpservletrequest httprequest = (httpservletrequest) request;
string requesturi = httprequest.getrequesturi();
// 排除静态资源
if (requesturi.startswith("/static/") ||
requesturi.endswith(".css") ||
requesturi.endswith(".js")) {
chain.dofilter(request, response);
return;
}
// 对动态请求进行特殊处理
system.out.println("处理动态请求: " + requesturi);
// 添加自定义请求属性
httprequest.setattribute("processingtime", system.currenttimemillis());
chain.dofilter(request, response);
// 后处理
long starttime = (long) httprequest.getattribute("processingtime");
if (starttime != null) {
long duration = system.currenttimemillis() - starttime;
system.out.println("请求处理耗时: " + duration + "ms");
}
}
}8.2 响应包装器
@webfilter("/*")
public class compressionfilter implements filter {
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
httpservletrequest httprequest = (httpservletrequest) request;
httpservletresponse httpresponse = (httpservletresponse) response;
string acceptencoding = httprequest.getheader("accept-encoding");
if (acceptencoding != null && acceptencoding.contains("gzip")) {
// 使用 gzip 压缩响应
compressionresponsewrapper wrappedresponse =
new compressionresponsewrapper(httpresponse);
chain.dofilter(request, wrappedresponse);
// 完成压缩并发送响应
wrappedresponse.finish();
} else {
chain.dofilter(request, response);
}
}
}
// 压缩响应包装器
class compressionresponsewrapper extends httpservletresponsewrapper {
private gzipservletoutputstream gzipstream;
private printwriter printwriter;
public compressionresponsewrapper(httpservletresponse response)
throws ioexception {
super(response);
}
@override
public servletoutputstream getoutputstream() throws ioexception {
if (printwriter != null) {
throw new illegalstateexception("getwriter() has already been called");
}
if (gzipstream == null) {
gzipstream = new gzipservletoutputstream(super.getoutputstream());
}
return gzipstream;
}
@override
public printwriter getwriter() throws ioexception {
if (gzipstream != null) {
throw new illegalstateexception("getoutputstream() has already been called");
}
if (printwriter == null) {
gzipstream = new gzipservletoutputstream(super.getoutputstream());
printwriter = new printwriter(new outputstreamwriter(gzipstream,
getcharacterencoding()));
}
return printwriter;
}
public void finish() throws ioexception {
if (printwriter != null) {
printwriter.close();
}
if (gzipstream != null) {
gzipstream.finish();
}
}
}9. filter 的最佳实践
9.1 性能考虑
@webfilter("/*")
public class performancefilter implements filter {
private boolean enableprofiling;
@override
public void init(filterconfig filterconfig) {
// 从配置中读取性能开关
string profiling = filterconfig.getinitparameter("enableprofiling");
this.enableprofiling = "true".equalsignorecase(profiling);
}
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
if (!enableprofiling) {
chain.dofilter(request, response);
return;
}
long starttime = system.nanotime();
try {
chain.dofilter(request, response);
} finally {
long endtime = system.nanotime();
long duration = (endtime - starttime) / 1_000_000; // 转换为毫秒
if (duration > 1000) { // 超过1秒的记录警告
system.err.println("慢请求: " + duration + "ms");
}
}
}
}9.2 异常处理
@webfilter("/*")
public class exceptionhandlingfilter implements filter {
@override
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception {
try {
chain.dofilter(request, response);
} catch (exception e) {
// 统一异常处理
handleexception(e, request, response);
}
}
private void handleexception(exception e, servletrequest request,
servletresponse response) throws ioexception {
httpservletrequest httprequest = (httpservletrequest) request;
httpservletresponse httpresponse = (httpservletresponse) response;
// 记录异常日志
system.err.println("处理请求时发生异常: " + httprequest.getrequesturi());
e.printstacktrace();
// 返回统一的错误响应
httpresponse.setstatus(httpservletresponse.sc_internal_server_error);
httpresponse.setcontenttype("application/json");
string errorjson = "{\"error\": \"服务器内部错误\", \"code\": 500}";
httpresponse.getwriter().write(errorjson);
}
}10. filter 的测试
10.1 单元测试示例
public class authenticationfiltertest {
@test
public void testdofilter_authenticateduser() throws exception {
// 创建模拟对象
httpservletrequest mockrequest = mock(httpservletrequest.class);
httpservletresponse mockresponse = mock(httpservletresponse.class);
filterchain mockchain = mock(filterchain.class);
httpsession mocksession = mock(httpsession.class);
// 设置模拟行为
when(mockrequest.getsession(false)).thenreturn(mocksession);
when(mocksession.getattribute("user")).thenreturn(new user("testuser"));
when(mockrequest.getrequesturi()).thenreturn("/user/profile");
// 执行测试
authenticationfilter filter = new authenticationfilter();
filter.dofilter(mockrequest, mockresponse, mockchain);
// 验证结果
verify(mockchain).dofilter(mockrequest, mockresponse);
}
@test
public void testdofilter_unauthenticateduser() throws exception {
// 创建模拟对象
httpservletrequest mockrequest = mock(httpservletrequest.class);
httpservletresponse mockresponse = mock(httpservletresponse.class);
filterchain mockchain = mock(filterchain.class);
// 设置模拟行为
when(mockrequest.getsession(false)).thenreturn(null);
when(mockrequest.getcontextpath()).thenreturn("/myapp");
// 执行测试
authenticationfilter filter = new authenticationfilter();
filter.dofilter(mockrequest, mockresponse, mockchain);
// 验证重定向
verify(mockresponse).sendredirect("/myapp/login");
verify(mockchain, never()).dofilter(mockrequest, mockresponse);
}
}总结
filter 是 javaweb 中非常重要的组件,它提供了以下核心功能:
- 请求预处理和响应后处理
- 链式处理机制
- 灵活的配置方式
- 强大的拦截能力
通过合理使用 filter,可以实现:
- 统一的字符编码处理
- 身份认证和授权
- 请求日志记录
- 性能监控
- 安全防护
- 跨域处理
- 数据压缩等
filter 的设计遵循了 aop(面向切面编程)的思想,使得横切关注点能够与业务逻辑分离,提高了代码的可维护性和复用性。
到此这篇关于javaweb 中的 filter 详解的文章就介绍到这了,更多相关javaweb filter内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论