0011-0016 教程学习笔记

0011 Session与Cookie实现原理

第一节 会话管理

Cookie   保存在客户端

Session 保存在服务器内存中,客户端与服务器通讯用SessionId

应用场景在哪里? 登录,购物车,移动App接口会话管理

第二节 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。

第三节 Session技术

Cookie的局限:

1)Cookie只能存字符串类型。不能保存对象

2)只能存非中文。

3)1个Cookie的容量不超过4KB。

 如果要保存非字符串,超过4kb内容,只能使用session技术!!!

 Session特点:

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

  服务器创建一个Session后,session信息保存在服务器中,响应中把sessionId返回给客户端

  客户端在下次请求中会将sessionId通过请求头的方式传给服务端

  1)第一次访问创建session对象,给session对象分配一个唯一的ID,叫JSESSIONID
    new HttpSession();
  2)把JSESSIONID作为Cookie的值发送给浏览器保存
    Cookie cookie = new Cookie("JSESSIONID", sessionID);
    response.addCookie(cookie);
  3)第二次访问的时候,浏览器带着JSESSIONID的cookie访问服务器
  4)服务器得到JSESSIONID,在服务器的内存中搜索是否存放对应编号的session对象。
  if(找到){
    return map.get(sessionID);
  }
  Map<String,HttpSession>

  <"s001", s1>
  <"s001,"s2>
  5)如果找到对应编号的session对象,直接返回该对象
  6)如果找不到对应编号的session对象,创建新的session对象,继续走1的流程

  结论:通过JSESSION的cookie值在服务器找session对象!!!!!
3.4 Sesson细节
  1)java.lang.String getId() : 得到session编号
  2)两个getSession方法:
    getSession(true) / getSession() : 创建或得到session对象。没有匹配的session编号,自动创 建新的session对象。
    getSession(false): 得到session对象。没有匹配的session编号,返回null
  3)void setMaxInactiveInterval(int interval) : 设置session的有效时间
  session对象销毁时间:默认情况30分服务器自动回收

第四节 自定义缓存

用Map集合去装数据

唯一不重复字符串作为key,简称sessionId

自定义缓存(简单的缓存框架)

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) { //此处不加synchronize会出现线程不安全问题
		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"));
	}

}

第六节 表单重复提交解决方案

出现原因:

1)网络延时  2)重复刷新   3)点击后退按钮到表单页

1. 前端解决办法

1)onSumit中设置提交标志  2)点击提交按钮后设置按钮为不可用

2. 后端解决办法

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

在下列情况下,服务器程序将拒绝处理用户提交的表单请求:

  1. 存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
  2. 当前用户的Session中不存在Token(令牌)。
  3. 用户提交的表单数据中没有Token(令牌)
LocaFromServlet
@WebServlet("/LocaFromServlet")
public class LocaFromServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		   // 生成token
		String tokenValue=TokenUtils.getToken();
		HttpSession session = req.getSession();
		session.setAttribute("sessionToken", tokenValue);
		req.getRequestDispatcher("from.jsp").forward(req, resp);
	}
	
}

  from.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form表单</title>
<script type="text/javascript">
	var flag = false;//false表示为提交 true 就是已经提交
	function dosubmit(){
	    //获取表单提交按钮
	    var btnSubmit = document.getElementById("submit");
	    //将表单提交按钮设置为不可用,这样就可以避免用户再次点击提交按钮
	    btnSubmit.disabled= "disabled";
	    //返回true让表单可以正常提交
	    return true;
	}

</script>
</head>

<body>
	<form action="DoFormServlet"
		method="post"  onsubmit="return dosubmit()">
		<input type="hidden" name="parameterToken" value="${sessionToken}">
		用户名:<input type="text" name="userName"> <input type="submit"
			value="提交" id="submit"   >
	</form>


</body>
</html>

  DoFromServlet

