您的位置:首页 > 运维架构 > Tomcat

手写tomcat系列3-长叶-第一个Servlet容器优化

2019-04-17 20:55 399 查看

系列2遗留问题

本文接系列2,首先抛出一个问题,系列2实现的第一个Servlet容器有一个安全问题:在 ServletProcessor1 类的 process 方法中执行servlet 的 service 方 法时,request对象向上转型为 javax.servlet.ServletRequest对象,response向上转型为
javax.servlet.ServletResponse对象。

try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) request,(ServletResponse) response);
}

这种玩法存在安全漏洞,具体问题为:知道这个servlet 容器的内部运作的程序员可以分别把ServletRequest 和 ServletResponse 实例向下转型本容器的Request 和Response对象,并调用Request 和Response对象的公共方法。如可以调用Request的parse方法或调用Response的sendStaticResource 方法。同时不能将 parse 和 sendStaticResource 方法定义为private,因为其他的类需要调用上述两个方法。
解决思路一:让 Request 和Response 类拥有默认访问修饰(包访问权限),不允许在包外部被调用。
解决思路二:更优雅的解决办法,门面模式,使用 facade 类隐藏Request 和Response。
划重点:本次优化是思路二的实现。

RequestFacade类代码实现:

/**
*** 门面模式应用,区别于代理模式,这里应用的目的是隐藏真request对象,而不是代理一个request对象接受用户请求。
* 使用此模式用户保护response和request对象中方法不被劫持利用。**
*/
public class RequestFacade implements ServletRequest {
**// 持有私有化的ServletRequest对象,通过构造函数初始化,通过此方式隐藏真request对象**
private ServletRequest request = null;
public RequestFacade(Request request) {
this.request = request;
}
/* implementation of the ServletRequest*/
public Object getAttribute(String attribute) {
return request.getAttribute(attribute);
}
public Enumeration<?> getAttributeNames() {
return request.getAttributeNames();
}
@SuppressWarnings("deprecation")
public String getRealPath(String path) {
return request.getRealPath(path);
}
public RequestDispatcher getRequestDispatcher(String path) {
return request.getRequestDispatcher(path);
}
public boolean isSecure() {
return request.isSecure();
}
public String getCharacterEncoding() {
return request.getCharacterEncoding();
}
public int getContentLength() {
return request.getContentLength();
}
public String getContentType() {
return request.getContentType();
}
public ServletInputStream getInputStream() throws IOException {
return request.getInputStream();
}
public Locale getLocale() {
return request.getLocale();
}
public Enumeration getLocales() {
return request.getLocales();
}
public String getParameter(String name) {
return request.getParameter(name);
}
public Map getParameterMap() {
return request.getParameterMap();
}
public Enumeration getParameterNames() {
return request.getParameterNames();
}
public String[] getParameterValues(String parameter) {
return request.getParameterValues(parameter);
}
public String getProtocol() {
return request.getProtocol();
}
public BufferedReader getReader() throws IOException {
return request.getReader();
}
public String getRemoteAddr() {
return request.getRemoteAddr();
}
public String getRemoteHost() {
return request.getRemoteHost();
}
public String getScheme() {
return request.getScheme();
}
public String getServerName() {
return request.getServerName();
}
public int getServerPort() {
return request.getServerPort();
}
public void removeAttribute(String attribute) {
request.removeAttribute(attribute);
}
public void setAttribute(String key, Object value) {
request.setAttribute(key, value);
}
public void setCharacterEncoding(String encoding)
throws UnsupportedEncodingException {
request.setCharacterEncoding(encoding);
}
}

ResponseFacade类代码实现:

/**
* 门面模式应用,区别于代理模式,这里应用的目的是隐藏真request对象,而不是代理一个request对象接受用户请求。
* 使用此模式用户保护response和request对象中方法不被劫持利用。
*/
public class ResponseFacade implements ServletResponse {
private ServletResponse response;
public ResponseFacade(Response response) {
this.response = response;
}
public void flushBuffer() throws IOException {
response.flushBuffer();
}
public int getBufferSize() {
return response.getBufferSize();
}
public String getCharacterEncoding() {
return response.getCharacterEncoding();
}
public Locale getLocale() {
return response.getLocale();
}
public ServletOutputStream getOutputStream() throws IOException {
return response.getOutputStream();
}
public PrintWriter getWriter() throws IOException {
return response.getWriter();
}
public boolean isCommitted() {
return response.isCommitted();
}
public void reset() {
response.reset();
}
public void resetBuffer() {
response.resetBuffer();
}
public void setBufferSize(int size) {
response.setBufferSize(size);
}
public void setContentLength(int length) {
response.setContentLength(length);
}
public void setContentType(String type) {
response.setContentType(type);
}
public void setLocale(Locale locale) {
response.setLocale(locale);
}
}

ServletProcessor2.0实现(ServletProcessor1升级版)

public class ServletProcessor2 {
public void process(Request request, Response response) {
String uri = request.getUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
// the forming of repository is taken from the createClassLoader method in
// org.apache.catalina.startup.ClassLoaderFactory
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
// the code for forming the URL is taken from the addRepository method in
// org.apache.catalina.loader.StandardClassLoader class.
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
}catch (ClassNotFoundException e) {
System.out.println(e.toString());
}
Servlet servlet = null;
/**
* 门面模式应用,区别于代理模式,这里应用的目的是隐藏真request对象,而不是代理一个request对象接受用户请求。
*/
**RequestFacade requestFacade = new RequestFacade(request);
ResponseFacade responseFacade = new ResponseFacade(response);**
try {
servlet = (Servlet) myClass.newInstance();
**servlet.service((ServletRequest) requestFacade, (ServletResponse) responseFacade);**
}
catch (Exception e) {
System.out.println(e.toString());
}
catch (Throwable e) {
System.out.println(e.toString());
}
}
}

其他类和系列2完全相同,不再重复列出。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: