2016-6-4

Java

Servlet

jakarta ee 文档 : https://jakarta.ee/learn/#documentation jakartaee-tutorial: https://jakarta.ee/learn/docs/jakartaee-tutorial/current/index.html

  • Java Servlet 3.1: 该版本于2013年发布,引入了一些新特性,包括对非阻塞 I/O 的支持、对 WebSockets 的原生支持以及对 Servlet 容器的配置灵活性的增强。
  • Java Servlet 4.0: Servlet 4.0 是 Java EE 8 的一部分,于2017年发布。这个版本引入了对 HTTP/2 的支持、对 Server Push 的支持、对 HTTP Upgrade 的支持以及对新的 HTTP 方法的支持。
  • Java Servlet 5.0: Servlet 5.0 是 Jakarta EE 9.1(原名 Java EE)的一部分,于2021年发布。这个版本引入了对 HTTP/3 和 Java 11 的全面支持,以及对 HTTP 头的新功能和增强。

4.0及之前的servlet-api由Oracle官方维护 而5.0及以后的servlet-api由Eclipse开源社区维护

生命周期

https://www.geeksforgeeks.org/life-cycle-of-a-servlet/

  1. 初始化阶段 当客户端向 Servlet 容器发出 HTTP 请求要求访问 Servlet 时,Servlet 容器首先会解析请求,检查内存中是否已经有了该 Servlet 对象,如果有,则直接使用该 Servlet 对象,如果没有,则创建 Servlet 实例对象,然后通过调用 init() 方法实现 Servlet 的初始化工作。

在 Servlet 的整个生命周期内,它的 init() 方法只调用一次。

  1. 运行阶段 这是 Servlet 生命周期中最重要的阶段,在这个阶段中,Servlet 容器会为这个请求创建代表 HTTP 请求的 ServletRequest 对象和代表 HTTP 响应的 ServletResponse 对象,然后将它们作为参数传递给 Servlet 的 service() 方法。 service() 方法从 ServletRequest 对象中获得客户请求信息并处理该请求,通过 ServletResponse 对象生成响应结果。

在 Servlet 的整个生命周期内,对于 Servlet 的每一次访问请求,Servlet 容器都会调用一次 Servlet 的 service() 方法,并且创建新的 ServletRequest 和 ServletResponse 对象,也就是说,service() 方法在 Servlet 的整个生命周期中会被调用多次

  1. 销毁阶段 当服务器关闭或 Web 应用被移除出容器时,Servlet 随着 Web 应用的关闭而销毁。在销毁 Servlet 之前,Servlet 容器会调用 Servlet 的 destroy() 方法,以便让 Servlet 对象释放它所占用的资源。

在 Servlet 的整个生命周期中,destroy() 方法也只调用一次

手搓servlet app

  1. %TOMCAT_HOME%/webapps/xxx 下新建自定义名字(xxx)项目文件夹
  2. 引入servlet-api.jar 包
  3. servlet app 目录说明:
目录/文件说明
WEB-INF/包含Web应用程序的部署描述符、编译后的类文件和依赖库。
WEB-INF/web.xmlWeb应用程序的部署描述符,配置Servlet、Filter等组件。
WEB-INF/classes/存放编译后的Java类文件。
WEB-INF/lib/存放Web应用程序所需的依赖库(JAR文件)。
META-INF/可选,包含一些元数据文件。

开发一个用户管理Web项目

目录

servlet_1 1.servlet开发流程 生命周期,三种实现方式 2.什么是servlet? 3.Servlet的运行过程 (再谈)

servlet_2 1.同一个用户的页面数据共享 1.1. cookie 1.2. sendRedirect() 跳转 1.3. 隐藏表单 1.4.session 技术

servlet_3 1.servlet 如何操作数据库 2.SQL注入

servlet_4 1.分页算法

servlet_5 1.cookie技术

servlet_6 1.网站框架的改进(MV模型)

servlet_7 1.什么是ServletContext?

