0011

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]


<"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对象销毁时间:
3.1 默认情况30分服务器自动回收
3.2 修改session回收时间
3.3 全局修改session有效时间

第四节 自定义缓存

用Map集合去装数据

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

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

public class CacheManager {

	private Map 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"%>



Form表单




	
用户名:

  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为空,所以不是第一次提交,返回失败,这样就能解决重复提交的问题

 

  

你可能感兴趣的:(0011)