在进行网站开发时需要对页面输出的信息内容进行过滤,于是编写了一个Filter过滤器由于拦截JSP和
Servlet的请求,并使用响应HttpServletResponseWrapper封装类现实响应数据的捕获。在实际应用过程中发现,在获取响应数
据并进行内容过滤后,要把数据写回response对象响应客户端时:
(1)使用tomcat5容器调用response.getOutputStream()方法即可实现,但调用
requonse.getWriter()方法时,输出二进制数据时(图片等内容无法显示)则出现“getWriter() has already
been called for this response”异常。
(2)使用tomcat6容器调用response.getOutputStream()方法时有中文字符会发
生“java.io.CharConversionException:Not an ISO 8859-1
character:”异常,调用requonse.getWriter()方法时可实现文本字符串数据输出,调用
response.getOutputStream()方法可现实字节流数据的输出。
就上述出现的问题进行分析研究,阅读了tomcat6的源代码发现,在调用response.getOutputStream()方法时会判断是否已调用
了requonse.getWriter()方法;相反在调用requonse.getWriter()方法时会判断是否已调用了
response.getOutputStream()方法。
在tomcat5时并没有出现这个问题,使用response.getOutputStream()方法可现实两种数据输出,只是在使用
requonse.getWriter()时发生异常,而在tomcat6下则必须针对不同的数据类型选择相应输出流,这时为什么呢?仔细阅读
tomcat6源代码没有发现问题的根源,给出的参考时:在一次客户端请求的响应动作中,只能调用一种响应输出方法,要么是getWriter()要么是
getOutputStream(),且如果使用getOutputStream()方法输出字符串格式的数据时,中文无法正常通过将发生
“java.io.CharConversionException:Not an ISO 8859-1
character:”异常,在tomcat5下没有对getOutputStream()方法进行严格控制,中文字符串可正常通过。可见tomcat6
的安全机制比tomcat5要严格,对于字符串格式的数据要求使用getWriter()方法输出响应,如果使用了getOutputStream()方
法输出响应,则对输出的字符串数据进验证,要求高字节必须为0,显然中文是无法通过的。
源代码分析:
//tomcat6中对用getOutputStream()方法的实现
public ServletOutputStream getOutputStream()
throws IOException {
if (usingWriter)
throw new IllegalStateException
(sm.getString("coyoteResponse.getOutputStream.ise"));
usingOutputStream = true;
if (outputStream == null) {
outputStream = new CoyoteOutputStream(outputBuffer);
}
return outputStream;
}
//tomcat6中对用getWriter ()方法的实现
public PrintWriter getWriter()
throws IOException {
if (usingOutputStream)
throw new IllegalStateException
(sm.getString("coyoteResponse.getWriter.ise"));
if (Globals.STRICT_SERVLET_COMPLIANCE) {
setCharacterEncoding(getCharacterEncoding());
}
usingWriter = true;
outputBuffer.checkConverter();
if (writer == null) {
writer = new CoyoteWriter(outputBuffer);
}
return writer;
}
//tomcat6中的ServletOutputStream对象的实现,其中print(String s)方法的实现
public void print(String s) throws IOException {
if (s==null) s="null";
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt (i);
if ((c & 0xff00) != 0) { // high order byte must be zero
String errMsg = lStrings.getString("err.not_iso8859_1");
Object[] errArgs = new Object[1];
errArgs[0] = new Character(c);
errMsg = MessageFormat.format(errMsg, errArgs);
throw new CharConversionException(errMsg);
}
write (c);
}
}
经过上述分析可知tomcat6的安全性提高了两个响应输出流是互锁关系,JSP或Servlet中到底调用了那个方法响应请求?能通过方式知道呢?难道因为提高了安全性就没有办法解决这个问题了吗?
回过头再看看我们的过滤器和封装类的调用关系,在过滤器拦截下请求后,请求将暂时停留在过滤器的处理链中,为了获取响应数据使用了
HttpServletResponseWrapper封装类,目的是将响应的输出写入封装类中,而不是直接写入到
HttpServletResponse对象里返回给客户端,我们要对信息进行过滤,恰恰在获得响应数据后对响应的数据进行内容过滤,然后再送往客户端。
在正常情况下tomcat容器connector处理一次客户端http请求时的处理流程是:创建HttpServletRequest请求、
HttpServletResponse响应和ClientJSPorServlet客户JSP或Servlet对象实例,然后调用service方法完
成处理。加入过滤器Filter和封装类Wrapper后情况有所改变,使调用过程发生了变化。容器在初始化时先创建Filter对象示例,当用客户端请
求时将先转给Filter进行处理(根据过滤器的url-pattern设置拦截那些请求),由Filter过滤器决定滞后的处理流程(或正常处理或中
断、转向等有代码控制);Filter将HttpServletRequest请求和封装后的HttpServletResponseWrapper响应
转给客户JSP或Servlet对象进行处理,处理结束后又回到过滤器,我们在这里加入信息过滤处理过程,然后再将数据写回
HttpServletResponse对象,响应客户端。
由此可见,HttpServletResponseWrapper是一个封装后的“假”响应对象,我们真是使用假的响应对象获取了响应数据,只是在写回数
据调用输出流时发生了问题:在JSP或Servket中调用了getWriter()或getOutputStream()方法时,如果在Wrapper
中现实了(覆盖)这两个方法则调用Wrapper中的方法,如果没有实现,则相当于调用HttpServletResponse中方法;而我们在写回数据
时必须调用getWriter()或getOutputStream()方法。
因此,tomcat6下:
(1)在Wrapper中现实getWriter()方法截获响应数据时,JSP或Servlet调用
getWriter()方法将数据写入Wrapper对象的缓存,处理后再调用HttpServletResponse的getWriter()方法写回
数据正常通过;但调用HttpServletResponse的getOutputStream()时由于数据是字符串流,因此中文无法通过。
(2)在Wrapper中现实getOutputStream
()方法截获响应数据时,JSP或Servlet调用getOutputStream
()方法将数据写入Wrapper对象的缓存,处理后再调用HttpServletResponse的getOutputStream
()方法写回数据正常通过。
如何知道JSP或Servlet对用了那个方法呢?我们在Wrapper中实现getOutputStream()和getWriter()两个方法,并
使用标识变量标识那个方法被调用,在截获响应数据后,判断Wrapper中的那个方法被调用了,以决定何种方式读出响应数据,处理后调用
HttpServletResponse的对应输出流写回数据。
//实现了两种方式的HttpServletResponse封装类
public class FilterResponseWrapper
extends HttpServletResponseWrapper {
/**
* 使用 OutputStream 输出数据
*/
private boolean usingOutputStream=false;
/**
* 使用 PrintWriter 输出数据
*/
private boolean usingWriter=false;
/**
* 输出字节流的对象,用于数据输出
*/
private ByteArrayOutputStream outstream;
/**
* 输出字符串文本的对象,用与JSP页面的输出
*/
private CharArrayWriter output;
/**
* 返回字符串格式数据
* @return String
*/
public String toString() {
return output.toString();
}
/**
* 返回字节数组格式的数据
* @return byte[]
*/
public byte[] toByteArray(){
return outstream.toByteArray();
}
/**
*
* @param response HttpServletResponse
*/
public FilterResponseWrapper(HttpServletResponse response) {
super(response);
output = new CharArrayWriter();
outstream = new ByteArrayOutputStream();
}
/**
*
* @return PrintWriter
*/
public PrintWriter getWriter() {
this.usingWriter=true;
return new PrintWriter(output);
}
/**
*
* @return ServletOutputStream
*/
public ServletOutputStream getOutputStream(){
this.usingOutputStream=true;
return new ByteOutputStream(outstream);
}
/**
*
* @return boolean
*/
public boolean isUsingOutputStream(){
return this.usingOutputStream;
}
/**
*
* @return boolean
*/
public boolean isUsingWriter(){
return this.usingWriter;
}
}
//
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterResponseWrapper wrapper = new FilterResponseWrapper( (HttpServletResponse)
response);
chain.doFilter(request, wrapper);
//使用 PrintWriter 对象输出数据
//进行内容过滤
if(wrapper.isUsingWriter()){
String buffer = word.process(wrapper.toString(),fileName);
PrintWriter output = response.getWriter();
output.print(buffer);
output.flush();
}
//使用 ServletOutputStream 对象输出数据
if(wrapper.isUsingOutputStream()){
ServletOutputStream output = response.getOutputStream();
output.write(wrapper.toByteArray());
output.flush();
}
return;
}
分享到:
相关推荐
**cURL封装类详解** 在Web开发中,cURL库是一个强大的工具,它允许开发者通过命令行接口或者在编程语言中进行HTTP和其他网络协议的传输。对于PHP开发者来说,使用cURL封装类能够极大地简化与远程API交互的过程,...
通常,`filter`可能是对过滤器功能的进一步封装或抽象,而`manager`可能是一个管理多个过滤器的类,负责它们的注册、排序和执行。 在过滤器中,有时我们需要访问原始的请求对象,以获取未被过滤的信息。例如,用户...
在Windows的任务管理器中,你可以看到`wrapper.exe`和`java.exe`两个进程,前者是Wrapper,后者是实际运行的Java应用程序。 总的来说,Java Service Wrapper提供了一种简单、可靠的方式来在各种操作系统上部署和...
MyBatisPlus条件构造器 -Wrapper详解(为知笔记版,可用网页打开),详解wrapper条件构造器的各种使用方法及其扩展类的使用方法。
4. **调试与日志**: `Wrapper`支持日志记录,可以通过配置`wrapper.logfile`和`wrapper.console`来指定日志输出位置,这对于调试和监控服务状态非常有用。 5. **更新服务**: 如果需要更新Java程序或修改配置,只需...
- `wrapper.java.classpath`:设定类路径。 - `wrapper.java.library.path`:指明Wrapper库文件位置。 - `wrapper.java.mainclass`:指定主类。 - `wrapper.app.parameter`:传递给应用的参数。 - `wrapper.nt...
过滤器(Filter)和包装器(Wrapper)是解决此类问题的有效工具。本文将深入探讨如何使用JAVA Web的过滤器和包装器来消除乱码问题。 首先,我们来看过滤器(Filter)。在JAVA Web中,过滤器是Servlet规范的一部分,...
- 头文件中的使用说明可能包含了如何实例化封装类,如何调用成员方法,以及如何处理返回的Recordset对象等信息。例如,如何打开连接: ```cpp MyADOCPPWrapper wrapper; wrapper.OpenConnection("DSN=...
最后,`odbcconn.doc`文件应该是关于这个封装类的文档,详细解释了如何使用这些API,可能包括以下内容: 1. **安装和配置ODBC数据源**:用户需要在操作系统中设置ODBC数据源,包括驱动选择和连接信息。 2. **实例...
windows 关于qt的数据库操作封装类 以及用例: SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它的功能特点有: 1. ACID事务 2. 零配置 – 无需安装和管理配置 3....
MyBatis-Plus的QueryWrapper是其强大的查询构造器,为开发者提供了便利的API来构建复杂的SQL查询。在Spring Boot项目中,MyBatis-Plus作为一个扩展MyBatis的轻量级框架,大大简化了数据库操作。下面我们将深入探讨...
标题中的"Wrapper部署Linux跟Windows"指的是在操作系统层面(Linux和Windows)使用Wrapper技术来封装应用程序,以便在不同环境中统一管理和运行。Wrapper通常是一个小型的控制程序,它负责调用和管理目标应用,同时...
下面是 Java Service Wrapper 使用总结的详细说明: 概述 Java Service Wrapper 是一个开源的项目,旨在将 Java 应用程序转换为一个可安装的服务。它提供了一种简单的方式来将 Java 应用程序打包成一个独立的服务...
Wrapper技术是将Java程序封装到一个可执行的系统服务或守护进程中,使其能够更方便地管理、监控和自动化启动。 首先,"wrapper-linux-x86-64-3.5.55"这个版本表明它是专为64位Linux操作系统设计的,兼容x86_64架构...
总结来说,这个压缩包提供了处理Web应用中乱码问题的解决方案,包括了自定义HttpServletRequestWrapper来正确解码GET参数,以及通过字符编码过滤器统一设置请求和响应的编码。这样的做法可以有效避免由于编码不一致...
Wrapper配置是将Java应用程序集成到操作系统服务中的技术,主要用于...以上就是关于"wrapper配置"的基本知识和步骤,通过熟练掌握这些内容,你可以有效地将Java应用程序集成到操作系统服务中,实现自启动和高效管理。
当 SQLite 引擎返回错误时,`KSQLite` 类会捕获这些错误,并通过 `lastError()` 方法提供错误信息,帮助开发者调试和解决问题。 在实际使用 Kompex SQLite Wrapper 时,开发者需要注意内存管理和线程安全。由于 ...
例如,设置`wrapper.java.mainclass`属性为你的主类,`wrapper.java.classpath`属性为你的类路径。 2. **添加启动参数**: 如果你的Java应用需要特定的启动参数,你可以在`wrapper.conf`中添加这些参数。例如,`...
1. **Wrapper相关文档**:这些文档通常包含了关于如何配置和使用Windows Wrapper的详细指南。它们可能包括安装步骤、配置文件的格式、设置参数的方法以及解决常见问题的技巧。通过这些文档,开发者可以了解如何将...
QueryWrapper 常用用法