servlet_8 功能添加

servlet_9 功能添加

servlet_10 功能添加 <<界面优化>> <<删除用户>> <<修改用户>> <<添加用户>> <<查找用户>>


Tomcat SSL配置{ 1.通过java的keytool工具创建证书 2. servlet配置SSL连接器 … 4.强制某个WEB应用访问https 5.配置tomcat-users

} 后续{ 1.文件上传

}

servlet_1{

<tomcat-users>
  <role rolename="manager"/>
  <user name="tomcat" password="tomcat" roles="tomcat" />
  <user name="role1"  password="tomcat" roles="role1"  />
  <user name="both"   password="tomcat" roles="tomcat,role1" />
  <user username="admin" password="admin" roles="manager"/>
</tomcat-users>

1.servlet开发流程,生命周期,三种实现方式

	1.1 servlet开发流程
		 1. %TOMCAT_HOME%webapps下新建自定义名字项目文件夹
		 
		 2. 引入servlet-api.jar 包
		 
		 3. 部署servlet项目文件夹有WEB-INF; 
				WEB-INF\classes  存放编译好的servlet ;
				WEB-INF\lib 该web app用到的库文件; 
				WEB-INF\web.xml 该web app的配置文件;
		
		
 
	1.2 生命周期: 
		访问http://localhost:8080/MyWeb/yy ->容器(如:tomcat,Jboss)装载servlet->
			调用servlet的init方法(只调用一次初始化)->
			调用servlet的service方法处理一般业务逻辑(每次访问都会调用)->
			调用servlet的destroy方法销毁( 1.reload 该Servlet(webapps) 2.关闭tomcat 3.关机 该方法会被调用)
		 
		 tomcat(开源的servlet容器可以受http请求,或者转发到(servlet/jsp);返回静态页面给用户)
			servlet(java 服务器小程序(执行java))[如:操作数据库,访问返回给tomcat]
		 
	1.3.Servlet 的三种实现方法
	  1.1 实现Servlet接口
	  1.2 继承GenericServlt的形式开发
	  1.3 继承HttpServlet 的形式开发
  
2.什么是servlet?

	引用:
		Servlet(Server Applet), 全称Java Servlet; 是用Java编写的服务器端程序; 
	 其主要功能在于交互式地浏览和修改数据, 生成动态Web内容; 
	 【狭义的Servlet是指Java语言实现的一个接口】,  【广义的Servlet是指任何实现了这个Servlet接口的类】, 
	 一般情况下, 人们将Servlet理解为后者; 
	 
		Servlet运行于支持Java的应用服务器中; 从原理上讲, Servlet可以响应任何类型的请求, 
	但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器; 
		
		servlet就是一个java类, 只不过运行在一个servlet容器中作为请求的后台服务提供者; 


3.Servlet的运行过程
																   
	Servlet程序是由WEB服务器调用, web服务器收到客户端的Servlet访问请求后: 
	
  ①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象; 如果是, 则直接执行第④步, 否则, 执行第②步; 
  ②装载并创建该Servlet的一个实例对象; 
  ③调用Servlet实例对象的init()方法; 
  ④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象, 
		然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去; 
  ⑤WEB应用程序被停止或重新启动之前, Servlet引擎将卸载Servlet, 并在卸载之前调用Servlet的destroy()方法;  

}