@WebServlet("/DoFormServlet")
public class DoFormServlet extends HttpServlet {

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setContentType("text/html;charset=utf-8");// 防止浏览器显示乱码
        if(!isBumit(req)){
        	System.out.println("您提交提交了数据..或者token错误!");
        	resp.getWriter().write("您提交提交了数据..或者token错误!");
        	return ;
        }
		String userName = req.getParameter("userName");
		try {
			Thread.sleep(300);
		} catch (Exception e) {
			// TODO: handle exception
		}
		System.out.println("数据库插入数据...userName:" + userName);
		// 插入数据库...
		resp.getWriter().write("保存成功..");
		req.getSession().removeAttribute("sessionToken");
		
	}

	public Boolean isBumit(HttpServletRequest request) {
		String parameterToken = request.getParameter("parameterToken");
		String sessionToken = (String) request.getSession().getAttribute("sessionToken");
	    //判断是否提交
		if (sessionToken == null) {
			return false;
		}
		// 判断是否是伪造token
		if(!(parameterToken.equals(sessionToken))){
			return false;
		}
		return true;

	}

}

1.访问/LocaFromServlet,服务器端生成token,一份存到session,一份传给from.jsp的隐藏域,然后转发到from.jsp

2。在from.jsp中输入内容,点击提交到DoFormServlet

3.在DoFormServlet中比对请求参数中隐藏域的token和session中的token是否一致,如果一致则说明是第一次提交,然后清除session,当再次提交时,session中的token为空,所以不是第一次提交,返回失败,这样就能解决重复提交的问题

 0012 深入理解Http协议

1.1 什么是http协议?

http协议: 对浏览器客户端 和  服务器端 之间数据传输的格式规范

第四节 防盗链

原因:B网站直接引用A网站的图片链接

方案: 判断referreferer的网址名是否是本网站名

public class ImgFilter implements Filter {

	public void destroy() {
		// TODO Auto-generated method stub

	}

	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		String referer = request.getHeader("referer");
		System.out.println("refer is" + "" + referer);
		if (referer == null || !referer.contains(request.getServerName())) { //如果是null则为直接从浏览器访问
			request.getRequestDispatcher("/static/error.png").forward(request, response);
		} else {
			filterChain.doFilter(request, response);
		}
	}

	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub

	}

}

 web安全与防攻击

1.什么是XSS?

  就是脚本注入(<script>这里写了一些恶意代码</script>),使用过滤器把提交参数的value值,转换成html代码执行

2.CSRF (模拟http请求) ,企业中会有白名单和黑名单,其实就是判断referer

第六节 http与https

 http与https区别

 1、https 协议需要到 ca 申请证书,一般免费证书较少,因而需要一定费用。

 2、http 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl 加密传输协议。

 3、http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。

 4、http 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全。

 

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

  (1)客户使用 https 的 URL 访问 Web 服务器,要求与 Web 服务器建立 SSL 连接。

  (2)Web 服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。

  (3)客户端的浏览器与 Web 服务器开始协商 SSL 连接的安全等级,也就是信息加密的等级。

  (4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。

  (5)Web 服务器利用自己的私钥解密出会话密钥。

  (6)Web 服务器利用会话密钥加密与客户端之间的通信

 0014 Spring基础知识

框架离不开 反射+解析xml

Spring是一个容器,就是把每个bean与bean的关系全部交给第三方容器进行管理,整个bean对象的生命周期都交给spring管理

1.springioc执行流程:

解析xml配置,获取bean class 地址

使用java反射机制,进行实例化对象

返回对象

 问原理不要答用了什么什么类,直接把思想打出来

2.单例在哪里使用?  我们用的spring默认是单例的,为了节约JVM资源。单例有什么缺点?线程不安全,所以尽量不要共享全局变量

3.Spring四种生命周期

Prototype 多例 :每次调用 getBean()都会新建一个bean

Singleton 单例 : 容器中只有一个实例

request  http 请求request作用里面使用,每次新http请求都会产生一个新的bean

session http请求session作用域中使用,每次http请求都会产生新的bean

第四节 SpringIOC

1.IOC容器创建对象方式:

  1)调用无参构造器

  2)调用带参构造器

  3)工厂创建对象

<!-- 无参构造函数 -->
	<bean id="user1" class="com.itmayiedu.entity.UserEntity" scope="prototype" />
	<!-- 有参构造函数 -->
	<bean id="user2" class="com.itmayiedu.entity.UserEntity">
		<constructor-arg name="name" type="java.lang.String"
			value="张三"></constructor-arg>
		<constructor-arg name="age" type="java.lang.Integer"
			value="18"></constructor-arg>
	</bean>

	<bean id="factory" class="com.itmayiedu.entity.ObjectFactory"></bean>
	<!-- 通过实例工厂方法创建 -->
	<bean id="user3" factory-bean="factory" factory-method="getInstance"></bean>
	<!-- 通过静态工厂方法创建 -->
	<bean id="user4" class="com.itmayiedu.entity.ObjectFactory"
		factory-method="getStaticInstance"></bean>

