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域中存储的标识号。
在下列情况下,服务器程序将拒绝处理用户提交的表单请求:
- 存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
- 当前用户的Session中不存在Token(令牌)。
- 用户提交的表单数据中没有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
指定当前的方法必须在事务的环境下执行;
如果当前运行的方法,已经存在事务: 事务会挂起; 会始终开启一个新的事务,执行完后; 刚才挂起的事务才继续运行。

浙公网安备 33010602011771号