Filter:简单来说就是设立在客户端和服务器之间的一个拦截关卡,当发现客户端请求的资源或者服务器响应给客户端的资源不规范(比如:敏感字符等)就会拦截该资源
还有一个作用就是:可以在拦截关卡这里存放一些权限控制在里面
一、Fiter快速入门
注意1:这个实现的Filter是 javax.servlet包下的Filter
注意2:只要Filter的拦截路径是/* 那么客户端访问的路径资源或者服务器响应的资源 都是会先被拦截下来的,然后放不放行看代码
代码演示:
eg:当我们没有Filter拦截的时候我们开启服务器访问hello.jsp资源结果如下:(正常访问该资源)
eg:当我们开启Filter拦截不放行的时候我们开启服务器访问hello.jsp资源结果如下:(假设我们不放行 那么相当于客户端访问hello.jsp的请求就被我们拦截下来了而且我们不放行 那么也就是说获取不到资源了)
注意:不放行直接不用写代码即可 放行需要调用放行方法
开启服务器客户端访问hello.jsp资源:
会发现拿不到资源数据了,因为客户端的请求被拦截下来了
eg:当我们开启Filter拦截 放行的时候(也就是说拦截到了客户端的请求但是我们放行了)客户端能获取到相应的路径下资源:
开启服务器客户端访问hello.jsp资源:
二、Filter 执行流程
用代码演示上图的执行流程:
hello.jsp:
访问hello.jsp的结果:如果输出 1 2 3 说明方形访问完资源后会回到Filter中执行放行后的代码逻辑
三、Filter使用细节 (拦截路径配置 & 过滤器链)
3.1、拦截路径的配置
3.2、过滤器链
Filter的优先级:
代码演示过滤器链(看是否按着上面的第一步.第二步…..代码执行的):
FilterDemo (Filter1):
FilterDemo2 (Fiter2):
hello.jsp:
开启服务器访问hello.jsp看执行结果说明验证上图成功:
四、登录验证
我们已经写过的表现层登录功能:LoginServlet
补充: 用cookie的目的是为了记住用户、用session的目的是为了过滤器的使用
package com.itheima.web;
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、接收客户端用户名和密码
String username =request.getParameter("username");
String password =request.getParameter("password");
// 获取复选框数据
String remember =request.getParameter("remember");
// 2、调用service层进行查询
UserService userService =new UserService();
User user =userService.login(username,password);
// 3、判断查询是否有结果
if (user != null){
// 判断user不为null说明登录成功了
// 判断用户是否勾选了记住我 remember
// 这里用:"1".equals(remember) 而不用remember.equals("1")
// 是为了防止空指针异常 因为remember有可能用户没勾选 为null 然后比较的话会空指针
if ("1".equals(remember)){
// 勾选了,发送Cookie
// 1 创建Cookie对象
Cookie c_username =new Cookie("username",username);
Cookie c_password =new Cookie("password",password);
// 设置Cookie数据在客户端存活的时间
c_username.setMaxAge(60*60*24*60);
c_password.setMaxAge(60*60*24*60);
// 2 发送Cookie
response.addCookie(c_username);
response.addCookie(c_password);
}
// 2. 把user查询出来的数据先封装到Session域当中 (数据保存在了服务器之间共享)
HttpSession httpSession =request.getSession();
// 存储到Session域中
httpSession.setAttribute("user",user);
// 1.登录成功 (要求:动态重定向到MVC三层架构讲的商品增删改查操作:SelectAllServlet资源下查询所有)
String path =request.getContextPath();
response.sendRedirect(path+"/selectAllServlet");
} else {
// 登录失败
// 储存错误提示信息到request域当中 转发给login.jsp
request.setAttribute("login_msg","用户名或密码错误");
// 跳转到登录的login.jsp页面
request.getRequestDispatcher("/login.jsp").forward(request,response);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
从代码中可以看出我们在判断user不为null的时候说明用户输入的账户和密码是正确的,登录成功的,那么此时我们又把用户输入的信息user储存到了Session域中
因此我们的拦截器只需要拿到Session域中的user数据判断user数据是否为null 、不为null说明用户登录的信息是正确的 那么我们就放行 让用户访问登录后的资源,如果为null 说明用户压根就输入的账户和密码不正确 那么我们就转发到登录的页面 (注意:第一次的时候拦截器里面拿到的user对象肯定为null,因为当客户端访问路径的时候会先进入拦截器路径下,此时的user对象还没有封装到Session域当中 )
拦截器:
客户端访问想要的资源路径的时候 拦截器路径为/* 所以会先进入拦截器中 因此这就说明了上面的红字问题
package com.itheima.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 登陆验证的过滤器
*/
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
// 1.判断Session中是否有user (LoginServlet登录中查询出来存储在Session域中的用户信息)
HttpServletRequest req =(HttpServletRequest) request;
// 注意:Session中的调用getSession()方法的request是HttpServletRequest包下的request所以需要把
// Filter包下的request转换成HttpServletRequest包下的request
HttpSession session =req.getSession();
Object user =session.getAttribute("user");
// 判断user是否为null
if (user != null){
// 不为null,说明用户登录过了
// 放行
chain.doFilter(request, response);
}
else {
// 为null,说明用户未登录 (跳转到登录页面)
request.setAttribute("login_msg","您尚未登陆!");
request.getRequestDispatcher("/login.jsp").forward(req,response);
}
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
当我们开启服务器客户端访问资源的时候:
因此我们需要修改代码,当客户端访问到是登录(注册)页面的时候,把关于登录(注册)页面中的资源(css、html等)展示给用户并且放行不被拦截:
package com.itheima.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 登陆验证的过滤器
*/
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req =(HttpServletRequest) request;
// 注意:Session中的调用getSession()方法的request是HttpServletRequest包下的request所以需要把
// Filter包下的request转换成HttpServletRequest包下的request
// !判断访问资源路径是否和登录注册有关
String[] urls = {"/login.jsp","/register.jsp","/imgs/","/css/","/loginServlet","/registerServlet","/checkCodeServlet"};
// ! 获取当前访问的资源路径
String url =req.getRequestURL().toString(); // http://localhost:8089/brand-demo/register.jsp 类型
// ! 判断
for (String u:urls) { // 遍历urls数组里面地址
if (url.contains(u)){ // 如果url包含遍历出来的u
// 包含的话 说明用户访问的是登录或者注册相关的资源路径
// 放行即可
chain.doFilter(request, response);
return;
}
}
// (5个就依次判断就可以了 判断完发现不包含就继续往下执行代码了)
// 1.判断Session中是否有user (LoginServlet登录中查询出来存储在Session域中的用户信息)
HttpSession session =req.getSession();
Object user =session.getAttribute("user");
// 判断user是否为null
if (user != null){
// 不为null,说明用户登录过了
// 放行
chain.doFilter(request, response);
}
else {
// 为null,说明用户未登录 (跳转到登录页面)
request.setAttribute("login_msg","您尚未登陆!");
request.getRequestDispatcher("/login.jsp").forward(req,response);
}
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
=====================代码中的细节逻辑分析===================
特别注意的小细节:转发、重定向等,只要是跳转新的页面的,URL地址栏就会发现变化,那么拦截器就会当成是一次新的访问请求 然后给拦截下来了 最后决定放不放行
拦截器代码:
package com.itheima.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 登陆验证的过滤器
*/
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req =(HttpServletRequest) request;
// 注意:Session中的调用getSession()方法的request是HttpServletRequest包下的request所以需要把
// Filter包下的request转换成HttpServletRequest包下的request
// !判断访问资源路径是否和登录注册有关
String[] urls = {"/login.jsp","/register.jsp","/imgs/","/css/","/loginServlet","/registerServlet","/checkCodeServlet"};
// ! 获取当前访问的资源路径
String url =req.getRequestURL().toString(); // http://localhost:8089/brand-demo/register.jsp 类型
// ! 判断
for (String u:urls) { // 遍历urls数组里面地址
if (url.contains(u)){ // 如果url包含遍历出来的u
// 包含的话 说明用户访问的是登录或者注册相关的资源路径
// 放行即可
chain.doFilter(request, response);
return; // 结束代码了 下面的代码也不执行了
}
}
// (5个就依次判断就可以了 判断完发现不包含就继续往下执行代码了)
// 1.判断Session中是否有user (LoginServlet登录中查询出来存储在Session域中的用户信息)
HttpSession session =req.getSession();
Object user =session.getAttribute("user");
// 判断user是否为null
if (user != null){
// 不为null,说明用户登录过了
// 放行
chain.doFilter(request, response);
}
else {
// 为null,说明用户未登录 (跳转到登录页面)
request.setAttribute("login_msg","您尚未登陆!");
request.getRequestDispatcher("/login.jsp").forward(req,response);
}
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
假设我们开启服务器后访问的是login.jsp路径下的资源:
我们从拦截器的urls数组里面可以看到,我们放行了login.jsp 并且点击登录后进入的是loginServlet路径下的资源 我们也放行了,
这里有一个细节:就是我们放行进入loginServlet路径资源下后看代码:
(我们把用户登录的信息封装成user对象封装到了Session域当中了,此时登录成功的话我们是重定向到selectAllServlet路径下的,注意:重定向的时候我们的URL地址栏就会发现变化,相当于重新访问了,那么就再次会被拦截器当成新的客户端请求拦截请求数据,然后决定放不放行,我们通过循环判断发现selectAllServlet不是我们所包含的对象,那么就会循环完后进入下面的代码判断user是否为null,注意:就是因为我们刚才登录的时候在loginServlet路径下已经把user对象封装在Session域当中了 所以这时候user不为null了 ,我们的代码是放行的, 因此我们就能查看到所有商品的数据了,再在selectAllServlet路径资源下再转发、重定向等新的URL地址时也会放行了,因为user已经有数据了)
package com.itheima.web;
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、接收客户端用户名和密码
String username =request.getParameter("username");
String password =request.getParameter("password");
// 获取复选框数据
String remember =request.getParameter("remember");
// 2、调用service层进行查询
UserService userService =new UserService();
User user =userService.login(username,password);
// 3、判断查询是否有结果
if (user != null){
// 判断user不为null说明登录成功了
// 判断用户是否勾选了记住我 remember
// 这里用:"1".equals(remember) 而不用remember.equals("1")
// 是为了防止空指针异常 因为remember有可能用户没勾选 为null 然后比较的话会空指针
if ("1".equals(remember)){
// 勾选了,发送Cookie
// 1 创建Cookie对象
Cookie c_username =new Cookie("username",username);
Cookie c_password =new Cookie("password",password);
// 设置Cookie数据在客户端存活的时间
c_username.setMaxAge(60*60*24*60);
c_password.setMaxAge(60*60*24*60);
// 2 发送Cookie
response.addCookie(c_username);
response.addCookie(c_password);
}
// 2. 把user查询出来的数据先封装到Session域当中 (数据保存在了服务器之间共享)
HttpSession httpSession =request.getSession();
// 存储到Session域中
httpSession.setAttribute("user",user);
// 1.登录成功 (要求:动态重定向到MVC三层架构讲的商品增删改查操作:SelectAllServlet资源下查询所有)
String path =request.getContextPath();
response.sendRedirect(path+"/selectAllServlet");
} else {
// 登录失败
// 储存错误提示信息到request域当中 转发给login.jsp
request.setAttribute("login_msg","用户名或密码错误");
// 跳转到登录的login.jsp页面
request.getRequestDispatcher("/login.jsp").forward(request,response);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
login.jsp:
<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
<link href="css/login.css" rel="stylesheet">
</head>
<body>
<div id="loginDiv" style="height: 350px">
<form action="/brand-demo/loginServlet" id="form">
<h1 id="loginMsg">LOGIN IN</h1>
<div id="errorMsg">${login_msg} ${register_msg}</div>
<%--
${login_msg} 就是我们在LoginServlet资源下登录失败后转发到login页面把
登录页面展示给用户,并且把转发时储存到request域当中的数据(用户名或密码错误)拿
到展示在登录页面上 ${login_msg}:EL表达式 拿储存在域中数据的
${register_msg} 拿到的是RegisterServlet资源下封装到request域当中的数据通过转发过来
(注册成功,请登录)展示在登录的页面上
--%>
<p>Username:<input id="username" name="username" value="${cookie.username.value}" type="text"></p>
<p>Password:<input id="password" name="password" value="${cookie.password.value}" type="password"></p>
<%-- value 的作用就是在复选框中,假设选中了该复选框那么该复选框的值也就是该value的值
这里remember是复选框 当我们勾选后 该默认值为“1”
--%>
<p>Remember:<input id="remember" name="remember" value="1" type="checkbox"></p>
<div id="subDiv">
<input type="submit" class="button" value="login up">
<input type="reset" class="button" value="reset">
<a href="register.jsp">没有账号?</a>
</div>
</form>
</div>
</body>
</html>