9-java安全基础——Servlet
Posted songly_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9-java安全基础——Servlet相关的知识,希望对你有一定的参考价值。
Servlet程序
Servlet是Servlet Applet的简称,翻译过来大概就是一个运行在服务端的小程序,用于处理服务器的请求,什么是服务器程序?
我们知道一般在Web应用程序中都是通过浏览器来访问web服务器资源的,通常浏览器要访问一个网页时会发送一个http请求给web服务器,然后web服务器接收到该请求会进行相应的处理,然后把处理的结果再返回给浏览器,因此不难看出Servlet是用于处理服务器的请求(业务逻辑)。
如何编写一个Servlet程序
servlet是Sun公司提供了一种开发动态web资源的一种技术,并在其API提供了一个servlet接口,如果开发者想要开发一个Servlet程序需要实现以下几个步骤:
- 自定义一个java类并实现Servlet接口
- 实现servlet接口中的抽象方法
- 配置Servlet访问规则
自定义ServletTest2类实现Servlet接口并重写接口中的抽象方法:
import javax.servlet.*;
import java.io.IOException;
/**
* @auther songly_
* @data 2021/8/1 16:47
*/
public class ServletTest2 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service方法执行了......");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
在web.xml文件中配置ServletTest2的访问规则:
<servlet>
<servlet-name>test2</servlet-name>
<servlet-class>ServletTest2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test2</servlet-name>
<url-pattern>/test2</url-pattern>
</servlet-mapping>
启动tomcat容器,在浏览器中访问:http://localhost:8080/ServletTest2_war_exploded/test2 ,当访问/test2时,ServletTest2中的service方法就就会被调用。
Servlet执行原理
接下来我们通过Servlet执行原理来分析ServletTest2中的service方法是如何被调用的
1. 用户在浏览器中输入http://localhost:8080/ServletTest2_war_exploded/test2时,会发送一个http请求
2. 当tomcat服务器接受到客户端浏览器的http请求后,会解析该请求中的资源文件rui路径(/test2),然后解析web.xml文件,查找<servlet-mapping>标签中的<url-pattern>标签体的uri路径是否为/test2,如果有继续根据<servlet-name>标签查找对应的<servlet-class>全类名,tomcat容器会根据ServletTest2的全类名找到该类加载到内存生成class文件,并通过反射创建Servlet实例对象
3. tomcat会创建一个ServletRequest对象和ServletResponse对象,然后将用户浏览器的http请求消息封装到ServletRequest对象中,再将这两个对象传给Servlet,然后调用Servle对象的Service方法
通过Servlet执行流程中不难看出,Servlet主要是负责接收请求,调用Service方法,然后响应数据。
Servlet生命周期
Servlet接口中有5个方法,如下所示:
init方法:当客户端发送请求时,Servlet容器就会调用init方法初始化一个Servlet对象,需要注意的是init方法只会在第一次请求时被调用
service方法:提供服务方法,每一次访问Servlet对象时都会被调用
destory方法:当要销毁Servlet之前会调用该方法,例如当要关闭服务时Servlet容器就会调用destory方法销毁Servlet对象
getServletInfo方法:这个方法会返回Servlet的描述信息,可以返回一段字符串
getServletConfig方法:这个方法会返回由Servlet容器传给ini方法的ServletConfig对象
关于init方法
这里想tomcat服务器发送了三次请求,可以看到init方法只会在第一次访问时被调用,说明一个Servlet在内存中只存在一个对象,Servlet是单例的,多个用户同时访问时,可能存在线程安全问题。
Servlet什么时候会被创建?web.xml文件中指定Servlet的创建时机:<load-on-startup>的值为负数表示第一次被访问时才创建Servlet,如果值为0或正整数表示在服务器启动时创建Servlet。
<servlet>
<servlet-name>test2</servlet-name>
<servlet-class>com.test.ServletTest2</servlet-class>
<!-- 配置Servlet创建时机 -->
<load-on-startup>0</load-on-startup>
</servlet>
关于service方法
浏览器每一次发送请求时都会调用service方法,但是我们知道Servlet是一个接口不能实例化,那么Servlet对象调用service方法实际上是调用了谁的service方法?
通过分析Servlet接口的结构体系发现,Servlet接口有一个实现类GenericServlet,但是GenericServlet是一个抽象类无法实例化,service方法也是一个抽象方法,如下所示:
public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
但是HttpServlet类继承了GenericServlet抽象类,并重写了service抽象方法
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
//http请求对象
HttpServletRequest request;
//http响应对象
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
//又调用了一次service方法
service(request, response);
}
service方法中有两个参数,ServletRequest表示当前http请求,ServletResponse表示当前http响应。在service方法中又调用了一次service方法。
service方法接收了两个参数,HttpServletRequest 是对http请求对象的封装,HttpServletResponse是对http响应对象的封装,service方法会获取http请求的请求方式,并判断http请求的具体请求方式,是否为GET或POST,PUT等其它请求方式,然后调用对应的do前缀方法。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取http请求的方式
String method = req.getMethod();
//判断http请求的具体方式
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
如果我们想要自己实现一个基于GET或者POST请求的服务逻辑的话,就可以通过自定义一个类继承HttpServlet类并重写doGet和doPost方法就行了,这样就可以不需要实现Servlet接口,还要重写Servlet接口的service方法这么麻烦的方式了。
以GET方式发送请求时就会调用doGet方法,以POST方式发送请求会调用doPost方法。
public class ServletTest3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet 执行了.......");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost 执行了......");
}
}
关于HttpServletRequest接口和HttpServletResponse接口
无论是doGet方法还是doPost方法,都会接收两个参数,HttpServletRequest表示当前http请求,HttpServletResponse表示当前http响应,当浏览器发送一个GET方式的http请求时,我们可以使用doGet方法中的req就可以获取http请求消息中的数据,那么大家有没有想过这两个对象从哪里来的呢?
回忆一下Servlet执行原理,发现tomcat已经自动把http请求和http响应抽象成两个接口了。
tomcat为什么要这么做?想象一下,如果tomcat不这么做,这意味着需要我们自己写socket程序,从流中解析http请求了,要知道这是一个很头疼的工作量,无形之中增加了开发复杂度,而tomcat为了简化开发流程已经帮我们完成了这件事,这样我们只需简单的调用这两个接口提供的API接口就可以操作http请求和响应数据了,剩下的把重心放在如何处理业务逻辑就行了。
来看一下HttpServletRequest和HttpServletResponse的结构体系:
使用HttpServletRequest对象获取http请求消息数据:
package com.test;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
/**
* @auther songly_
* @data 2021/8/2 14:37
*/
@WebServlet("/test4")
public class ServletTest4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取request运行类型
System.out.println(request.getClass());
//请求的协议和版本信息
String protocol = request.getProtocol();
//请求的URL地址
String requestURL = request.getRequestURL().toString();
//请求的uri资源
String requestURI = request.getRequestURI();
//请求的Servlet路径
String servletPath = request.getServletPath();
//请求方式
String method = request.getMethod();
//发送请求客户端IP地址
String remoteAddr = request.getRemoteAddr();
//发送请求的客户端端口
int remotePort = request.getRemotePort();
System.out.println("请求的协议和版本信息: " + protocol);
System.out.println("请求的URL地址: " + requestURL);
System.out.println("请求的uri资源: " + requestURI);
System.out.println("请求的Servlet路径: " + servletPath);
System.out.println("请求方式: " + method);
System.out.println("发送请求客户端IP地址: " + remoteAddr);
System.out.println("发送请求的客户端端口: " + remotePort);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
程序执行结果如下:
关于ServletConfig对象和ServletContext对象
在Servlet的web.xml配置文件的<init-param>标签表示servlet配置的初始化参数,tomcat会将这些初始化参数封装到ServletConfig对象中,当Servlet的init方法被调用时要求传入一个ServletConfig参数,init方法会将ServletConfig对象传给servlet。
当创建Servlet对象时,<init-param>标签中的初始化参数会封装到ServletConfig对象中,我们可以通过Servlet对象的getServletConfig方法获取ServletConfig对象,在通过getInitParameterNames方法获取所有初始化参数。
当tomcat服务器启动时会加载web.xml文件,tomcat会为每个web应用程序创建一个ServletContext对象,可以理解为整个web.xml文件就代表ServletContext对象,并且一个web应用通常会有多个Servlet
无论是哪种方式都是获取同一个ServletContext对象,并且在开发过程中可以通过ServletConfig对象的getServletContext方法获得ServletContext对象,通常一个web应用中通常会有多个Servlet,并且这些Servlet共享同一个ServletContext对象,因此可以通过ServletContext对象来实现Servlet之间数据共享。
以上是关于9-java安全基础——Servlet的主要内容,如果未能解决你的问题,请参考以下文章