servlet第二部分
- Cookie
- HttpSession
- 什么是session?
- 创建session
- 会话控制流程
- 管理session有效时间
- 关于浏览器禁用cookie之后的处理 --> url重写
- 关于JSESSIONID,HttpSession的创建时机
- 关闭服务器保持session
- 关闭浏览器保持session
- session实例--用户的登入登出
- Filter
- Filter的基本概念
- Filter的使用
- Filter的执行流程/原理
- Filter的生命周期
- urlpattern的配置
- FilterConfig对象
- Filter 根据request参数拦截部分请求
- Filter中的``
- Filter拦截器链
- Filter实例 --> 权限验证
- Listener
Cookie
cookie基本概念
概念
cookie是保存在浏览器客户端的简单的文本文件,用于存储web服务器下发的简单数据。不超过4K大小,每个应用不超过200条cookie键值对。浏览器每次访问web应用,都会把该应用对应的cookie记录全部带上。如果是自己实现的http客户端,需要自己去实现。
场景
cookie相当于理发店的会员卡ID,客户每次去理发店,给前台人员报出会员卡ID,后台人员根据这个会员卡ID,去柜子里面查找对应的会员卡信息,比如柜子某一页记录页对应这个会员卡ID,记录页面上记录了办卡人姓名,年龄,充值了多少钱等。
组成
- key-value :
设置Cookie的名称和值,用于http请求时,提交给服务器调端该用户的信息。 - Expires :
设置Cookie的生存事件。Cookie分为持久型和会话型,如果不设置expires,则cookie为会话型,存储于内存之中,浏览器关闭则消失;如果将expires设置为负数,则表示浏览器端收到cookie之后,直接丢弃;如果将expires设置为0,则表示浏览器端如果存储有该cookie,删除原有cookie,当然新的也不会进行存储;如果expires设置为正数,则cookie会持久性,浏览器关闭的时候,不会消失,时间到了就自动删除。 - Path :
定义web站点上可以访问该cookie的目录,用于不同的访问path,传递不同的cookie,减少cookie的携带量。 - Domian :
指定可以访问该cookie的web站点或者域。允许一个子域设置或者访问父域的cookie。(比如单点登录的cookie共享) - Secure :
指定是否使用https协议发送cookie。 - HttpOnly :
用于防止客户端js通过Document.cookie属性访问cookie,但是部分浏览器未遵守此规定。且大多数浏览器允许通过XMLHTTP对象读取HTTP响应头中的Set-Cookie头。
HTTP请求与响应中cookie存在的形式
-
HTTP请求的cookie的形式。一个’cookie’字段。
-
HTTP响应中的cookie的形式。多个’Set-Cookie’字段。
cookie的前后端使用
cookie的后端使用
- 创建cookie,并且设置到响应头传给浏览器
#每一个key-value,都对应一个Cookie实例。
#创建cookie,并设置key-value
Cookie cookie = new Cookie("username", "meng");
//设置过期时间Expires
response.setMaxAge(10000);
//设置path
response.setPath("/test");
//添加到响应头,http发送响应请求的时候会返回给浏览器
response.addCookie(cookie);
Cookie cookie2 = new Cookie("password", "123456");
response.addCookie(cookie2);
- 获取cookie(浏览器每次http请求默认携带该web所有的cookie)
请求体HttpServletRequest只有cookie的key-value,没有其他属性,如path,expires等
Cookie[] cookieList = request.getCookies();
for( Cookie cookie : cookieList ){
System.out.println( key + " : "+ value);
}
- 修改cookie
如果要修改浏览器中存储的cookie,只需要发送同名key
就行,浏览器会保证key的唯一,用新value覆盖就value。
#浏览器接收到已经存储的key-value, 则直接覆盖。
#对于服务器而言,修改浏览器的cookie操作与新增一样。
Cookie cookie2 = new Cookie("password", "1234567890");
response.addCookie(cookie2);
- 删除cookie
当浏览器接收到expires为0的cookie时,会根据key去查找已存储的cookie,如果存在,则删除。当然expires为0的cookie也不会被保存。
Cookie cookie2 = new Cookie("password", "1234567890");
cookie2.setMaxAge(0);
response.addCookie(cookie2);
cookie不能使用中文以及加密处理
cookie不能存储中文问题处理
- 编码存储cookie中的中文
Cookie cookie = new Cookie("password3", URLEncoder.encode("这就是中文", "UTF-8"));
cookie.setPath("/test");
cookie.setMaxAge(10000);
cookie.setHttpOnly(true);
resp.addCookie(cookie);
- 编码获取cookie中的中文
Cookie[] cookies = req.getCookies();
if ( cookies != null ){
for ( Cookie cookie : cookies ){
System.out.println(cookie.getName() +" : "+ cookie.getPath() + " : "+ URLDecoder.decode(cookie.getValue(),"utf-8"));
}
}
cookie对于重要内容的加密处理
以后实际项目中有使用再添加。。。
cookie的前端使用
判断cookie是否开启
#BOM navigator对象提供的属性
if ( !navigator.cookieEnabled ){
console.log("cookie被禁止");
}
关于使用cookie进行存取来判断cookie是否开启,容易误判
- cookie的httpOnly属性,用于告诉浏览器cookie不允许被js代码存取,但是有部分浏览器不允许读,但是允许写。所以导致cookie虽然开启可用,但是通过cookie进行存取判断cookie是没有开启的。
获取cookie
- document.cookie是获取所有cookie键值对。
- #document.cookie只能获取到path为"/“下的cookie,其他的获取不到。测试过程中,有两条cookie,一条没设置path(“username”:“meng”),一条设置path为”/test" (”password“,:“123456”), 虽然我访问的网页为 http://localhost:8080/test/index.html,但是js获取的cookie只显示(“username”:“mengze”),没有(“password”:“123456”)。但是在请求该网页的请求头中看到了这两条cookie。
var cookie = document.cookie;
console.log(cookie)
修改cookie
- document.cookie=xxx 是以追加的形式添加cookie,如果不是同名key,那么不会覆盖之前的cookie内容,而是追加。当然,如果是同名key,那么就是覆盖了。
console.log(document.cookie);
document.cookie = "username1=meng123;expires=30000;path=/";
console.log(document.cookie);
增加cookie
- 增加与修改类似,同名key就直接覆盖,不同名的key就追加。
document.cookie = "username1=meng123;expires=30000;path=/";
删除cookie
- 使用chrome测试cookie删除,无效。
XMLHttpRequest在setRequestHeader设置cookie无效
request.setRequestHeader("cookie", "name: 111");
关于cookie相关的安全问题与防御
HttpSession
什么是session?
如果只是用cookie来证明用户身份,那么cookie需要存储大量的信息,且这些信息还不一定安全可靠。Session是另一种记录用户状态的机制。不同于cookie保存在客户端浏览器中,而是保存在服务器中。通过在浏览器中保存一个cookie(“JSESSIONID”,“xxxxxx”)来作为一个用户标识,然后在服务器中获取SESSIONID对应的Session,然后从session拿去之前存储的用户信息。
创建session
servlet中创建session
#调用无参的或者参数为true的getSession(),如果当前用户对应的session不存在,那么就创建一个新session。如果存在session,就返回原有的session。可以通过session.isNew()判断session是新建的,还是旧的。
#调用参数为false的getSession(),如果当前用户对应的session不存在,那么就返回false。如果存在session,就返回原有的session。
httpServletRequest.getSession(); //相当于参数为true。
httpservletRequest.getSession(Boolean);
jsp中创建session
- jsp页面默认开启session,也就是说只要访问了该页面,就会创建session。
会话控制流程
- 用户访问服务器时 未携带JSESSIONID="xxxxx"的cookie , 此时如果存在getSession(),或者getSession(true), 或者访问的jsp页面开启了session。那么就会创建对应该用户的httpSession,并且将sessionID通过cookie形式发送给浏览器,并且以 JSESSIONID="xxxx"形式存在。
- 用户访问服务器时 携带JSESSIONID="xxxxx"的cookie, 那么服务器如果能找到session(当然如果session有效期过了,那么也需要重新创建session),则直接返回存在的session。在JESSIONID-Session能对应的情形下,永远是同一个session对象。
#
HttpSession session = req.getSession(false);
if ( session == null ){
//session没有创建,创建session
}else {
//session存在,可以直接使用
}
//一般是在登录的时候创建httpSession
HttpSession session = req.getSession();
if ( session.isNew() ){
//表明session是新建的,并没有存储任何数据。
}else {
//表明session是旧的。
}
管理session有效时间
获取session的有效时间
//查看session的有效时间, 以秒钟为单位
long timeout = session.getMaxInactiveInterval();
System.out.println(timeout);
tomcat中session默认的有效时间与修改
#tomcat默认时间为1800秒,可以去tomcat中的conf目录的we'b.xml中看到。
#也可以在自己项目中的we'b.xml中配置
<session-config>
<session-timeout>30</session-timeout> #这个时间单位是分钟。
</session-config>
代码形式修改session的有效时间
//设置session的有效时间
session.setMaxInactiveInterval(10);
强制session失效(一般用于登出)
- session.invalidate()函数,会清空当前session中所有存储的对象,并且会将该session设置为无效。下次有该session对应的JESSIONID传过来时,不会再获取到该session。
session.invalidate();
关于浏览器禁用cookie之后的处理 --> url重写
如果cookie不可存取,那么浏览器就不会在http请求时自动携带jsessionid。这个时候,可以在URL上携带jsessionid。一样能被tomcat解析到。
原理:
- 针对前后端分离的网站或者APP,只要将sessionID存储在本地,然后手动设置请求头的cookie就行。对于网页的ajax设置cookie失效的情形,采用在URL上携带( ;jsessionid=xxxxxxx )就行。
#在URL上携带( ;jsessionid=xxxxxxx ), 也能让tomcat获取到jessionid,并且获取到对应的session。
http://localhost:8081/test/hello;jsessionid=DEF0234640EA834D79038F6FE66E8A0C
URL重定向处理
#非重定向的url处理(一般是动态网页处理<a>, <form>等上面的请求URL)
String uri = "otherServlet";
uri = response.encodeURL(uri);
out.println("<a href='" + uri + "'>跳转</a>到otherServlet");
#重定向的URL处理
String url = request.getCotnextPaht() + "/otherServlet";
uri = response.encodeRedirectURL(uri);
response.sendfRedirect(uri);
关于JSESSIONID,HttpSession的创建时机
JSESSIONID就是tomcat获取用户对应HttpSession的cookie。
HttpSession创建时间
- tomcat中HttpSession是在 servlet中调用 httpServletRequest.getSession() 或者 httpServletRequest.getSession(true), 或者JSP中配置了开启session时创建。
- 有些网站在用户还没登录就创建很多HttpSession的原因,一般就是JSP页面默认开启的session,导致用户还未登录就创建了session。
- 当然,这里要说的就是,服务器不是在启动时就创建HttpSession的,而是在需要的时候(比如调用getSession方法)。
- 另外,session对应的JSESSIONID也不是第一次访问web站点就会返回给浏览器的。是在httpSession创建之后的那次服务器响应返回后,带给浏览器的。
JSESSIONID创建时间
- 浏览器cookie中存的JSESSIONID,并不是第一次访问网页的时候就返回并存储的。而是当服务器当为该用户创建了对应的httpSession实例的时候,响应头中’Set-Cookie’中携带的。(也就是说会员卡并不是你第一次去店里就创建的,而是当你愿意办理的时候才办理的,然后在办理了会员卡之后,下次再来的时候,你才有卡号可以出示,以及能查到你对应的会员卡以及详细信息。)
关闭服务器保持session
钝化与活化
- 现象
服务器关闭后重新启动,只要浏览器没关,还是能获取session里面的内容。
- 钝化
服务器关闭以后,会将session对象序列化保存在磁盘中。可以在tomcat的work/当前项目下观察到SESSION.SER文件。
- 活化
服务器再次启动时,把之前的序列化号的文件加载进来,就会再次加载之前保存的session。(SESSION.SER包含了session域中所有内容)。当然, 如果session存储的对象没有实现序列化接口,将不会被序列化保存。
关闭浏览器保持session
- session创建时,默认30分钟有效时间。同时,返回的sessionid没有设置expires,也就说,是会话型cookie,浏览器关闭该jsessionid就会消失。
- 可以采用以下方式来创建持久性的关于JSESSIONID的cookie。
#手动获取sessionid,然后创建cookie('jsessionid', sessionid),然后设置expires,这样浏览器中的JESSIONID内容的cookie就是持久性的了。
HttpSession session = request.getSession();
String id = session.getId();
Cookie cookie = new Cookie('jsessionid', id);
cookie.setMaxAge(60*60*24);
response.addCookie(cookie);
session实例–用户的登入登出
#登录 -- 将用户信息存放到session中
resp.setContentType("text/json;charset=utf-8");
//登录处理
String username = req.getParameter("username");
String password = req.getParameter("password");
if( "meng".equals(username) && password.equals("123456")){
//账号密码正确,则登录成功
HttpSession session = req.getSession();
session.setAttribute("username", username);
resp.getWriter().write("登录成功");
}else {
resp.getWriter().write("登录失败");
}
#登出 -- 将用户信息移除session,且作废该session。
resp.setContentType("text/json;charset=utf-8");
HttpSession session = req.getSession(false);
if ( session != null ){
session.invalidate();
}
resp.getWriter().write("退出成功");
Filter
Filter的基本概念
Filter是web服务器的三大组件(Servlet, Filter, Listener)之一.用于用户请求在到达servlet之前, 以及服务器响应到达浏览器之前。进行请求拦截或者功能增强,或者响应增强。
Filter的使用
web.xml中配置Filter
<filter>
<filter-name>filterB</filter-name>
<filter-class>com.mz.BFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filterB</filter-name>
<url-pattern>/hello</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
实现Filter接口
public class HelloFilter implements Filter {
private FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String uri = ((HttpServletRequest) servletRequest).getRequestURI();
if ( uri.endsWith(".jsp") ){
System.out.println("拦截此次请求");
}else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
public void destroy() {
}
}
Filter的执行流程/原理
在执行Servlet的前后,进行了请求拦截,就响应拦截处理。
作用:
- Filter可以在请求到达目标资源(JSP、Servlet、Html、CSS)之前拦截请求,并作相应处理。
- Filter可以放行请求,使请求到达目标资源。
- Filter可以在响应到达浏览器之前做一些处理。
Filter的生命周期
单例多线程形式存在。
- Servlet容器启动,Filter就创建并且初始化。在容器中以单例多线程形式存在。
- 每次用户请求都执行doFilter方法。
- Servlet容器停止后,Filter销毁。
urlpattern的配置
1、精确匹配
- 要拦截的资源的详细路径。需要请求路径的字符串完全相等。比如/a.jsp; /b/c.jsp;
2、路径匹配
- 拦截指定路径下的所有请求。 比如 /a/*
/* 表示拦截所有请求。 /path/* 表示拦截/path路径下的所有请求。
3、后缀匹配
- 拦截所有以xxx后缀结尾的所有请求。
*.jsp
4、错误举例 /page/*.jsp
FilterConfig对象
Filter对象在创建并且初始化时,会调用init(FilterConfig):void方法,并且将该对象传递给Filter对象。 FilterConfig含有web.xml中中配置的初始化参数。以及ServletContext对象。
#web.xml中配置初始化参数
<filter>
<filter-name>filterB</filter-name>
<filter-class>com.mz.BFilter</filter-class>
<init-param>
<param-name>name</param-name>
<param-value>meng</param-value>
</init-param>
<init-param>
<param-name>age</param-name>
<param-value>18</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filterB</filter-name>
<url-pattern>/hello</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
#在FilterB中获取配置的初始化参数,以及ServletContext
public class BFilter implements Filter {
private FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
ServletContext servletContext = filterConfig.getServletContext();
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String name = this.filterConfig.getInitParameter("name");
String age = this.filterConfig.getInitParameter("age");
filterChain.doFilter(servletRequest, servletResponse);
}
public void destroy() {
}
}
Filter 根据request参数拦截部分请求
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String uri = ((HttpServletRequest) servletRequest).getRequestURI();
if ( uri.endsWith(".jsp") ){
System.out.println("拦截此次请求");
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
Filter中的<dispatcher>
Filter中<dispatcher>
有四个取值. 一个Filter可以配置多个<dispatcher>
.
REQUEST
:默认值,表示拦截用户的直接请求。FORWARD
:表示请求转发。INCLUDE
: 表示拦截包含页面。ERROR
:表示拦截web.xml中配置的错误页面。
<filter-mapping>
<filter-name>filterB</filter-name>
<url-pattern>/hello</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Filter拦截器链
对于同一个用户请求,可以配置多个Filter拦截器,从而形成拦截器链。用户请求在拦截器链串行执行,处理的顺序是web.xml中对应的Filter的配置顺序;服务器响应在拦截器链中处理的顺序则相反。比如配置了Filter A、B、C,那么用户请求来时,A->B->C, 服务器响应时,C->B->A。
Filter实例 --> 权限验证
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
servletResponse.setContentType("text/json;charset=utf-8");
//获取session,如果是存在该用户,就表示已经登录
//如果不存在session,那么就没有登录
HttpSession httpSession = req.getSession(false);
if (httpSession == null){
resp.getWriter().write("用户没有登录");
return;
}else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
Listener
Listener的基本概念
概念
- listener也就是监听器,用于监听一个特定的对象,当被监听的对象变化时,监听的对象会执行一系列动作。
作用:
- 用于三大域对象的创建销毁监听
ServletContextListener #监听ServletContext的创建与销毁
HttpSessionListener #监听Session的创建与失效
ServletRequestListener #监听ServletRequest的创建于销毁
- 三大域对象对属性增加、修改、删除监听;
ServletContextAttributeListener #监听ServletContext增加,删除,修改属性
HttpSessionAttributeListener #监听HttpSession增加,删除,修改属性
ServletRequestAttributeListener #监听ServletRequest增加,删除,修改属性
- 对某个属性在session中的增加,删除监听。
HttpSessionActivationListener #监听对象在session中的钝化与活化
HttpSessionBindingListener #监听对象被添加与删除
生命周期监听器
ServletContextListener
用于监听servletContext容器对象的创建与销毁。用于在用户访问之前,创建或者初始化必要的支持对象。比如spring容器对象的创建。
public class MyServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("web容器创建");
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("web容器销毁");
}
}
HttpSessionListener
用于监听HttpSession对象的创建与失效。 一个HttpSession代表一个用户,所以可以监听用户数量,会员数量,访问量,访问时间等。
- 关于httpSession的生命周期: httpSession是在调用getSession(),且参数不为false的情形下创建的;httpSession是在有效时间过期,或者强制失效的情况下销毁的。与web服务器的开启关闭没关系。(即使关闭,session也会被钝化,而不是被删除)
public class MySessionListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("有新的session创建");
}
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("有session销毁");
}
}
ServletRequestListener
用于监听ServletRequest(HttpServletRequest)的创建与监听。(请求转发过程中,一直是同一个ServletRequest)
public class MyServletRequestListener implements ServletRequestListener {
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
}
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
}
}
属性监听器
ServletContextAttributeListener
监听ServletContext的域中属性的添加,删除,修改。
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
}
public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
}
public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
}
}
HttpSessionAttributeListener
用于监听HttpSession域对象中属性的添加,删除,修改。
public class MySessionAttributeListener implements HttpSessionAttributeListener {
public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
}
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
}
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
}
}
ServletRequestAttributeListener
用于监听ServletRequest(HttpServletRequest)域中的属性增加,删除,修改。
public class MyServletRequestAttributeListener implements ServletRequestAttributeListener {
public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
}
public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {
}
public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {
}
}
session固有监听器
HttpSessionActivationListener 用于添加到session中的属性监听,如果钝化,激活事件发生,则会调用该属性实现的ActivationListener接口的方法。
public class UserInfo implements Serializable, HttpSessionActivationListener {
public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {
System.out.println("session中的userinfo即将钝化");
}
public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {
System.out.println("session中的userinfo已经激活");
}
}
HttpSessionBindingListener 监听session中某个属性的增加,删除。
public class UserInfo implements Serializable, HttpSessionBindingListener {
public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("该属性被添加到了session中");
}
public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("该属性从session中移除");
}
}