Filter过滤器
**作用:**拦截请求
当用户请求某个Servlet时,会先执行部署在这个请求上的Filter,如果Filter“放行”,那么会继承执行用户请求的Servlet;如果Filter不“放行”,那么就不会执行用户请求的Servlet。
其实可以这样理解,当用户请求某个Servlet时,Tomcat会去执行注册在这个请求上的Filter,然后是否“放行”由Filter来决定。可以理解为,Filter来决定是否能调用Servlet!当执行完成Servlet的代码后,还会执行Filter后面的代码。
使用过滤器
- 编写一个类实现javax.servlet.Filter接口
- 在web.xml文件或使用注解对过滤器进行配置, 配置该过滤器拦截哪些请求的url
FilterChain 过滤器链
doFilter()方法的参数中有一个类型为FilterChain的参数,它只有一个方法:doFilter(ServletRequest,ServletResponse)
。
前面我们说doFilter()方法的放行,让请求流访问目标资源!但这么说不严密,其实调用该方法的意思是,“我(当前Filter)”放行了,但不代表其他人(其他过滤器)也放行。
也就是说,一个目标资源上,可能部署了多个过滤器,就好比在你去北京的路上有多个打劫的匪人(过滤器),而其中第一伙匪人放行了,但不代表第二伙匪人也放行了,所以调用FilterChain类的doFilter()方法表示的是执行下一个过滤器的doFilter()方法,或者是执行目标资源!
如果当前过滤器是最后一个过滤器,那么调用chain.doFilter()方法表示执行目标资源,如果不是最后一个过滤器,那么chain.doFilter()表示执行下一个过滤器的doFilter()方法。
放行
filterChain.doFilter(servletRequest,servletResponse);
如果Filter没有显示调用上面这一行代码,表示该过滤器不放行
过滤器的执行顺序:
使用web.xml配置文件配置过滤器: 过滤器的配置文件决定过滤器执行顺序
由过滤器的<filter-mapper>
顺序决定的, 谁的<filter-mapper>
在前,谁先执行
在web3.0提供注解@WebFilter的配置方式:
使用注解的方式, 过滤器执行的顺序, 由过滤器的名字决定,排在前面的先执行。 当有多个过滤器时, 推荐使用web.xml配置
访问控制
有些资源,必须登录了才能访问 例如:增删改必须登录才能访问
案例 未登录时访问拦截
被拦截的url执行过滤器的doFilter方法
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 1.判断是否已经登录
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
HttpSession session = request.getSession();
if(session.getAttribute("admin")!=null){
// 已登录,放行
chain.doFilter(req, resp);
return;//不执行后续方法内代码
}
// 2.未登录,拦截,跳转到登录界面
String requestURI = request.getRequestURI();//用户想要访问的资源
session.setAttribute("requestPage",requestURI);
response.sendRedirect(request.getContextPath()+"/login.jsp");
}
注意点:
- ServletRequest的对象req没有request.getSession()方法,所以要将对象转换成它的子类HttpServletRequest
2.判断是否放行的依据是用户是否已经登录,所以LoginServlet处理时若登录成功要将登录用户保存到域里作为依据
3.若放行,注意方法后的代码不要执行了(使用return 或else语句)
4.如果用户未登录,拦截跳转到登录界面,这里需要注意,若用户登录成功,应该跳转到用户想要访问的资源而不是首页(不要写死登录成功后跳转的界面)
- 用户想要访问资源被拦截时,保存用户想要访问的资源到域里( request.getRequestURI()),登录成功后跳转到保存的这个资源
LoginServlet处理用户登录请求
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.设置编码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html,charset=utf-8");
String error ="";
HttpSession session = request.getSession();
// 2.获取数据
String username = request.getParameter("username");
String psaaword = request.getParameter("password");
// 3.调用service
AdminService adminService = BeanFactory.creatBean(AdminService.class);
Admin admin = adminService.login(username, psaaword);
//判断验证码是否正确
String verifycode = request.getParameter("verifycode");
String checkcode = session.getAttribute("CHECKCODE_SERVER").toString();
if(!checkcode.equalsIgnoreCase(verifycode)){
//不相等
error="验证码输入错误";
request.setAttribute("error",error);
request.getRequestDispatcher("/login.jsp").forward(request,response);
return;//验证码出错。不执行后续代码
}
//判断用户是否存在
if (admin != null) {
//5.把数据保存域中, admin保存到session域
session.setAttribute("admin",admin);
//用户是否勾选记住密码
String remenberPassword = request.getParameter("remenberPassword");
if ("rem".equals(remenberPassword)) {
//勾选
//创建用户名cookie, 密码cookie (加密)
Cookie nameCookie = new Cookie("username", username);
Cookie passwordCookie = new Cookie("password", psaaword);
//默认7天有效
nameCookie.setMaxAge(7 * 24 * 60 * 60);
passwordCookie.setMaxAge(7 * 24 * 60 * 60);
//保存到客户端浏览器
response.addCookie(nameCookie);
response.addCookie(passwordCookie);
} else {
//没有勾选, 判断之前是否保存账户密码的Cookie有就删除
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("username") || cookie.getName().equals("password")) {
//删除
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
}
}
// 登录成功 重定向
//从session获取到想访问的资源
Object requestPage = session.getAttribute("requestPage");
if(requestPage == null){
response.sendRedirect(request.getContextPath()+"/index.jsp");
}else{
response.sendRedirect(request.getContextPath()+requestPage);
//把session域中requestPage属性删除
session.removeAttribute("requestPage");
}
}else if(admin==null){
//登录失败
//5.把数据保存域中
error = "用户名或者密码错误";
request.setAttribute("error",error);
//6.页面跳转 转发: 把数据存在request域
request.getRequestDispatcher("/login.jsp").forward(request,response);
}
}
注意点:
用户如果是从被拦截后的登录页面登录成功的,那么登录成功之后记得要把session里的requestURI删除掉,否则这个session会在本次会话一直存在,有可能会导致下次登录时还是跳转到该页面
WEB-INF下的文件不能直接访问,只能通过转发访问,所以被拦截的资源文件夹可以放在WEB-INF中
四种拦截方式
REQUEST、FORWARD、INCLUDE、ERROR。
可以在<filter-mapping>
中添加0~n个<dispatcher>
子元素,来说明当前访问的拦截方式。
文件上传
这里的文件上传是小文件上传
大文件上传需要编写TCP进行大文件上传
实现:客户端把一个本地文件(图片,视频,音频,文本文件…) 上传到服务器, 保存在服务器中
文件上传对前端的要求
- 必须使用表单,而不能是超链接;
- 表单的method必须是POST,而不能是GET;
- 表单的enctype必须是multipart/form-data;
- 使用表单元素: `
<form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br/>
文件1:<input type="file" name="file1"/><br/>
文件2:<input type="file" name="file2"/><br/>
<input type="submit" value="提交"/>
</form>
文件上传对后端的要求
当提交的表单是文件上传表单时,那么对Servlet也是有要求的。
首先我们要肯定一点,文件上传表单的数据也是被封装到request对象中的。
- form表单 enctype的值为"multipart/form-data" 时,request.getParamter()这个方法失效
- 可以通过请求对象的ServletInputStream得到数据, 也可以借助第三方的jar包,apache提供的 commons-fileupload.jar
request.getParameter(String)方法获取指定的表单字段字符内容,但文件上传表单已经不在是字符内容,而是字节内容,所以失效。
这时可以使用request的getInputStream()方法获取ServletInputStream对象,它是InputStream的子类,这个ServletInputStream对象对应整个表单的正文部分(从第一个分隔线开始,到最后),这说明我们需要的解析流中的数据。当然解析它是很麻烦的一件事情,而Apache已经帮我们提供了解析它的工具**:commons-fileupload。**
使用Commons-fileupload步骤
- 导入依赖
- 创建DiskFileItemFactory工厂类, FileItem: 对应表单的表单项(input,select,teaxarea)
- 创建一个Request对象解析器 ServletFileUpload
- 解析Request对象 ServletFileUpload的
List<FileItem> parseRequest(request)
- 遍历第4步的 得到
List<FileItem>
得到一个一个FileItem,根据FileItem类型(普通表单项,非普通表单项(文件表单))进行处理
文件上传的细节
客户端上传的文件保存在服务器的硬盘上, 前端默认无法访问得到,
如何让客户端能够访问到上传到服务器的文件?,
Tomcat提供虚拟路径,
给服务器硬盘的某个路径映射一个访问的url, 当客户端访问这个url时, 间接的去访问服务器上对应硬盘的路径下的资源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4HpZ9Cc3-1667978787131)(D:\飞思实训三\6.JavaWeb\笔记\5_Filter和文件上传\assets\image-20221025160917060.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-23V1hNgI-1667978787132)(D:\飞思实训三\6.JavaWeb\笔记\5_Filter和文件上传\assets\image-20221025160938768.png)]
选择硬盘地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s6zX7K3k-1667978787132)(D:\飞思实训三\6.JavaWeb\笔记\5_Filter和文件上传\assets\image-20221025161105316.png)]
设置url
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9uWdn4x-1667978787134)(D:\飞思实训三\6.JavaWeb\笔记\5_Filter和文件上传\assets\image-20221025161236835.png)]
fileupload概述
fileupload是由apache的commons组件提供的上传组件。它最主要的工作就是帮我们解析request.getInputStream()。
fileupload组件需要的JAR包有:
commons-fileupload.jar,核心包;
commons-io.jar,依赖包
fileupload简单应用
fileupload的核心类有:DiskFileItemFactory、ServletFileUpload、FileItem。
使用fileupload组件的步骤如下:
- 创建工厂类DiskFileItemFactory对象:DiskFileItemFactory factory = new DiskFileItemFactory()
- 使用工厂创建解析器对象:ServletFileUpload fileUpload = new ServletFileUpload(factory)
- 使用解析器来解析request对象:List list = fileUpload.parseRequest(request)
隆重介绍FileItem类,它才是我们最终要的结果。一个FileItem对象对应一个表单项(表单字段)。一个表单中存在文件字段和普通字段,可以使用FileItem类的isFormField()方法来判断表单字段是否为普通字段,如果不是普通字段,那么就是文件字段了。
- String getName():获取文件字段的文件名称;
- String getString():获取字段的内容,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件;
- String getFieldName():获取字段名称,例如:,返回的是username;
- String getContentType():获取上传的文件的类型,例如:text/plain。
- int getSize():获取上传文件的大小;
- boolean isFormField():判断当前表单字段是否为普通文本字段,如果返回false,说明是文件字段;
- InputStream getInputStream():获取上传文件对应的输入流;
- void write(File):把上传的文件保存到指定文件中。
案例 上传文件
处理文件上传UploadServlet
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//借助于 commons-fileupload
//1.创建DiskFileItemFactory对象
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
//2.创建一个解析Request对象解析器 ServletFileUpload
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
try {
//3.解析Request对象
List<FileItem> fileItems = servletFileUpload.parseRequest(request);
//4.遍历fileItems
for (FileItem fileItem : fileItems) {
//5.判断是否为普通表单
if (fileItem.isFormField()) {
//true 普通表单
String fieldName = fileItem.getFieldName();//获取name值
String value = fileItem.getString("UTF-8");//获取表单的value
System.out.println(fieldName + ":" + value);
} else {
//文件表单
//上传的文件保存到服务器的硬盘上
String savePath = "D:/upload/";
//得到上传的文件名 getName()
String uploadFilename = fileItem.getName();
// 创建一个文件名 文件名称 + 后缀名(上传文件的后缀名)
String saveFile = FileUploadUtil.randomFileNameWithUUID() + FileUploadUtil.getFileSuffix(uploadFilename);
//保存 FileItem的write(File file)
fileItem.write(new File(savePath, saveFile));
}
}
}catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
注意点:
1.服务器中保存的文件名应该是唯一的,不能以用户上传的文件名来保存到服务器中
2.文件名唯一的处理方法(可以封装工具类)
使用时间戳拼接其他(例如用户名等等)
使用java内置的UUID获取随机生成的32位16进制字符串
3.保存文件的方法 FileItem.write(File file)
4.保存到服务器时别忘记拼接文件的后缀名,String uploadFilename = fileItem.getName(); (工具类封装方法截取后缀名)
工具类FileUploadUtil
public class FileUploadUtil {
/**
* 得到文件名的后缀
* @param fileName
* @return
*/
public static String getFileSuffix(String fileName){
if(fileName==null||fileName.isEmpty()){
throw new RuntimeException("找不到文件,filename:"+fileName);
}
return fileName.substring(fileName.lastIndexOf("."));
}
/**
* 使用UUID生成文件名
*/
public static String randomFileNameWithUUID(){
return UUID.randomUUID().toString().replaceAll("-","");
}
/**
* 基于时间戳生成文件名
*/
public static String randomFileNameWithTime(){
return System.currentTimeMillis()+"";
}
/**
* 基于时间戳 + UUID生成文件名
* @return
*/
public static String randomFilenameWithUUIDandTime(){
return System.currentTimeMillis()+randomFileNameWithUUID();
}
}
ajax
异步的JavaScript和XML
AJax的两大特点:
- 异步通信
- 局部刷新
JSON
JSON语法格式:
- 数据以key/value对存储, key:value
- 数据与数据之间使用逗号分割: k1:v1,k2:v2
- 使用
{}
表示一个对象{"id":1,"name":"张三"}
- 使用
[]
表示一个数组{"id":1,"name":"张三","loves":["read","music"]}
stus:[{"id":1,"name":"张三"},{"id":2,"name":"李四",sex:null}]
注意:
- key一定使用双引号引起
- 如果value是字符串,一定使用双引号引起
JSON值:
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true 或 false)
- 数组(在方括号中
[]
) - 对象(在花括号中
{}
) - null
ES5语法中 JSON类
JSON.parse(json字符串): 把JSON字符串转换为js对象
JSON.stringify(js对象): 把js对象转换为JSON格式字符串
JQuery中:
$.parseJSON(json字符串): 把json字符串转换为js对象, 该方法在jquery3.0 以后的版本删除
java对象与json的转换
借助第三方的jar: fastJson, jackson, json-lib…
课件: fastJson: 阿里巴巴的
核心类: JSON
核心方法:
把json字符串转换为java对象: parseObject()
把对象数组形式json字符串转换为java的List集合: parseArray()
把java对象转换为json字符串: toJSONString()
@JSONField注解
Student类 设置不可序列化及JSON的日期格式(使用fastjson jar包)
@JSONField(serialize=false)
private Integer age;
//format: 字符串转换格式
@JSONField(format="yyyy-MM-dd")
private Date birthday; //在实体类中, Date类一定是java.util.Date
jackson, json-lib…
课件: fastJson: 阿里巴巴的
核心类: JSON
核心方法:
把json字符串转换为java对象: parseObject()
把对象数组形式json字符串转换为java的List集合: parseArray()
把java对象转换为json字符串: toJSONString()
@JSONField注解
Student类 设置不可序列化及JSON的日期格式(使用fastjson jar包)
@JSONField(serialize=false)
private Integer age;
//format: 字符串转换格式
@JSONField(format="yyyy-MM-dd")
private Date birthday; //在实体类中, Date类一定是java.util.Date