文章目录
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 个部分组成:
- XML 声明:指明所用 XML 的版本、文档的编码、文档的独立性信息
- 文档类型声明:指出 XML 文档所用的 DTD
- 元素:由开始标签、元素内容和结束标签构成
- 注释:以结束,用于对文档中的内容起一个说明作用
- 处理指令:通过处理指令来通知其他应用程序来处理非 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解析方式
Dom4j、Sax、Pull
2.3 Dom4j与Sax区别
dom4j不适合大文件的解析,因为它是一下子将文件加载到内存中,所以有可能出现内存溢出,sax是基于事件来对xml进行解析的,所以他可以解析大文件的xml,也正是因为如此,所以dom4j可以对xml进行灵活的增删改查和导航,而sax没有这么强的灵活性,所以sax经常是用来解析大型xml文件,而要对xml文件进行一些灵活(crud)操作就用dom4j。
2.4 使用dom4j解析xml
解析XML过程是通过获取Document对象,然后获取各个节点以及属性等操作,因此获取Document对象是第一步,大体来说有三种方式:
- 自己创建Document对象
Document document = DocumentHelper.createDocument(); //students是根节点,可以继续添加其他节点等操作。 Element root = document.addElement("students");
- 自己创建Document对象
// 创建SAXReader对象 SAXReader reader = new SAXReader(); // 读取文件 转换成Document Document document = reader.read(new File("XXXX.xml"));
- 读取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可更加明晰其修饰的目标。
- CONSTRUCTOR:用于描述构造器
- FIELD:用于描述域
- LOCAL_VARIABLE:用于描述局部变量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述参数
- 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后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
- 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
- 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
- 服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
- 客户端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类:用于存储会话数据
- 构造Cookie对象
Cookie(java.lang.String name, java.lang.String value) - 设置cookie
void setPath(java.lang.String uri) :设置cookie的有效访问路径
void setMaxAge(int expiry) : 设置cookie的有效时间
void setValue(java.lang.String newValue) :设置cookie的值 - 发送cookie到浏览器端保存
void response.addCookie(Cookie cookie) : 发送cookie - 服务器接收cookie
Cookie[] request.getCookies() : 接收cookie
Cookie原理
- 服务器创建cookie对象,把会话数据存储到cookie对象中。
new Cookie(“name”,“value”); - 服务器发送cookie信息到浏览器
response.addCookie(cookie);
举例: set-cookie: name=eric (隐藏发送了一个set-cookie名称的响应头) - 浏览器得到服务器发送的cookie,然后保存在浏览器端。
- 浏览器在下次访问服务器时,会带着cookie信息
举例: cookie: name=eric (隐藏带着一个叫cookie名称的请求头) - 服务器接收到浏览器带来的cookie信息
request.getCookies();
Cookie的细节
- void setPath(java.lang.String uri) :设置cookie的有效访问路径。有效路径指的是cookie的有效路径保存在哪里,那么浏览器在有效路径下访问服务器时就会带着cookie信息,否则不带cookie信息。
- void setMaxAge(int expiry) : 设置cookie的有效时间。
- 正整数:表示cookie数据保存浏览器的缓存目录(硬盘中),数值表示保存的时间。
- 负整数:表示cookie数据保存浏览器的内存中。浏览器关闭cookie就丢失了!!
- 零:表示删除同名的cookie数据
- Cookie数据类型只能保存非中文字符串类型的。可以保存多个cookie,但是浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
Cookie的局限
- Cookie只能存字符串类型。不能保存对象
- 只能存非中文。
- 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类:用于保存会话数据
- 创建或得到session对象
HttpSession getSession()
HttpSession getSession(boolean create) - 设置session对象
void setMaxInactiveInterval(int interval) : 设置session的有效时间
void invalidate() : 销毁session对象
java.lang.String getId() : 得到session编号 - 保存会话数据到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();
- 第一次访问创建session对象,给session对象分配唯一的ID,叫JessionId
new HttpSession();
- 把JessionId作为Cookie的值发送给浏览器保存
Cookie cookie = new Cookie("JSESSIONID", sessionID); response.addCookie(cookie);
- 第二次访问的时候,浏览器带着JSESSIONID的cookie访问服务器
- 服务器得到JSESSIONID,在服务器的内存中搜索是否存放对应编号的session对象。
if(找到){ return map.get(sessionID); }
- 如果找到对应编号的session对象,直接返回该对象
- 如果找不到对应编号的session对象,创建新的session对象,继续走1的流程
Sesson细节
- getId() :得到session编号
- 两个getSession方法:
getSession(true)/getSession():创建或得到session对象。没有匹配的session编号,自动创建新的session对象。
getSession(false):得到session对象。没有匹配的session编号,返回null - void setMaxInactiveInterval(int interval):设置session的有效时间
session对象销毁时间:- 默认情况30分服务器自动回收
- 修改session回收时间
- 修改全局session有效时间
<session-config> <session-timeout>1</session-timeout> </session-config>
- 手动销毁session对象
void invalidate() : 销毁session对象
- 如何避免浏览器的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重复提交)
- 服务器端休眠0.3秒。在0.3秒内提交都算重复提交
7.1 使用javascript 解决
- 第一种方法:增加js变量submitFlag为false,并在form标签增加οnsubmit=“return submitFlag()”,当提交时修改submitFlag为true,下次提交就为false,form就不会被提交。
- 第二种方法:表单提交后将按钮置为不可用,让用户没有机会点击第二次提交按钮。
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的区别
- GET方式
- 地址栏(URI)会跟上参数数据。以?开头,多个参数之间以&分割。
- GET提交参数数据有限制,不超过1KB。
- GET方式不适合提交敏感密码。
- 注意: 浏览器直接访问的请求,默认提交方式是GET方式
- POST方式
- 参数不会跟着URI后面。参数而是跟在请求的实体内容中。没有?开头,多个参数之间以&分割。
- POST提交的参数数据没有限制。
- 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对象
- 响应行: response.setStatus(); //设置状态码
- 响应头: response.setHeader(“name”,“value”); //设置响应头
- 实体内容:
- response.getWriter().writer(); //发送字符实体内容
- response.getOutputStream().writer(); //发送字节实体内容
请求重定向(Location)
resp.setStatus(302); resp.setHeader("Location", "OtherServlet");
9.4 Http与Https
http与https区别?
- https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
- http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
- http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
- http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
https工作原理?
HTTPS能够加密信息,以免敏感信息被窃取,所以很多安全级别较高的服务都采用HTTPS协议。
客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示。
- 客户使用https的URL访问Web服务器,要求与 Web 服务器建立SSL连接。
- Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
- 客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
- 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
- Web服务器利用自己的私钥解密出会话密钥。
- Web服务器利用会话密钥加密与客户端之间的通信。
https缺点
- HTTPS协议握手阶段比较费时,会使页面的加载时间延长近50%,增加10%到20%的耗电;
- HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗,甚至已有的安全措施也会因此而受到影响;
- SSL证书需要钱,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。
- SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗。
- 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请求发送其他域名。
跨域问题解决办法
- 在后台response添加header,response.setHeader(“Access-Control-Allow-Origin”, “*”); 支持所有网站
- 使用JSONP
- 使用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请求
来源:CSDN
作者:西安博哥
链接:https://blog.csdn.net/suwenbovip/article/details/104186650