最近刚好工作中有遇到一个安全问题,IE 在 URL 地址栏中输入 < 这种特殊字符的时候,将会抛出异常打印异常堆栈信息,这样你的系统是不安全的,而且即使你在项目中使用了拦截器、过滤器等进行拦截都没用办法处理。原因是<并没有进入我们自己的web应用而是在中间间层tomcat容器就已经报错了。原因tomcat8.5.30之后拦截特殊字符解决办法
我们的 Web 应用是运行在 Tomcat 中的,请求必须是先经过 Tomcat 的。Tomcat 你也可以理解为是一个应用。他需要提供几个常见的服务。
位于网络上,并且要支持 TCP/IP 等协议,肯定是需要基于 Socket 的连接的。
一个 Tomcat 可以为多个 Web 应用提供服务,所以 Tomcat 可以把 URL 下发到不同的 Web 应用中去。
话不多说,上手写程序…
我们先实现构建好处理请求和响应的类,iRequest 与 iResponse 。
IRequest.java
package com.company;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* tomcat 中的请求接收类
*
* @author qiuweijie
* @since 2020-05-05
*/
public class IRequest {
// 请求路径
private String url;
// 请求方法
private String method;
// 读取输入字节流,封装成字符串格式的请求内容
public IRequest(InputStream inputStream) throws IOException{
String httpRequest = "";
byte[] httpRequestBytes = new byte[1024];
int length =0;
if ((length = inputStream.read(httpRequestBytes)) > 0) {
httpRequest = new String(httpRequestBytes, 0 ,length);
}
// HTTP 请求协议:首行的内容依次为请求方法、请求路径以及请求协议及其对应版本号
// GET / HTTP/1.1
String httpHead = httpRequest.split("\n")[0]; // 取出 HTTP 请求协议的首行
System.out.println(httpHead);
method = httpHead.split("\\s")[0]; // 按照空格进行分割,第一个是请求的方法
url = httpHead.split("\\s")[1]; // 按照空格进行分割,第二个是请求的路径
System.out.println(this.toString());
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
@Override
public String toString() {
return "IRequest{" +
"url='" + url + '\'' +
", method='" + method + '\'' +
'}';
}
}
IResponse.java
package com.company;
import java.io.IOException;
import java.io.OutputStream;
/**
* tomcat 中的请求接收类
* @since 2020-05-05
* @author qiuweijie
*/
public class IResponse {
private OutputStream outputStream;
public IResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
//将文本转换为字节流
public void write(String content) throws IOException {
StringBuffer httpResponse = new StringBuffer();
httpResponse.append("HTTP/1.1 200 OK\n") //按照HTTP响应报文的格式写入
.append("Content-Type:text/html\n")
.append("\r\n")
.append("")
.append(content) //将页面内容写入
.append("");
outputStream.write(httpResponse.toString().getBytes()); //将文本转为字节流
outputStream.close();
}
}
IServlet .java
package com.company;
/**
* 抽象的 servlet 类,提供 servlet 常见的三种方法
*/
public abstract class IServlet {
public void service(IRequest iRequest, IResponse iResponse){
if(iRequest.getMethod().equalsIgnoreCase("POST")){
doPost(iRequest,iResponse);
}else if(iRequest.getMethod().equalsIgnoreCase("GET")){
doGet(iRequest, iResponse);
}
}
public void doGet(IRequest iRequest, IResponse iResponse) {
}
public void doPost(IRequest iRequest, IResponse iResponse) {
}
}
HelloServlet .java
package com.company;
import java.io.IOException;
public class HelloServlet extends IServlet {
@Override
public void doGet(IRequest iRequest, IResponse iResponse) {
try {
iResponse.write("get hello servlet");
}catch (IOException e){
e.printStackTrace();
}
}
@Override
public void doPost(IRequest iRequest, IResponse iResponse) {
try{
iResponse.write("post hello servlet");
}catch (IOException e){
e.printStackTrace();
}
}
}
ServletMapping .java
package com.company;
/**
* 类似于 web.xml 中的 servlet 标签
* 存放名字 对应 url 对应类
*/
public class ServletMapping {
private String servletName;
private String url;
private String clazz;
public ServletMapping(String servletName, String url, String clazz) {
this.servletName = servletName;
this.url = url;
this.clazz = clazz;
}
public String getServletName() {
return servletName;
}
public void setServletName(String servletName) {
this.servletName = servletName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}
ServletMappingConfig .java
package com.company;
import java.util.ArrayList;
import java.util.List;
/**
* 类似于 web.xml 把一个一个 servlet 标签注册进来
*/
public class ServletMappingConfig {
public static List<ServletMapping> servletMappingList = new ArrayList<>();
static {
servletMappingList.add(new ServletMapping("index", "/index", "com.company.HelloServlet"));
}
}
ITomcat .java
package com.company;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* 启动 tomcat
*/
public class ITomcat {
private Integer port = 8888; // 定义socket连接端口
private Map<String, String> urlServletMapping = new HashMap<>(); // 存储 url 和 对应的类
public ITomcat(Integer port) {
this.port = port;
}
public void start(){
initServletMapping();
try{
ServerSocket serverSocket = null;
serverSocket = new ServerSocket(port);
while (true){
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
IRequest iRequest = new IRequest(inputStream);
IResponse iResponse = new IResponse(outputStream);
dispatch(iRequest, iResponse);
socket.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
private void dispatch(IRequest iRequest, IResponse iResponse) {
String clazz = urlServletMapping.get(iRequest.getUrl());
try {
Class<IServlet> iServletClass = (Class<IServlet>) Class.forName(clazz);
IServlet iServlet = iServletClass.newInstance();
iServlet.service(iRequest, iResponse);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private void initServletMapping() {
for (ServletMapping servletMapping: ServletMappingConfig.servletMappingList){
urlServletMapping.put(servletMapping.getUrl(), servletMapping.getClazz());
}
}
public static void main(String[] args) {
ITomcat iTomcat = new ITomcat(8888);
iTomcat.start();
}
}
测试结果如下:
现在我们再倒退一下:
先启动 ITomcat 的 main 方法,监听 socket 协议的 8888 窗口.
start 方法里已经做了名称、路径、处理类映射。
页面访问 http://localhost:8888/index
将会通过类反射机制找到相应的处理类
最后找到处理类的 doget 方法
调用 response的write 方法
源码地址:https://github.com/weijieqiu/iStudy