servlet_2{

1.同一个用户的页面数据共享
	1.1. cookie 
	1.2. sendRedirect() 跳转
	1.3. 隐藏表单
	1.4.session 技术
	
	1.1. cookie :
		参见servlet_5笔记
		
	1.2. sendRedirect() 跳转:

	使用sendRedirect()实现不同页面的数据共享
	
	注意:
		res.sendRedirect("wel?uname="+u);
		1.用[?]号跟url分开,多个数据用[&]符隔开.
		2.传送信息比较快,但只能传送字符串不能传送对象.
		
	1.3. 隐藏表单:
		 "<input type=hidden name=sex value = man>"
		 跟普通表单差不多,但是用户是看不到的.

	1.4.session 技术:
	
		session,用户访问某个网站时,(服务器暂时为该用户独立出一部分内存空间,
			作数据共享[其他用户不可访问];默认时间是30min,可以修改)
			
		session作用
			防止用户登陆到非法页面
			保存登陆信息
			...
			
		如何存储?
			类似一个两列的表; 一个属性(String)名称对应一个值(object) 理论无限个对应

		使用?
			1.得到 session:
				HttpSession hs = request.getSession(true);//如果没有自动创建一个

			2.向session 添加属性:
				hs.setAttribute(String name, Object val);//名称,对应的值

			3.从session 获得某个属性:
				Object val = hs.getAttribute(String name);

			4.从session 删除属性:
				hs.removeAttribute(String name)//名称

				session.invalidate();//清理所有session
				session.removeAttrubute(String name);//这里的参数是session变量名称

			
		注意:
			session属性默认存在时间是30min,超时会被清除(在30min都没有对该属性进行过操作)
			session会为每一个(浏览器)分配一个唯一的session ID[即一个浏览器的软件实例]
			session占用服务器内存(注意用户数量多的情况)

}

servlet_3{

1.servlet 如何操作数据库

操作数据库跟java是一样的
serverSQL 2000是: msbase, mssqlserver, msutil
注意:
	java连接数据库的三个jar包, mssqlserver.jar, msutil.jar, msbase.jar 需要放到
	%TOMCAT_HOME%/commons/lid/下(作用于整个tomcat),或者
	%TOMCAT_HOME%webapps/MyWeb**/WEB-INF/lib下(作用于你的webapps)

2.SQL注入

特别注意: SQL注入!
	例子 select  * from accountTable where userName='abc' and userPassword='abc' or 1 ='1'
		 or 1 ='1' //即使前面错误,后面条件成立,依然合法!!!

}

servlet_4{

1.分页算法:
 定义4个变量
 int pageSize; //每页显示多少条记录
 int pageNow;  //希望显示第几页
 int pageCount;//一共有多少页
 int rowCount; //一共有多少条记录

 pageSize;指定的,pageNow;用户选择,rowCount; 从数据库查到
pageCount;可以计算
if([一共有多少条记录] / 显示多少条记录)

if([一共有多少条记录] % 显示多少条记录 == 0)
{
	[一共有多少页] = [一共有多少条记录] / 显示多少条记录
}else{

	[一共有多少页] = [一共有多少条记录] / 显示多少条记录 + 1
}

}

servlet_5{

1.cookie技术

 cookie 服务端在客户端保存信息(如:登陆用户名,用户密码)
服务端可根据需要读取,写入到客户端


如何存储?
	  类似一个两列的表; 一个属性(String)名称对应一个值(String)理论无限个对应,注意两个值都是String!
	cookie正常是明文保存安全性不高
如何使用?
	1. 在服务的创建cookie
		Cookie c =  new Cookie(String name,String val);

	2. 将一个cookie添加(写入)到客户端
		response.addCookie(c);

	3. 从客户端读取cookie服务端(所有的cookie,以name标识,遍历找不到可能过期)
		Cookie[] request.getCookie();//返回值为null 没有Cookie,

	4.设置cookie存在时间,不设定时间cookie将不被保存.
		c.setMaxAge(int s);//秒 注意不同于session 这个时间是真实的

	5. 删除一个cookie,如果是负数不会保存,
		Cookie.setMaxAge(0);


cookie 与session
	1.cookie 保存在客户端, session 在服务端
	2.安全性比较而言 cookie低于session,cookie正常明文保存, 且保存于客户端,所以安全性较低
	3.网络传输,cookie通过网络与服务端传输, 而session在服务端不需要传输
	4.cookie生命周期累计(以30分钟为例),从创建时开始累计,30分钟后过期, session 生命周期是间隔计时, 在30分钟内
			没有访问, or操作才清除, 30分钟内访问过,即从新计时间.
	5.关机会影响session,对cookie没有任何影响.

}

