第一节 状态管理-Session
1.1 Session概述
1.1.1 什么是Session
Session用于跟踪客户的状态。Session指的是在一段时间内,单个客户与Web服务器的一连串相关的交互过程。
在一个Session中,客户可能会多次请求访问同一个网页,也有可能请求访问各种不同的服务器资源。
表示的是会话,session域中存的值,他的有效范围是一次会话,浏览器代开访问服务器到访问结束关闭浏览器这段时间,这就是使用session去解决无状态性。session他的是属于服务器的
1.1.2 Session工作原理
1.当浏览器第一发送请求给服务器时,那么服务器会在内存中开辟一个对象(session),通过hash算法得到这个对象的内存地址(eac123456),此时服务器会new cookie对象(new Cookie("JsessionId","eac123456")),将cookie添加到response中响应给浏览器进行存储。
2.当同一个浏览器第二次访问同一台服务器,根据cookie的自动发送原则,会将匹配到url的cookie封装到request请求中,那么此时服务器回去解析cookie,拿到JsessionId所对应的内存地址eac123456,根据内存地址找到原来new的对象(session对象),将对象关联给request
3.第三次,第四次以此类推
1.1.3 如何获得Session
//获取Session对象
HttpSession session=request.getSession();
System.out.println("Id:"+session.getId());//唯一标记,和jSessionId不一样
System.out.println("getLastAccessedTime:"+session.getLastAccessedTime());//最后一次访问时间,毫秒
System.out.println("getMaxInactiveInterval:"+session.getMaxInactiveInterval());//获取最大的空闲时间,单位秒
System.out.println("getCreationTime:"+session.getCreationTime());//获取Session的创建,单位毫秒
1.1.4 如何使用Session绑定对象
使用HttpSession的setAttribute(属性名,Object)方法
1.1.5 如何删除Session
使用HttpSession的invalidate方法
注意:如何理解session的作用范围是一次会话
1.session有着自己的生命周期,可以自己设定时长
2.是因为存放原来JsessionId的那个cookie的maxAge=-1;浏览器关闭了,这个cookie还能使用吗?
3.由于存放JsessionId的cookie被销毁类,所以你找不到原来的session对象类,但是那个对象还会在内存中存在
1.2 Session超时
1.2.1 什么是Session超时
HttpSession的最后一程访问时间和当前时间的差距大于了指定的最大空闲时间,这时服务器就会销毁Session对象。默认的空闲时间为30分钟。
1.2.2 如何修改Session的缺省时间限制
1 使用HttpSession的session.setMaxInactiveInterval(20*60);设置,单位秒
2 在web.xml中配置 ,单位分钟
<session-config>
<session-timeout>20</session-timeout>
</session-config>
1.2.3 Session失效的几种情况
1、超过了设置的超时时间
2、主动调用了invalidate方法
3、服务器主动或异常关闭
注意:浏览器关闭并不会让Session失效
注意的点:session失效不失session对象被销毁了,他还在内存中,只是不能用了。
监听器去解决
总结:
1.session的工作原理
2.如何去理解session的作用范围是一次会话
3.sesssio这个对象本质是一个键值对的容器,通过get/setAttribute方法可以往容器中放值取值,这些值的作用范围是一次会话,主要应用,整个web去存放登录信息
2.4 Session的典型应用
2.4.1 验证登录
记得导jar包:
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2020/2/27
Time: 11:45
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<body>
<form action="login" method="post">
<label>邮箱:</label><input name="email"><br/>
<label>密码:</label><input type="password" name="psw"><br/>
<label>验证码:</label><input type="text" name="code"><br/>
<img src="code" id="img">
<input type="submit" value="登录">
</form>
<script type="text/javascript">
var img=document.getElementById("img");
//这是浏览器的一个缓存问题,静态资源的缓存问题
//表现形式:当链接地址没有发生变化时,他不会去发送请求
var num=1;
img.οnclick=function () {
num++;//没有实际的意义,只是去迷惑浏览器,使其认为这不是同一个请求
img.src="code?num="+num;
}
</script>
</body>
</html>
package com.qf.controller;
import cn.dsna.util.images.ValidateCode;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// public ValidateCode(int width, int height, int codeCount, int lineCount)
//width:验证码图片的宽度
//height:验证码图片的高度
//codeCount:验证码的个数
//lineCount:文字行高
ValidateCode vc=new ValidateCode(200,30,4,10);
System.out.println("验证码为"+vc.getCode()+"======================================");
//当同一个session对象重复添加键时,新加的会把原来的覆盖掉
request.getSession().setAttribute("code",vc.getCode());
//将图片通过响应的输出流响应给浏览器展示
vc.write(response.getOutputStream());
}
}
package com.qf.controller;
import com.qf.model.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
String email = request.getParameter("email");
String psw = request.getParameter("psw");
String code = request.getParameter("code");
//想办法拿到原来生成的code,在session里面
String trueCode= (String) request.getSession().getAttribute("code");
if(trueCode.equals(code)){
//模拟去数据库查
//User u=service.login();
User u=new User("zhangsan@qq.com","1234");
if(u!=null){
//登录成功之后一定要记得将登录信息放到session域中
request.getSession().setAttribute("user",u);
response.sendRedirect("index.jsp");
}else{
response.getWriter().println("密码失败");
response.sendRedirect("error.jsp");
}
}else{
response.getWriter().println("验证码失败");
response.sendRedirect("error.jsp");
}
}
}
第二节 过滤器
2.1 什么是过滤器
Filter也称之为过滤器,它是Servlet技术中最激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。
2.2 如何编写过滤器
1、编写java类实现Filter接口
2、重写doFilter方法
3、设置拦截的url
入门案例:
package com.qf.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author wgy 2018/11/28 9:23
* @version 1.0
*/
@WebFilter("/myservlet1")//过滤路径
public class MyFilter1 implements Filter {
//初始化过滤器
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化了........init... "+filterConfig);
}
//执行过滤
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("过滤前........doFilter ");
//放行
chain.doFilter(request, response);
System.out.println("过滤后.......doFilter");
}
//销毁
@Override
public void destroy() {
System.out.println("销毁了.....destroy");
}
}
2.3 过滤器的配置
2.3.1 注解式配置
在自定义的Filter类上使用注解@WebFilter(“/*”)
2.3.2 xml配置
在web.xml中进行过滤器的配置:
<!--过滤器的xml配置 -->
<filter>
<!--名称-->
<filter-name>sf</filter-name>
<!--过滤器类全称-->
<filter-class>com.qf.web.filter.SecondFilter</filter-class>
</filter>
<!--映射路径配置-->
<filter-mapping>
<!--名称-->
<filter-name>sf</filter-name>
<!--过滤的url匹配规则和Servlet的一模一样-->
<url-pattern>/*</url-pattern>
</filter-mapping>
2.4 过滤器链
(在资源放行前,过滤请求,在资源放行后,过滤响应,多个过滤器之间过滤成链)
通常客户端对服务器请求之后,服务器调用Servlet之前会执行一组过滤器(多个过滤器),那么这组过滤器就称为一条过滤器链。
每个过滤器实现某个特定的功能,一个过滤器检测多个Servlet。(匹配几个,检测几个)。
一组过滤器中的执行顺序与<filter-mapping>的配置顺序呢有关。
当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源
2.5 过滤器的优先级
在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter。当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源
如果为注解的话,是按照类名的字符串顺序进行起作用的
如果web.xml,按照 filter-mapping注册顺序,从上往下
web.xml配置高于注解方式
推荐使用xml方式进行配置
2.6 过滤器的初始化参数
在过滤器的创建的时候,可以传递初始化参数
第一种:基于注解的
/**
* Servlet Filter implementation class FirstFilter 创建过滤器
*/
@WebFilter(value="/*",initParams= {@WebInitParam(name = "version", value = "1.0")})
public class FirstFilter implements Filter {
/**
* Default constructor.
*/
public FirstFilter() {
// TODO Auto-generated constructor stub
}
/**
* @see Filter#destroy() 销毁
*/
public void destroy() {
// TODO Auto-generated method stub
System.out.println("destroy销毁……");
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) 过滤
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
// place your code here
System.out.println("doFilter……过滤");
// 是否继续---访问下一个
chain.doFilter(request, response);
}
/**
* @see Filter#init(FilterConfig)
* 初始化
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
System.out.println("init……初始化");
System.out.println("初始化参数:版本号:"+fConfig.getInitParameter("version"));
}
}
第二种:基于xml配置
/**
* 创建过滤器
*/
public class SecondFilter implements Filter {
/**
* Default constructor.
*/
public SecondFilter() {
// TODO Auto-generated constructor stub
}
/**
* @see Filter#destroy() 销毁
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) 过滤
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 是否继续---访问下一个
chain.doFilter(request, response);
}
/**
* @see Filter#init(FilterConfig)
* 初始化
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
System.out.println("初始化参数:版本号:"+fConfig.getInitParameter("version"));
}
}
Web.xml实现配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
<display-name>Web_Day</display-name>
<!--过滤器的xml配置 -->
<filter>
<filter-name>myfilter</filter-name>
<filter-class>com.qf.web.filter.SecondFilter</filter-class>
<!--过滤器的初始化参数 -->
<init-param>
<param-name>version</param-name>
<param-value>1.0</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myfilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
2.7 过滤器的优点
可以实现 Web 应用程序中的预处理和后期处理逻辑
chain.doFilter(req, resp);//资源放行
如果不去执行这一句话,那么请求就会被拦截,不再继续往下走
在资源放行的前后,我们可以对请求进行处理和对响应进行处理
2.8 过滤器的典型应用
过滤器的作用来看
1.过滤所有的请求,因为他是在servlet.jsp.html等资源执行之前执行,可以类似封装的作用,比如处理乱码
2.可以在资源放行之前,对request进行处理,过滤敏感词
3.我们可以在资源放行之后,对资源响应给浏览器的response进行相关处理:压缩响应内容
案例1禁用浏览器缓存
web对静态资源有缓存作用(img,html,js,css)
对于目前现在的浏览器,get请求动态资源缓存问题已经解决。
max-age<=0 时 向server 发送http 请求确认 ,该资源是否有修改
对于静态资源部分浏览器使用Cache-Control头和Expires头设置缓存时间。
对于静态资源服务器会采用304状态码控制是否再次发送数据,从而节省带宽;可以通过Cache-Control=no-store,Expires=-1,Pragma=no-cache控制304无效。
package com.qf.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CacheFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//将req转换成HttpServletRequest
HttpServletRequest request= (HttpServletRequest) req;
HttpServletResponse response= (HttpServletResponse) resp;
//通过以下参数的设置,可以禁用浏览器缓存
response.setDateHeader("Expires",-1);
response.setHeader("Cache-Control","no-store");
response.setHeader("Pragma","no-cache");
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
//获取初始化参数
String driverClassName = config.getInitParameter("driverClassName");
System.out.println(driverClassName+"============================================");
}
}
案例2 自动登录
案例3 过滤脏词
案例4 过滤器解决编码
案例五:对响应内容进行压缩