Java基础

為{幸葍}努か 提交于 2020-02-12 02:21:34


07-Java网络编程之socket
06-自定义注解与设计模式、
05-数据交换格式与SpringIOC底层实现、
11-Session与Cokile实现原理、
12-深入理解Http协议

1. JSON

1.1 常用JSON解析框架

fastjson(阿里)、gson(谷歌)、jackson(SpringMVC自带)

1.2 使用fastjson解析json

引入依赖

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.1.43</version>
</dependency>

fastjson api

public static final Object parse(String text); // 把JSON文本parse为JSONObject或者JSONArray 
public static final JSONObject parseObject(String text)// 把JSON文本parse成JSONObject    
public static final <T> T parseObject(String text, Class<T> clazz); // 把JSON文本parse为JavaBean 
public static final JSONArray parseArray(String text); // 把JSON文本parse成JSONArray 
public static final <T> List<T> parseArray(String text, Class<T> clazz); //把JSON文本parse成JavaBean集合 
public static final String toJSONString(Object object); // 将JavaBean序列化为JSON文本 
public static final String toJSONString(Object object, boolean prettyFormat); // 将JavaBean序列化为带格式的JSON文本 
public static final Object toJSON(Object javaObject); //将JavaBean转换为JSONObject或者JSONArray。

解析json

static String jsonStr = "{\"sites\":[{\"name\":\"蚂蚁课堂\",\"url\":\"www.itmayiedu.com\"},{\"name\":\"每特教育\",\"url\":\"http://meiteedu.com/\"}]}";

public static void main(String[] args) {
	JSONObject jsonObject = new JSONObject();
	// 将json字符串转为jsonbject
	JSONObject jsonStrObject = jsonObject.parseObject(jsonStr);
	JSONArray jsonArray = jsonStrObject.getJSONArray("sites");
	for (Object object : jsonArray) {
		JSONObject stObject = (JSONObject) object;
		String name = stObject.getString("name");
		String url = stObject.getString("url");
		System.out.println(name + "---" + url);
	}
}

组装json

JSONObject jsonObject = new JSONObject();
JSONArray jsonArray = new JSONArray();
JSONObject stObject = new JSONObject();
stObject.put("name", "this is name");
stObject.put("url", "this is url");
jsonArray.add(stObject);
jsonObject.put("sites", jsonArray);
System.out.println(jsonObject.toJSONString());

2. XML

XML是可扩展标记语言(Extensible Markup Language,简称XML),是一种标记语言,主要用于描述数据和用作配置文件。
XML 文档在逻辑上主要由一下 5 个部分组成:

  1. XML 声明:指明所用 XML 的版本、文档的编码、文档的独立性信息
  2. 文档类型声明:指出 XML 文档所用的 DTD
  3. 元素:由开始标签、元素内容和结束标签构成
  4. 注释:以结束,用于对文档中的内容起一个说明作用
  5. 处理指令:通过处理指令来通知其他应用程序来处理非 XML 格式的数据,格式为

2.1 XML样例

<!-- xml文件头说明xml的版本和编码,utf-8一般是网络传输用的编码 -->
<?xml version="1.0" encoding="UTF-8"?>  
<students>  
    <student1 id="001">  
        <微信公众号>@残缺的孤独</微信公众号>  
        <学号>20140101</学号>  
        <地址>北京海淀区</地址>  
        <座右铭>要么强大,要么听话</座右铭>  
    </student1>  
    <student2 id="002">  
        <新浪微博>@残缺的孤独</新浪微博>  
        <学号>20140102</学号>  
        <地址>北京朝阳区</地址>  
        <座右铭>在哭泣中学会坚强</座右铭>  
    </student2>  
</students>

2.2 XML解析方式

Dom4jSaxPull

2.3 Dom4j与Sax区别

dom4j不适合大文件的解析,因为它是一下子将文件加载到内存中,所以有可能出现内存溢出,sax是基于事件来对xml进行解析的,所以他可以解析大文件的xml,也正是因为如此,所以dom4j可以对xml进行灵活的增删改查和导航,而sax没有这么强的灵活性,所以sax经常是用来解析大型xml文件,而要对xml文件进行一些灵活(crud)操作就用dom4j。