servlet_6{

1.网站框架的改进(MV模型)


 此前所有的实现 都是将界面ui和业务逻辑处理都放在一起(modell模式),它们有一些问题.
	1.logincl, 和 wel 都去操作数据库,它们逻辑相似,代码重复
	2.整体没有清晰的层次关系,非常乱
	3.代码可读性差,难维护
	4.界面改动可能影响业务逻辑
  
 如何改进?
	1.分层(界面层,业务逻辑层)[MV模式] ; M(Model 模型) , V(View 视图)
	2.将常用(重复性)的代码封装函数.

{
  显示层
	login,logincl, wel 
  需要逻辑数据可以到UserBeanCl封装的某个方法访问(必要可以定义,成员字段,或者(UserBean)映射成对象,减少对数据库操作 )

}
{
  逻辑层
	UserBeanCl (专门操作数据库)

	UserBean (数据库一条记录,对应一个UserBean实例,UserBeanCl操作数据库后可以即时分离(关闭数据库链接) )
	
	ConnDB(得到数据库连接)	
}

}

servlet_7{

1.什么是ServletContext?

	理解ServletContext,要与Cookie,和Session对比,
Cookie和Session无论在客户机还是服务端,都是单个用户数据共享; 而ServletContex存储于服务端
可以被所有客户端访问.
 
如何存储?
	类似一个两列的表; 一个属性(String)名称对应一个值(object) 理论无限个对应
	
使用? 
	1. 得到 ServletContext 实例
	  ServletContext  sc = this.getServletContext();
	2. 添加属性值
		sc.setAttribute(String name, Object val);
	3.得到属性值
		Object  sc.getAttribute(String name);//返回object
	4.删除属性值
		removeAttribute(String name);
	5.生命周期
		ServletContext中的属性值的生命周期, 从创建开始 到服务器关闭时结束.( tomcat(容器)关闭 )

注意: ServletContext 好用, 但是ServletContext存储周期相对较长(),非必要不使用.
}

servlet_8{

看项目文件吧=.= }

servlet_9{

看项目文件吧 }

servlet_10{

<<界面优化>>
<<删除用户>>
<<修改用户>>
<<添加用户>>
<<查找用户>>
-------------------------------
	<<删除用户>>
集中操作到一个(Main.java)主界面,更好(直观)的多项管理

wel.java(删除用户)->delUsercl.java (独立页面;转发数据库操作到UserBeanCl.java(没有界面,仅作处理后跳转) )=>
	UserBeanCl.java(真的操作数据库)---(操作成功)---> Ok.java (成功提示页面)
	UserBeanCl.java(真的操作数据库)---(操作失败)--->Err.java (失败提示页面)

--------------------------------------
	<<修改用户>>
wel.java(修改用户)->Update.java(独立页面,表单提交.)->Updatecl.java(没有界面,仅作处理后跳转) ->转发操作数据库到UserBeanCl.java=>
	UserBeanCl.java(真的操作数据库)---(操作成功)---> Ok.java (成功提示页面)
	UserBeanCl.java(真的操作数据库)---(操作失败)--->Err.java (失败提示页面)


-----------------------
	<<添加用户>>
main.java(主界面)->AddDate.java(独立页面,表单提交.)->AddDateCl.java(没有界面,调用处理) ->转发操作数据库到UserBeanCl.java=>
	UserBeanCl.java(真的操作数据库)---(操作成功)---> Ok.java (成功提示页面)
	UserBeanCl.java(真的操作数据库)---(操作失败)--->Err.java (失败提示页面)
	
	


	
-----------------------
	<<查找用户>>

main.java(主界面)->FindUsers.java(独立页面,显示结果,转发操作) ->转发操作数据库到UserBeanCl.java->
	UserBeanCl.java(真的操作数据库)--<返回结果到>--->>FindUsers.java(显示结果)

}

