Cookie,Session Filter,Listener详解
HTTP请求的无状态性
HTTP的无状态性是其一个重要的特征,指的是HTTP协议本身并不保留客户端与服务器交互的历史信息,换而言之,即每次的HTTP请求都是独立的,服务器在处理每一个请求时都不会记住前一个请求的状态
无状态的含义
- 独立性:每次的HTTP请求都是独立的,不依赖于之前的请求,即服务器处理每次请求时都会从头开始,不会参照之前的状态
E.g:假设A用户在一个Web超市添加的一个商品到购物车中,等到再次购买时,服务器已经无法分别判断购买行为是属于用户A还是其他用户
就是由于HTTP请求的无状态性,服务器无法识别其请求的上文状态,因此人们就开发了新的技术来解决HTTP无状态性带来的状态丢失问题,也就是我们接下来要讲解的
Cookie
与Session
也借鉴了创造者@测试开发喵
Cookie
Cookie原意为”饼干”,是由W3C组织提出的.目前的主流浏览器
IE,Google,Edge,Firefox
等等都支持了Cookie技术
Cookie的原理机制
鉴于HTTP请求协议是一个无状态的请求协议,服务器单从网络连接上无法得知客户的身份.于是前辈们就想出了一个方法:给客户端们都颁发一个通行证,这样每次发起请求后都要携带自己的通行证.于是乎服务器就可以从通行证上确认用户的身份了
Cookie的本质
- Cookie实际上是一小串文字信息.当客户端请求服务器时,若服务器要记录该用户的状态,则使用
response
向客户端浏览器发送一个Cookie.客户端的浏览器会把Cookie保存→当此浏览器再次申请服务器时,浏览器会将请求的网站连同Cookie一起提交给服务器→浏览器会检查该Cookie,以此来辨别用户的状态,服务器也可以根据需求修改Cookie中的内容
Cookie的类型
- 持久型cookie
- 以文件方式存放在硬盘空间上的永久性cookie.持久cookie是值存放在客户端硬盘中的cookie信息(被设置了一段有效期)
- 当用户访问网站时,浏览器会在本地硬盘上查找与该网站相关联的cookie.若该cookie存在,则浏览器会将其和页面请求一起发送到用户所在的站点,之后服务器会比对cookie中相应的属性值与存放在服务器中的信息是否一致,以此判断”新用户”和老用户
- 以文件方式存放在硬盘空间上的永久性cookie.持久cookie是值存放在客户端硬盘中的cookie信息(被设置了一段有效期)
- 会话型cookie
- 停留在浏览器内存中的临时cookie.会话型cookie.仅在会话期间存在,浏览器一旦关闭,会话型cookie就会被销毁
Cookie中的属性
- Domain:指定Cookie会被哪些域名下的页面访问
- Path:指定Cookie可以被哪些路径下的页面访问
- Secure:若设置为true,则只有在HTTPS连接下才会发送Cookies,增加了安全性
- HttpOnly:若设置为true,则Cookie不能被
JavaScript
访问,有利于防止跨站脚本攻击 - SameSite:设置Cookie是否随着第三方请求一起发送,有助于防止跨站伪造攻击
JavaWeb对Cookie的操作
Java中将Cookie操作封装到
javax.servlet.http.Cookie
类中,
Cookie对象的创建
new Cookie(String name, String value)
:设置Cookie的名称与值- name:是Cookie的唯一标识,通过name可以区分不同的Cookie对象,一个网站可能有多个Cookie如
username,language,
等等 - value:用于存储实际的数据,通过cookie值可以跟踪用户状态
- name:是Cookie的唯一标识,通过name可以区分不同的Cookie对象,一个网站可能有多个Cookie如
Java中常用的Cookie的属性
属 性 名 | 描 述 |
---|---|
String name |
该Cookie的名称。Cookie一旦创建,名称便不可更改 |
String value |
该Cookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码 |
int maxAge |
该Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1 |
boolean secure |
该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false |
String path |
该Cookie的使用路径。如果设置为“/sessionWeb/”,则只有contextPath为“/sessionWeb”的程序可以访问该Cookie。如果设置为“/”,则本域名下contextPath都可以访问该Cookie。注意最后一个字符必须为“/” |
String domain |
可以访问该Cookie的域名。如果设置为“.google.com”,则所有以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.” |
String comment |
该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明 |
int version |
该Cookie使用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范 |
Cookie的有效期
Cookie的maxAge决定着Cookie的有效期,单位为秒(second),Cookie中的
getMaxAge()
与setMaxAge(int maxAge)
可以用来读写maxAge
属性
- 若
maxAge
为正数,则表示该Cookie会在maxAge秒后失效,浏览器会将maxAge为正数的Cookie持久化,即写入到对应的Cookie文件中.无论客户关闭了浏览器还是电脑,只要在maxAge之内,访问相应的网站仍然有效,
Cookie cookie = new Cookie("username","张三");//新建cookie
cookie.setMaxAge(Integer.MAX_VALUE);//设置生命时间为无限
resp.addCookie(cookie);//响应到客户端
//此方法添加的cookie信息永远生效
- 若
maxAge
为负数时则表示此cookie只在该浏览器窗口以及期子窗口生效,关闭浏览器即失效,maxAge
为负数的Cookie为临时Cookie,不会被持久化,即不会被写入到文件中,Cookie信息只保留在浏览器的内存中,Cookie的默认值为-1,即默认为会话Cookie - 若
maxAge
为0时则表示,删除该Cookie.Cookie的机制没有提供删除Cookie的方法,因此可以提供设置maxAge
为0的方法即时失效,实现删除Cookie的效果,失效的Cookie会被浏览器从文件或内存中清除
Cookie cookie = new Cookie("username","李四");//创建cookie
cookie.setMaxAge(0);//设置为0,即时失效
resp.addCookie(cookie);//相应到客户端
Cookie的修改
- Cookie本身并不提供修改的操作,若要修改一个Cookie,则需要新建一个同名的Cookie,添加到response中将原本的Cookie覆盖
- 注意点:修改时新建的Cookie除了
value,maxAge
之外的属性,例如name,path,domian
等等都要和原先的Cookie一致才能完成修改
- 注意点:修改时新建的Cookie除了
Cookie的不可跨域名性
Cookie的不可跨域名性表示的是:Cookie通常只能由创建它的域名所访问,这意味着一个域名下的Cookie不能被另一个域名下的Cookie访问
-
提供设置
Domain
属性是实现不可跨域名性:- 例如当把
Domian
设置为:.example.com
时,则该Cookie不仅可以在example.com域名上被访问,也可以在sub.example
上被访问
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie("username","李四");//创建cookie cookie.setMaxAge(Integer.MAX_VALUE); cookie.setHttpOnly(true);//不可被JavaScript访问 cookie.setSecure(true);//设置只能提供Http发送 cookie.setDomain(".example.com");//cookie可以被example.com及其子域名访问 resp.addCookie(cookie);//相应到客户端 }
- 例如当把
Session
Session
也是一种解决HTTP请求无状态性的技术,Session是服务器使用的一种记录客户端状态的机制,相比Cookie来说,Session更加简单,但增加了服务器的存储压力
Session的原理机制
Session是一种服务器端的机制,服务器使用一种哈希表的结构存储Session信息
Session的行为
- Session是依赖于Cookie的,用户客户端请求申请创建一个session→服务器首先检查客户端请求中是否包含了一个session的标识—
session id
若包含一个session id
则说明已经为该客户端创建过session,服务器会按照session id
检索出来直接使用,若检索不到则新建一个;若不包含session id
,则为此客户端新建一个session id
并生成相应的session id
→新建的session id会包含在cookie中随响应一起返回到客户端中保存
- 简单来说:如果是cookie是检查客户端携带的”通行证”,来确认客户.那么session就是通过检查服务器上的”客户端明细表”来确认客户端身份,session相对于在服务器上建立了一份客户档案.
Session的属性
与Cookie一致的是 Session的值也是以
key-value
的形式存在的,Java中把将Session的操作封装到javax.servlet.http.Session
类中
获取Session
- 使用 request中的
HttpSession getSession()
方法可以获取当前用户的Session,若该用户的Session不存在则返回null
- 进而衍生出了另一种传参的
getSession()
方法HttpSession getSession(boolean create);
当create为true时,该方法会新建一个Session,再讲Session返回
- 进而衍生出了另一种传参的
public class myServletTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
HttpSession session1 = req.getSession(true);
}
}
Session中常用的方法
方 法 名 | 描 述 |
---|---|
void setAttribute(String attribute, Object value) |
设置Session属性。value参数可以为任何Java Object。通常为Java Bean。value信息不宜过大 |
String getAttribute(String attribute) |
返回Session属性 |
Enumeration getAttributeNames() |
返回Session中存在的属性名 |
void removeAttribute(String attribute) |
移除Session属性 |
String getId() |
返回Session的ID。该ID由服务器自动创建,不会重复 |
long getCreationTime() |
返回Session的创建日期。返回类型为long,常被转化为Date类型,例如:Date createTime = new Date(session.get CreationTime()) |
long getLastAccessedTime() |
返回Session的最后活跃时间。返回类型为long |
int getMaxInactiveInterval() |
返回Session的超时时间。单位为秒。超过该时间没有访问,服务器认为该Session失效 |
void setMaxInactiveInterval(int second) |
设置Session的超时时间。单位为秒 |
void putValue(String attribute, Object value) |
不推荐的方法。已经被setAttribute(String attribute, Object Value)替代 |
Object getValue(String attribute) |
不被推荐的方法。已经被getAttribute(String attr)替代 |
boolean isNew() |
返回该Session是否是新创建的 |
void invalidate() |
使该Session失效 |
Session与Cookie的关系
-
Session实现的原理与Cookie有关
-
当服务器创建了Session对象后,首先将其存入服务器内存,并把Session的唯一标识session id以Cookie的形式写回客户端本地文件中(键名为JSESSIONID,值为该session的id)
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); Cookie cookie = new Cookie("JSESSIONID",session.getId()); cookie.setMaxAge(60*60); resp.addCookie(cookie); }
-
-
Session的存储结构
Session是以Map数据结构存储的key值为session id;value值为Session对象
protected Map<String, Session> sessions = new ConcurrentHashMap<>();
-
Session与Cookie的区别
- 安全性:
- Session:存储在服务器中,更加安全
- Cookie:存储在客户端,更容易受到攻击,可以设置
HttpOnly
,和Secure
属性来提高安全性
- 数据量:
- Session:可以存储较大的数据量,没有严格的大小限制
- Cookie:提出限制在4kb左右
- 应用场景:
- Session:用于跟踪用户状态,适合存储敏感信息,如身份验证等等
- Cookie:用于存储非敏感信息,适合短期保存数据
- 安全性:
Filter过滤器
Filter过滤器是
Servlet
技术中最实用的部分,Web开发人员通过Filter技术管理Web服务器的所有资源,如JSP,Servlet,HTML
等等,主要用于对客户请求进行预处理
Filter的主要功能
- 编码转换:在请求资源到达资源之前转换字符编码,确保字符编码的一致性
- 日志记录:在请求和响应中记录相关信息
- 预处理和后处理认任务:
- 修改请求头或响应头
- 添加缓存控制头
- 进行安全检查
Filter的配置
Filter
有两种配置方法,一种是在web.xml
文件中配置,第二种是使用注释的方法配置(Java Servlet 3.0 及以上版本)
web.xml配置
借鉴作者@coderland
<filter>
<filter-name>myFilter01</filter-name>//设置filter名称
<filter-class>com.mashang.web.myFilter01</filter-class>
//指定用于指定过滤器的完整类路径
</filter>
<filter-mapping>//设置Filter负责拦截的资源
<filter-name>myFilter01</filter-name>//需和filter-name一致
<url-pattern>/ *</url-pattern>//设置filter拦截的路径 URL结构
</filter-mapping>
注释配置
@WebFilter()
- 在
Filter
实现类,顶部使用@WebFilter()
进行注释 - @WebFilter()中的属性
String filterName() default "";
:指定Filter名称String[] urlPatterns() default {};
:指定Filter拦截的URL,使用/*
表示所有URL都会经过此Filter-
由于
urlPatterns
以字符串数组存在,因此可以一次配置多条路径,例如@WebFilter(filterName = "MyFilter", urlPatterns = {"/secure/*", "/admin/*"})
-
@WebInitParam()
@WebInitParam注释可用于在@WebFilter中配置参数初始值
WebInitParam[] initParams() default {};
:是@WebFilter中的参数类别,默认为空值,表示没有被初始化- 其有两个参数:
name
:参数名称;value
:参数值
- 其有两个参数:
@WebFilter(filterName = "myFilter01",urlPatterns = "/*",
initParams ={
@WebInitParam(name = "encoding",value = "UTF-8")
//将其中的encoding 属性配置为UTF-8,来实现编码统一
}
)
public class myFilter01 implements Filter { }
Filter的生命周期
Filter是Java的一个接口,其有一个简单的生命周期,
void init(FilterConfig config)
- 与Servlet程序一致,Filter的创建和销毁都是由Web服务器操作的,Web服务器启动时创建Filter对象,并调用
init()
方法完成初始化工作,读取web.xml
文件,Filter对象只会创建一次,因此init()
方法只会执行一次, FilterConfig
是 Java Servlet API 中的一个接口,它提供了获取Filter
配置信息的方法,在下文我们会展开讲解
- 与Servlet程序一致,Filter的创建和销毁都是由Web服务器操作的,Web服务器启动时创建Filter对象,并调用
- **
void doFilter(ServletRequest serReq, ServletResponse** **serResp, FilterChain filterChain)**
- 完成实际过滤操作的方法.Web服务器会在每次调用service()方法之前调用Filter的
doFilter()
方法 FilterChain
:Filter链,在开发中会编写多个Filter,FilterChain
是Filter的集合,如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源
- 完成实际过滤操作的方法.Web服务器会在每次调用service()方法之前调用Filter的
void destroy()
- Filter对象创建后会驻留在Web内存中,当Web用于结束服务器停止时,Web服务器销毁之前的对象
- 与init()一致的是destroy()也只调用一次
FilterConfig接口
当Filter被初始化时,FilterConfig对象会被传递给
init()
方法,在init()
中通过FilterConfig,可以回去Filter的名称,初始化参数,及相关的ServletContext
FilterConfig主要方法
-
String getFilterName():
返回Filter的名称 -
String getIntiParameter(String name)
:获取指定名称的初始参数的值,若没找到对应的初始化参数则返回null
-
Enumeration<String> getInitParameterNames()
:返回所有初始化参数名称, -
ServletContext getServletContext()
:返回与Filter关联的ServletContext
对象,ServletContext对象提供了整个Web应用程序的访问public void init(FilterConfig filterConfig) throws ServletException { //获取FilterConfig中的所有参数名 Enumeration<String> initParameterNames = filterConfig.getInitParameterNames(); while (initParameterNames.hasMoreElements()) { String name = initParameterNames.nextElement(); System.out.println("param"+":"+name); } ServletContext servletContext = filterConfig.getServletContext(); servletContext.setAttribute("name","value"); }
用Filter实现编码统一
@WebFilter(urlPatterns = "/*")
public class myFilter01 implements Filter {
private String characterEncoding=null;
@Override
public void init(FilterConfig fC) throws ServletException {
//在初始定义中获取characterEncoding
//若为非空,encoding也是非空 则直接获取 encoding的值
if (fC != null && fC.getInitParameter("encoding") !=null
&& ! fC.getInitParameter("encoding").equals(""))
{
characterEncoding=fC.getInitParameter("encoding");
}
else{
characterEncoding="UTF-8";//否则置为utf8
}
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//将Servlet转换为HttpServlet用于处理HTTP请求响应
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
//拦截所有请求进行统一编号
//指定request和response的编号
req.setCharacterEncoding(characterEncoding);
//将response数据响应为utf8
resp.setCharacterEncoding(characterEncoding);
//告诉浏览器输出内容为HTML格式,
resp.setContentType("text/html;charset="+characterEncoding);
filterChain.doFilter(req,resp);//将请求和响应传递给下一个 Filter 或目标资源
}
Listener
Listener 监听器与Servlet 程序,Filter 过滤器共称JavaWeb的三大组件,Listener作用
application,session,request
三个对象中,Listener用于监听特点的时事件,然后回调函数,并做出相应的反应,Listner主要分为三个大类:ServletContext 监听,Session监听,Request 监听;Listener本质是个接口
Listener的分类及使用
ServletContext监听
ServletContext监听是由
ServletContextListener
和ServletContextAttributeListener
接口实现的,
ServletContextListener方法
对整个Servlet上下文进行监听(创建或销毁)
public void contextInitialized(ServletContextEvent sce);
:执行初始化任务,如加载缓存等等public void contextDestroyed(ServletContextEvent sce);
:停止时执行清除任务,释放资源,关闭连接等等- ServletContextEvent对象的操作:
public ServletContext getServletContext();
:取得一个ServletContext(application)对象
ServletContextAttributeListener方法
对Servlet上下文属性的监听(增删改属性)
public void attributeAdded(ServletContextAttributeEvent scab);
:向ServletContext中添加属性public void attributeRemoved(ServletContextAttributeEvent scab);
:删除ServletContext中的属性public void attributeRepalced(ServletContextAttributeEvent scab);
:替换SevletContext中的属性,(重复设置无效果)- ServletContextAttributeEvent对象的操作:能获取属性名于值
public String getName();
:获取属性名public Object getValue();
:获取属性的值
Session监听
于
ServletContext
类似,Session也分为两种接口,分别为HttpSessionListener
和HttpSessionAttributeListener
HttpSessionListener方法
对Session整体状态的监听
public void sessionCreated(HttpSessionEvent se);
:创建Sessionpublic void sessionDestroyed(HttpSessionEvent se);
:销毁Session- HttpSessionEvent对象的操作:
public HttpSession getSession();
:取得当前操作的session
HttpSessionAttributeListener方法
对Session属性的监听
public void attributeAdded(HttpSessionBindingEvent se);
:添加属性到HttpSession
中public void attributeRemoved(HttpSessionBindingEvent se);
:将一个属性从HttpSession
中删除public void attributeReplaced(HttpSessionBindingEvent se);
:替换HttpSession
中的属性- HttpSessionBindingEvent对象的操作
public String getName();
:取得属性的名public Object getValue();
:取得属性的值public HttpSession getSession();:
取得当前的session
Request监听
Request监听分为
ServletRequestListener
和ServletRequestAttributeListener
ServletRequestListener方法
public void requestInitialized(ServletRequestEvent sre);
:request初始化public void requestDestroyed(ServletRequestEvent sre);
:request销毁- ServletRequestEvent对象的操作
public ServletRequest getServletRequest();
:取得一个ServletRequest对象public ServletContext getServletContext();
:取得一个ServletContext(application)对象
ServletRequestAttributeListener方法
public void attributeAdded(ServletRequestAttributeEvent srae);
:往当前ServletRequest
对象中增加属性public void attributeRemoved(ServletRequestAttributeEvent srae);
往当前ServletRequest
对象中删除属性public void attributeReplaced(ServletRequestAttributeEvent srae);
属性替换(第二次设置同一属性)- ServletRequestAttributeEvent对象操作
public String getName();
:得到属性名称public Object getValue();
:取得属性的值
Listener的配置
-
web.xm配置
原理于Servlet,Filter大同小异,不展开讲解
<listener> <listener-class>com.listener.class</listener-class> </listener>
-
注释@WebListener
- 往Listener实现类顶部加上
@WebListener
注释即可
@WebListener public class myListener01 implements HttpSessionAttributeListener {}
- 往Listener实现类顶部加上
Listener的实例应用
使用HttpSessionListener统计最大在线人数
@WebListener
public class myListener01 implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent event) {
//使用Session获取ServletContext对象
ServletContext app = event.getSession().getServletContext();
//获取Servlet中的count属性
int count = (int) app.getAttribute("onLineCount");
count++;//每次创建一个Session Count就会+1 用来计数
app.setAttribute("onLineCount",count);
int maxOnLineCount= (int) app.getAttribute("maxOnLineCount");
//若count>maxOnLineCount 则将maxOnLineCount更新为count的值
if (count>maxOnLineCount)
{
app.setAttribute("maxOnLineCount",count);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
app.setAttribute("date",sdf.format(new Date()));
}
}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
//结束会话后会删除Session 即减少一位客户 count--
ServletContext app = event.getSession().getServletContext();
int count = (int)app.getAttribute("count");
count--;
app.setAttribute("count", count);
}
}