2.4 使用dom4j解析xml

 解析XML过程是通过获取Document对象,然后获取各个节点以及属性等操作,因此获取Document对象是第一步,大体来说有三种方式:

  1. 自己创建Document对象
    Document document = DocumentHelper.createDocument();
    //students是根节点,可以继续添加其他节点等操作。
    Element root = document.addElement("students"); 
    
  2. 自己创建Document对象
    // 创建SAXReader对象
    SAXReader reader = new SAXReader();
    // 读取文件 转换成Document
    Document document = reader.read(new File("XXXX.xml"));
    
  3. 读取XML文本内容获取Document对象
    String xmlStr = "<students>......</students>";
    Document document = DocumentHelper.parseText(xmlStr);
    

注意:

//获取当前项目路径
this.getClass().getClassLoader().getResourceAsStream(xmlPath)

三. 注解

 注解是Jdk1.5新增新技术。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。

注解分类:内置注解(也称为元注解,负责注解其他注解)、自定义注解(Spring框架)

3.1 内置注解

 元注解的作用是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,被用来提供对其它annotation类型作说明。Java5.0定义的元注解:

@Target
 @Target说明了Annotation所修饰的对象范围:Annotation可被用于packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

  1. CONSTRUCTOR:用于描述构造器
  2. FIELD:用于描述域
  3. LOCAL_VARIABLE:用于描述局部变量
  4. METHOD:用于描述方法
  5. PACKAGE:用于描述包
  6. PARAMETER:用于描述参数
  7. TYPE:用于描述类、接口(包括注解类型) 或enum声明

@Retention
 表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

@Documented

@Inherited

3.2 自定义注解

使用 @interface 定义注解。