Tomcat SSL配置{

参考: http://www.cnblogs.com/codewater/articles/2182826.html

1.通过java的keytool工具创建证书

	通过java的keytool工具创建证书的命令为: 

	keytool -genkeypair -alias "yang" -keyalg "RSA" 
		将生产一对非对称密钥和自我签名的证书, 这个命令中几个参数的意思如下: 
		-genkeypair: 生成一对非对称密钥; 
		-alias: 指定密钥对的别名, 该别名是公开的; 
		-keyalg: 指定加密算法, 本例中的采用通用的RAS加密算法
	
	1.1 首先会提示输入keystore的密码, 这里我输入的密码是sunchis; 
	1.2 然后提示输入个人信息, 如姓名, 组织单位和所在城市等, 只要输入真实信息即可; 
	1.3 接着会提示输入信息是否正确, 输入"y"表示信息正确; 
	
	以上命令将在操作系统的用户目录下生成名为".keystore"的文件; 

	1.4 另外, 如果希望生成的keystore文件存放在其他目录中
		keytool -genkeypair -alias "yang" -keyalg "RSA" –keystore "D:\sunchis.keystore" 
		
	1.5 查看已生成的证书的命令为: 
		keytool -list -keystore "C:\Documents and Settings\os用户名\.keystore" 
		
2.	 servlet配置SSL连接器
	在Tomcat的server.xml文件中, 已经提供了现成的配置SSL连接器的代码, 只要把<Connector>元素的注释去掉即可: 
	
		;(这是Tomcat6 的);
			 <!-- Define a SSL HTTP/1.1 Connector on port 8443
			 This connector uses the JSSE configuration, when using APR, the 
			 connector should be using the OpenSSL style configuration
			 described in the APR documentation -->
		<!--
		<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
				   maxThreads="150" scheme="https" secure="true"
				   clientAuth="false" sslProtocol="TLS" keystorePass="*?*"/>
		-->

	
	{
		
		在linux下
		cd 到java/bin 
		./keytool -genkey -alias yang -keyalg RSA  
		//键入必要的信息, 在用户目录下生成.keystore 文件
		//查看
		keytool -list -keystore /home/yang/.keystore
				
				
		//server.xml文件,指定证书文件: 
		keystoreFile="/home/yang/.keystore"
		
		

		
		
	}
	实际上, 基于SSL的HTTPS使用的默认端口是443; 但Tomcat在这里将HTTPS端口设置为8443; 
	
	<Connector>配置里的一些属性参数如下表: 
	1.clientAuth	: 如果设为true, 表示Tomcat要求所有的SSL客户出示安全证书, 对SSL客户进行身份验证
	
	2.keystoreFile: 	指定keystore文件的存放位置, 可以指定绝对路径, 
		也可以指定相对于<CATALINA_HOME> (Tomcat安装目录)环境变量的相对路径; 如果此项没有设定, 
		默认情况下, Tomcat将从当前操作系统用户的用户目录下读取名为 ".keystore"的文件; 
	
	3.keystorePass: 	指定keystore的密码, 如果此项没有设定, 在默认情况下, 
		Tomcat将使用"changeit"作为默认密码; 
	
	4.sslProtocol: 	指定套接字(Socket)使用的加密/解密协议, 
		默认值为TLS, 用户不应该修改这个默认值; 
	
	5.ciphers: 	指定套接字可用的用于加密的密码清单, 多个密码间以逗号(,)分隔; 
		如果此项没有设定, 在默认情况下, 套接字可以使用任意一个可用的密码; 
		
		
3.测试
	以https的方式访问; 
		https://192.168.3.10/
	当然这是一个自签证书, 会受到浏览器警告
		
4.强制某个WEB应用访问https
只需要修改webapps文件夹下各自的web.xml文件(注意: 这是wbapp下的web.xml; 非tomcat的conf/web.xml)
在welcome-file-list元素下增加, 如下: 
	; 
		<welcome-file-list>
			<welcome-file>index.jsp</welcome-file>
		</welcome-file-list>
  
		 <login-config>
		 <!-- Authorization setting for SSL -->
		 <auth-method>CLIENT-CERT</auth-method>
		 <realm-name>Client Cert Users-only Area</realm-name>
		 </login-config>
		 <security-constraint>
		<!-- Authorization setting for SSL -->
		 <web-resource-collection >
		 <web-resource-name >SSL</web-resource-name>
		 <url-pattern>/*</url-pattern>
		 </web-resource-collection>
		 <user-data-constraint>
		 <transport-guarantee>CONFIDENTIAL</transport-guarantee>
		 </user-data-constraint>
		 </security-constraint>
	; ; ; ; ; ; */

redirectPort="8443"


5.配置tomcat-users

<role rolename="manager-gui"/>

}