2. 依赖注入

  1) 通过构造函数

        2) 通过set方法给属性注入值

        3) p名称空间

   (常用)Set方法注入值

 

<!-- dao instance -->
	<bean id="userDao" class="cn.itmayiedu.UserDao"></bean>

	<!-- service instance -->
	<bean id="userService" class="cn.itmayiedu.UserService">
		<property name="userDao" ref="userDao"></property>
	</bean>
	
	<!-- action instance -->
	<bean id="userAction" class="cn.itmayiedu.UserAction">
		<property name="userService" ref="userService"></property>
	</bean>

   p 名称空间注入属性值 (优化)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
	
	<!-- ###############对象属性赋值############### -->
	
	<!-- 
		给对象属性注入值:
			# p 名称空间给对象的属性注入值
			 (spring3.0以上版本才支持)
	 -->
	 <bean id="userDao" class="cn.itmayiedu.UserDao"></bean>
	 
	 <bean id="userService" class="cn.itmayiedu.UserService" p:userDao-ref="userDao"></bean>
	 
	 <bean id="userAction" class="cn.itmayiedu.UserAction" p:userService-ref="userService"></bean>
	
	
	<!-- 传统的注入: 
	 <bean id="user" class="cn.itmayiedu.User" >
	 	<property name="name" value="xxx"></property>
	 </bean>
	-->
	<!-- p名称空间优化后 -->
	<bean id="user" class="cn.itmayiedu.User" p:name="Jack0001"></bean>
	 
</beans>   

注解版本使用

注解方式可以简化spring的IOC容器的配置!

 

使用注解步骤:

        1)先引入context名称空间

                xmlns:context="http://www.springframework.org/schema/context"

        2)开启注解扫描

                <context:component-scan base-package="cn.itcast.e_anno2"></context:component-scan>

        3)使用注解

                通过注解的方式,把对象加入ioc容器。

 

           创建对象以及处理对象依赖关系,相关的注解:

                @Component   指定把一个对象加入IOC容器

 

@Repository   作用同@Component; 在持久层使用

@Service      作用同@Component; 在业务逻辑层使用

@Controller    作用同@Component; 在控制层使用

 

@Resource     属性注入

 

总结:

        1) 使用注解,可以简化配置,且可以把对象加入IOC容器,及处理依赖关系(DI)
        2) 注解可以和XML配置一起使用。

@Resource与@autiwer

0015 深入理解Spring事务

1.Spring事务与传播行为

事务特性: 原子性、一致性、隔离性、持久性

事务用法: 1)编程事务(自己手动控制事务)  2)声明式事务(xml 注解)

 

第七节 事务传播行为

Spring中事务的定义:

事务传播行为:

        Propagation.REQUIRED

                指定当前的方法必须在事务的环境下执行;

                如果当前运行的方法,已经存在事务, 就会加入当前的事务;

        Propagation.REQUIRED_NEW

                指定当前的方法必须在事务的环境下执行;

 

                如果当前运行的方法,已经存在事务:  事务会挂起; 会始终开启一个新的事务,执行完后;  刚才挂起的事务才继续运行。

 

Class Log{
		Propagation.REQUIRED  
		insertLog();  
}

	Propagation.REQUIRED
	Void  saveDept(){
		insertLog();    // 加入当前事务
		.. 异常, 会回滚
		saveDept();
	}


	Class Log{
		Propagation.REQUIRED_NEW  
		insertLog();  
}

	Propagation.REQUIRED
	Void  saveDept(){
		insertLog();    // 始终开启事务
		.. 异常, 日志不会回滚
		saveDept();
	}

  

事务传播行为:

        Propagation.REQUIRED

                指定当前的方法必须在事务的环境下执行;

                如果当前运行的方法,已经存在事务, 就会加入当前的事务;

        Propagation.REQUIRED_NEW

                指定当前的方法必须在事务的环境下执行;

                如果当前运行的方法,已经存在事务:  事务会挂起; 会始终开启一个新的事务,执行完后;  刚才挂起的事务才继续运行。

 

posted @ 2020-02-16 00:34  余***龙  阅读(191)  评论(0)    收藏  举报