@Target(value={ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotation {
	int beanId() default 0;
	String className() default "";
	String[] arrays();
}

案例:实现ORM框架映射
ORM框架实体类与表字段不一致,底层生成sql语句原理。

自定义表映射注解

@Target(value = { ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SetTable {
	//对应数据库表名称
	String value();
}

自定义字段属性

@Retention(RetentionPolicy.RUNTIME)
public @interface SetProperty {
	//字段名称
	String name();
	//长度
	int leng();
}

使用java自定义注解,模拟ORM框架注解版本

public class Main {
	public static void main(String[] args) throws ClassNotFoundException {
		// 1.反射class
		Class<?> classForName = Class.forName("com.entity.Sudent");
		// 2.获取表名称注解
		SetTable setTable = classForName.getAnnotation(SetTable.class);
		// 3.获取所有的成员属性
		Field[] declaredFields = classForName.getDeclaredFields();
		StringBuffer sf = new StringBuffer();
		sf.append(" select ");
		String fromName = setTable.value();
		for (int i = 0; i < declaredFields.length; i++) {
			Field field = declaredFields[i];
			// 4.属性字段
			SetProperty sb = field.getAnnotation(SetProperty.class);
			sf.append(" " + sb.name() + " ");
			if (i == declaredFields.length - 1) {
				sf.append(" from ");
			} else {
				sf.append(" , ");
			}
		}
		sf.append(" " + fromName);
		System.out.println(sf.toString());
	}
} 

四. Socket

网络模型
在这里插入图片描述

Socket是一种使用TCP或UDP协议进行IO通讯的一种技术。
TCP与UDP在概念上的区别:

  • udp:
    • 1.是面向无连接, 将数据及源的封装成数据包中,不需要建立建立连接
    • 2.每个数据报的大小在限制64k内
    • 3.因无连接,是不可靠协议
    • 4.不需要建立连接,速度快
  • tcp:
    • 1.建议连接,形成传输数据的通道.
    • 2.在连接中进行大数据量传输,以字节流方式
    • 3.通过三次握手完成连接,是可靠协议
    • 4.必须建立连接m效率会稍低

4.1 UDP协议

通过UDP协议实现课程案例,客户端与服务器端进行传输

服务器端代码

DatagramSocket ds = new DatagramSocket(8080);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
// 阻塞效果
ds.receive(dp);
String str = new String(dp.getData(),0,dp.getLength());
System.out.println("客户端发送数据:"+str);
ds.close();

客户端代码

DatagramSocket ds = new DatagramSocket();
String sendMsg= "客户端发送数据....";
byte[] strByte = sendMsg.getBytes();
DatagramPacket dp = new DatagramPacket(strByte, strByte.length, InetAddress.getByName("192.168.1.3"), 8080);
ds.send(dp);
ds.close();

4.2 TCP握手协议(三次握手,四次分手)

在TCP/IP协议中,TCP协议采用三次握手建立一个连接。

  • 第一次握手:建立连接时,客户端发送SYN包(SYN=J)到服务器,并进入SYN_SEND状态,等待服务器确认;
  • 第二次握手:服务器收到SYN包,必须确认客户的SYN(ACK=J+1),同时自己也发送一个SYN包(SYN=K),即SYN+ACK包,此时服务器V状态;
  • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=K+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据

四次分手:
 由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

  1. 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
  2. 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
  3. 服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
  4. 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。

1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
答:因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的.
 
2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
答:因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

服务器端代码

ServerSocket serverSocket = new ServerSocket(8080);
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
byte[] buf= new byte[1024];
int len=inputStream.read(buf);
String str =new String(buf,0,len);
System.out.println("str:"+str);
serverSocket.close();

客户端代码

Socket s = new Socket("192.168.1.3", 8080);
OutputStream outputStream = s.getOutputStream();
outputStream.write("我是客戶端....".getBytes());
s.close(); 

五、Session与Cookie(自定义Session)

5.1 Cooke技术

Cookie技术特点: 会话数据保存在浏览器客户端。

Cookie技术核心
Cookie类:用于存储会话数据

  1. 构造Cookie对象
    Cookie(java.lang.String name, java.lang.String value)
  2. 设置cookie
    void setPath(java.lang.String uri) :设置cookie的有效访问路径
    void setMaxAge(int expiry) : 设置cookie的有效时间
    void setValue(java.lang.String newValue) :设置cookie的值
  3. 发送cookie到浏览器端保存
    void response.addCookie(Cookie cookie) : 发送cookie
  4. 服务器接收cookie
    Cookie[] request.getCookies() : 接收cookie

Cookie原理

  1. 服务器创建cookie对象,把会话数据存储到cookie对象中。
    new Cookie(“name”,“value”);
  2. 服务器发送cookie信息到浏览器
    response.addCookie(cookie);
    举例: set-cookie: name=eric (隐藏发送了一个set-cookie名称的响应头)
  3. 浏览器得到服务器发送的cookie,然后保存在浏览器端。
  4. 浏览器在下次访问服务器时,会带着cookie信息
    举例: cookie: name=eric (隐藏带着一个叫cookie名称的请求头)
  5. 服务器接收到浏览器带来的cookie信息
    request.getCookies();

Cookie的细节

  1. void setPath(java.lang.String uri) :设置cookie的有效访问路径。有效路径指的是cookie的有效路径保存在哪里,那么浏览器在有效路径下访问服务器时就会带着cookie信息,否则不带cookie信息。
  2. void setMaxAge(int expiry) : 设置cookie的有效时间。
    • 正整数:表示cookie数据保存浏览器的缓存目录(硬盘中),数值表示保存的时间。
    • 负整数:表示cookie数据保存浏览器的内存中。浏览器关闭cookie就丢失了!!
    • 零:表示删除同名的cookie数据
  3. Cookie数据类型只能保存非中文字符串类型的。可以保存多个cookie,但是浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。

Cookie的局限

  1. Cookie只能存字符串类型。不能保存对象
  2. 只能存非中文。
  3. Cookie的容量不超过4KB。

案例:显示用户上次访问的时间

@WebServlet("/LastAccessTime")
public class LastAccessTime extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setContentType("text/html;charset=utf-8");// 防止浏览器显示乱码
		String lastAccessTime = null;
		Cookie[] cookies = req.getCookies();
		for (Cookie cookie : cookies) {
			String name = cookie.getName();
			if (name.equals("lastAccessTime")) {
				lastAccessTime = cookie.getValue();
				break;
			}
		}
		if (StringUtils.isEmpty(lastAccessTime)) {
			resp.getWriter().print("您是首次访问!");
		} else {
			resp.getWriter().print("你上次访问时间:" + lastAccessTime);
		}
		// 保存访问时间
		// 创建cookie 将当前时间作为cookie保存到浏览器
		String currenttime = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss").format(new Date());
		Cookie cookie = new Cookie("lastAccessTime", currenttime);
		cookie.setMaxAge(60 * 60 * 24);
		// 发送cookie
		resp.addCookie(cookie);
	}

	String addCookie(String key, String value, HttpServletResponse resp) {
		return key;
	}
}

5.2 Session技术

面对cookie的局限性(上节的局限性),如果要保存非字符串,超过4kb内容,只能使用session技术!!!

Session特点:会话数据保存在服务器端。(内存中)

Session技术核心
HttpSession类:用于保存会话数据

  1. 创建或得到session对象
    HttpSession getSession()
    HttpSession getSession(boolean create)
  2. 设置session对象
    void setMaxInactiveInterval(int interval) : 设置session的有效时间
    void invalidate() : 销毁session对象
    java.lang.String getId() : 得到session编号
  3. 保存会话数据到session对象
    void setAttribute(java.lang.String name, java.lang.Object value) : 保存数据
    java.lang.Object getAttribute(java.lang.String name) : 获取数据
    void removeAttribute(java.lang.String name) : 清除数据

Session原理
问题: 服务器能够识别不同的浏览者!!!

代码解读:HttpSession session = request.getSession();

  1. 第一次访问创建session对象,给session对象分配唯一的ID,叫JessionId
    new HttpSession();
    
  2. 把JessionId作为Cookie的值发送给浏览器保存
    Cookie cookie = new Cookie("JSESSIONID", sessionID);
    response.addCookie(cookie);
    
  3. 第二次访问的时候,浏览器带着JSESSIONID的cookie访问服务器
  4. 服务器得到JSESSIONID,在服务器的内存中搜索是否存放对应编号的session对象。
    if(找到){
    	return map.get(sessionID);
    }
    
  5. 如果找到对应编号的session对象,直接返回该对象
  6. 如果找不到对应编号的session对象,创建新的session对象,继续走1的流程

Sesson细节

  1. getId() :得到session编号
  2. 两个getSession方法:
    getSession(true)/getSession():创建或得到session对象。没有匹配的session编号,自动创建新的session对象。
    getSession(false):得到session对象。没有匹配的session编号,返回null
  3. void setMaxInactiveInterval(int interval):设置session的有效时间
    session对象销毁时间:
    1. 默认情况30分服务器自动回收
    2. 修改session回收时间
    3. 修改全局session有效时间
    <session-config>
    	<session-timeout>1</session-timeout>
    </session-config>
    
    1. 手动销毁session对象
      void invalidate() : 销毁session对象
  4. 如何避免浏览器的JSESSIONID的cookie随着浏览器关闭而丢失的问题
    /**
     * 手动发送一个硬盘保存的cookie给浏览器
     */
    Cookie c = new Cookie("JSESSIONID",session.getId());
    c.setMaxAge(60*60);
    response.addCookie(c);
    

5.3 自定义缓存

定义缓存实体类

public class Cache {
	public Cache(String key, Object value, Long timeOut) {
		super();
		this.key = key;
		this.value = value;
		this.timeOut = timeOut;
	}
	
	public Cache() {
	}

	private String key;
	private Object value;
	
	//超时时间
	private Long timeOut;

	//setter和getter...
}

定义缓存类

public class CacheManager {
	private Map<String, Cache> cacheMap = new HashMap<>();

	//往缓存存值
	public void put(String key, Object oj) {
		put(key, oj, null);
	}

	//往缓存存值
	public synchronized void put(String key, Object oj, Long timeOut) {
		if (oj == null) {
			return;
		}
		Cache cache = new Cache();
		cache.setKey(key);
		if (timeOut != null)
			cache.setTimeOut(timeOut + System.currentTimeMillis());
		cache.setValue(oj);
		cacheMap.put(key, cache);
	}

	//删除
	public synchronized void deleteCache(String key) {
		cacheMap.remove(key);
	}

	//获取缓存中数据
	public synchronized Object get(String key) {
		Cache cache = cacheMap.get(key);
		Object oj = null;
		if (cache != null) {
			oj = cache.getValue();
		}
		return oj;
	}

	//检查数据是否在有效期内
	public synchronized void checkValidityData() {
		for (String key : cacheMap.keySet()) {
			Cache cache = cacheMap.get(key);
			Long timeOut = cache.getTimeOut();
			if (timeOut == null) {
				return;
			}
			long currentTime = System.currentTimeMillis();
			long endTime = timeOut;
			long result = (currentTime - endTime);
			if (result > 0) {
				System.out.println("清除:"+key);
				cacheMap.remove(key);
			}
		}
	}

	public static void main(String[] args) throws InterruptedException {
		CacheManager cacheManager = new CacheManager();
		// cacheManager.put("lisi", 0);
		cacheManager.put("zhangsan", "jj", 5000l);
		ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
		scheduledThreadPool.schedule(new Runnable() {
			public void run() {
				cacheManager.checkValidityData();
			}
		}, 5000, TimeUnit.MILLISECONDS);
		Thread.sleep(5000);
		System.out.println(cacheManager.get("zhangsan"));
	}
}

六、自定义Token

 token就是一个令牌,具有随机性,类似于sessionId。在对接一些第三方平台时,为了能够保证数据安全性,通常会使用一些令牌进行交互。
 token的生成规则,只要保证token生成一个不重复的唯一字符串即可。例如使用jdk自带的uuid生成规则。

七、表单重复提交解决方案(防止Http重复提交)

  1. 服务器端休眠0.3秒。在0.3秒内提交都算重复提交

7.1 使用javascript 解决

  1. 第一种方法:增加js变量submitFlag为false,并在form标签增加οnsubmit=“return submitFlag()”,当提交时修改submitFlag为true,下次提交就为false,form就不会被提交。
  2. 第二种方法:表单提交后将按钮置为不可用,让用户没有机会点击第二次提交按钮。

7.2 使用后端提交解决

 做法是在服务器端生成一个Token令牌,并在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。

八、Servlet之Fileter

 Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web 开发人员通过 Filter 技术,对 web 服务器管理的所有 web 资源:例如 Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现 URL 级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
 它主要用于对用户请求进行预处理,也可以对 HttpServletResponse 进行后处理。使用 Filter 的完整流程:Filter 对用户请求进行预处理,接着将请求交给 Servlet 进行处理并生成响应,最后 Filter 再对服务器响应进行后处理。

Fileter使用

public class FilterDemo implements Filter {
    public  FilterDemo(){
    	System.out.println("构造函数");
    }
    
	public void init(FilterConfig paramFilterConfig) throws ServletException {
	}
	
	public void doFilter(ServletRequest request, ServletResponse response,FilterChain filterChain) throws IOException, ServletException {
	    System.out.println("doFilter");
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		// 请求地址
		String requestURI = request.getRequestURI();
		System.out.println("requestURI:"+requestURI);
	}

	public void destroy() {
	}
}

web.xml

<filter>
	<filter-name>FilterDemo</filter-name>
	<filter-class>com.servlet.FilterDemo</filter-class>
</filter>
<filter-mapping>
	<filter-name>FilterDemo</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

7.1 使用Fileter防止XSS攻击
 XSS攻击使用Javascript脚本注入进行攻击。
 例如在表单中注入:<script>location.href='http://www.itmayiedu.com'</script>,在结果页原样展示时会被浏览器解析执行。

7.1 解決方案
使用Fileter过滤器过滤器注入标签

public class FilterDemo implements Filter {
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain filterChain) throws IOException, ServletException {
		System.out.println("doFilter");
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		XssAndSqlHttpServletRequestWrapper xssReqWrapper = new XssAndSqlHttpServletRequestWrapper(request);
		paramFilterChain.doFilter(xssReqWrapper, servletResponse);
	}
}

class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
	HttpServletRequest request;
	
	public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) {
		super(request);
		this.request = request;
	}
	
	@Override
	public String getParameter(String name) {
		String value = request.getParameter(name);
		System.out.println("name:" + name + "," + value);
		if (!StringUtils.isEmpty(value)) {
			// 转换Html
			value = StringEscapeUtils.escapeHtml4(value);
		}
		return value;
	}
}