文件上传

需要导入jar包 commons-fileupload-1.3.2.jar commons-io-2.5.jar

<form action="upload" method="post" enctype="multipart/form-data">
<input name="f" type="file" />
<input  name="feil" value="val" >
<input type="submit" value="提交">
</form> 
	2.servlet代码
	private String filePath="e:/";    // 文件存放目录  
    private String tempPath="e:/";    // 临时文件目录  
	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		//  Auto-generated method stub
			res.setContentType("text/plain;charset=utf-8");  
	        PrintWriter pw = res.getWriter();  
	        try{  
	            DiskFileItemFactory diskFactory = new DiskFileItemFactory();  
	            // threshold 极限, 临界值, 即硬盘缓存 1M  
	            diskFactory.setSizeThreshold(4 * 1024);  
	            // repository 贮藏室, 即临时文件目录  
	            diskFactory.setRepository(new File(tempPath));  
	          
	            ServletFileUpload upload = new ServletFileUpload(diskFactory);  
	            // 设置允许上传的最大文件大小 4M  
	            upload.setSizeMax(4 * 1024 * 1024);  
	            // 解析HTTP请求消息头  
	            List fileItems = upload.parseRequest((HttpServletRequest) req);  
	            Iterator iter = fileItems.iterator();  
	            while(iter.hasNext())  
	            {  
	                FileItem item = (FileItem)iter.next();  
	                if(item.isFormField())  
	                {  
	                    System.out.println("处理表单内容 ...");  
	                    processFormField(item, pw);  
	                }else{  
	                    System.out.println("处理上传的文件 ...");  
	                    processUploadFile(item, pw);  
	                }  
	            }// end while()  
	 
	            pw.close();  
	        }catch(Exception e){  
	            System.out.println("使用 fileupload 包时发生异常 ...");  
	            e.printStackTrace();  
	        }// end try ... catch ...  
	        
	        //返回
	      //res.sendRedirect("/ok.jsp");
	}
	
		//	// 处理表单内容  
			private void processFormField(FileItem item, PrintWriter pw)  
				throws Exception  
			{  
				String name = item.getFieldName();  
				String value = item.getString();          
				pw.println(name + ": " + value + "\r\n");  
			}  
			  
			// 处理上传的文件  
			private void processUploadFile(FileItem item, PrintWriter pw)  
				throws Exception  
			{  
				// 此时的文件名包含了完整的路径, 得注意加工一下  
				String filename = item.getName();         
				System.out.println("完整的文件名: " + filename);  
				int index = filename.lastIndexOf("\\");  
				filename = filename.substring(index + 1, filename.length());  
		 
				long fileSize = item.getSize();  
		 
				if("".equals(filename) && fileSize == 0)  
				{             
					System.out.println("文件名为空 ...");  
					return;  
				}  
		 
				File uploadFile = new File(filePath + "/" + filename);  
				item.write(uploadFile);  
				pw.println(filename + " 文件保存完毕 ...");  
				pw.println("文件大小为: " + fileSize + "\r\n");  
				
			}  
	
	
}