九、深入理解Http协议

http协议版本
http1.0:当前浏览器客户端与服务器端建立连接之后,只能发送一次请求,一次请求之后连接关闭。
http1.1:当前浏览器客户端与服务器端建立连接之后,可以在一次连接中发送多次请求。(基本都使用1.1)

请求资源
URI:统一资源标记符,/day09/hello。用于标记任何资源。可以是本地文件系统,局域网的资源(//192.168.14.10/index.html),可以是互联网。
URL:统一资源定位符,http://localhost:8080/testImg.html。只能定位互联网资源。是URI的子集。

常见的请求方式: GET 、 POST、 HEAD、 TRACE、 PUT、 CONNECT 、DELETE

9.1 GET与POST的区别

  1. GET方式
    1. 地址栏(URI)会跟上参数数据。以?开头,多个参数之间以&分割。
    2. GET提交参数数据有限制,不超过1KB。
    3. GET方式不适合提交敏感密码。
    4. 注意: 浏览器直接访问的请求,默认提交方式是GET方式
  2. POST方式
    1. 参数不会跟着URI后面。参数而是跟在请求的实体内容中。没有?开头,多个参数之间以&分割。
    2. POST提交的参数数据没有限制。
    3. POST方式提交敏感数据。

9.2 防止非法链接(referer)

 为了防止通过域名直接访问图片资源,可以在客户端浏览器的网站内第1次打开图片后,通过Http的referer判断地址是否为官方网址,如果不是则拒绝访问。

代码:

<filter>
	<filter-name>ImgFilter</filter-name>
	<filter-class>com.filter.ImgFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>ImgFilter</filter-name>
	<url-pattern>/static/*</url-pattern>
</filter-mapping>
public class ImgFilter implements Filter {
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		String referer = request.getHeader("referer");
		if (referer == null || !referer.contains(request.getServerName())) {
			request.getRequestDispatcher("/static/error.png").forward(request, response);
		} else {
			filterChain.doFilter(request, response);
		}
	}
}

9.3 获取请求参数

request.getQueryString();//获取GET方式
request.getInputStream();//获取POST方式

//以上两种方式不通用,且获取到的参数还需要进一步解析。所以可使用统一方便的方式:
request.getParameter("参数名");  //根据参数名获取参数值(注意,只能获取一个值的参数)
request.getParameterValue("参数名")//根据参数名获取参数值(可以获取多个值的参数)
request.getParameterNames();   //获取所有参数名称列表

HttpServletResponse对象

  1. 响应行: response.setStatus(); //设置状态码
  2. 响应头: response.setHeader(“name”,“value”); //设置响应头
  3. 实体内容:
    1. response.getWriter().writer(); //发送字符实体内容
    2. response.getOutputStream().writer(); //发送字节实体内容

请求重定向(Location)

resp.setStatus(302);
resp.setHeader("Location", "OtherServlet");

9.4 Http与Https

http与https区别?

  1. https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
  2. http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
  3. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  4. http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

https工作原理?
 HTTPS能够加密信息,以免敏感信息被窃取,所以很多安全级别较高的服务都采用HTTPS协议。

客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示。
在这里插入图片描述

  1. 客户使用https的URL访问Web服务器,要求与 Web 服务器建立SSL连接。
  2. Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
  3. 客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
  4. 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
  5. Web服务器利用自己的私钥解密出会话密钥。
  6. Web服务器利用会话密钥加密与客户端之间的通信。

https缺点

  1. HTTPS协议握手阶段比较费时,会使页面的加载时间延长近50%,增加10%到20%的耗电;
  2. HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗,甚至已有的安全措施也会因此而受到影响;
  3. SSL证书需要钱,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。
  4. SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗。
  5. HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。最关键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行。

七、http请求

服务器模拟http请求工具:httpclient、HttpURLConnection

httpCient请求

<dependencies>
	<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
	<dependency>
		<groupId>javax.servlet</groupId>
		<artifactId>javax.servlet-api</artifactId>
		<version>3.1.0</version>
		<scope>provided</scope>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
	<dependency>
		<groupId>org.apache.httpcomponents</groupId>
		<artifactId>httpclient</artifactId>
		<version>4.3.5</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-lang3</artifactId>
		<version>3.5</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>fastjson</artifactId>
		<version>1.2.29</version>
	</dependency>
</dependencies>
	public void post() {
		// 创建默认的httpClient实例.
		CloseableHttpClient httpclient = HttpClients.createDefault();
		// 创建httppost
		HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");
		// 创建参数队列
		List<NameValuePair> formparams = new ArrayList<NameValuePair>();
		formparams.add(new BasicNameValuePair("type", "house"));
		UrlEncodedFormEntity uefEntity;
		try {
			uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
			httppost.setEntity(uefEntity);
			CloseableHttpResponse response = httpclient.execute(httppost);
			try {
				HttpEntity entity = response.getEntity();
				if (entity != null) {
					System.out.println(EntityUtils.toString(entity, "UTF-8"));
				}
			} finally {
				response.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭连接,释放资源
			try {
				httpclient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public void get() {
		CloseableHttpClient httpclient = HttpClients.createDefault();
		try {
			// 创建httpget.
			HttpGet httpget = new HttpGet("http://www.baidu.com/");
			// 执行get请求.
			CloseableHttpResponse response = httpclient.execute(httpget);
			try {
				// 获取响应实体
				HttpEntity entity = response.getEntity();
				if (entity != null) {
					EntityUtils.toString(entity);
				}
			} finally {
				response.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				httpclient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

7.4 跨域实战解决方案

跨域原因产生:在当前域名请求网站中,默认不允许通过ajax请求发送其他域名。

XMLHttpRequestcannotload\color{red}{XMLHttpRequest cannot load} 跨域问题解决办法

  1. 在后台response添加header,response.setHeader(“Access-Control-Allow-Origin”, “*”); 支持所有网站
  2. 使用JSONP
  3. 使用nginx转发

前端代码:

$.ajax({
	type : "POST",
	async : false,
	url : "http://a.a.com/a/FromUserServlet?userName=张三",
	dataType : "jsonp",//数据类型为jsonp
	jsonp : "jsonpCallback",//服务端用于接收callback调用的function名的参数  
	success : function(data) {
		alert(data.result);
	}
});

后端代码:

@WebServlet("/FromUserServlet")
public class FromUserServlet extends HttpServlet {
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setCharacterEncoding("UTF-8");
		resp.setContentType("text/plain");
		resp.setHeader("Pragma", "No-cache");
		resp.setHeader("Cache-Control", "no-cache");
		resp.setDateHeader("Expires", 0);
		try {
			PrintWriter out = resp.getWriter();
			JSONObject resultJSON = new JSONObject(); // 根据需要拼装json
			resultJSON.put("result", "content");
			String jsonpCallback = req.getParameter("jsonpCallback");// 客户端请求参数
			out.println(jsonpCallback + "(" + resultJSON.toJSONString() + ")");// 返回jsonp格式数据
			out.flush();
			out.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
}

JSONP的缺点:JSONP只支持get请求不支持psot请求

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!