JAVA EE(七) —— Filter 过滤器

一、Filter 过滤器概述

1、Filter 介绍

(1)介绍

  • Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源如Jsp、Servlet、静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
  • 通常使用过滤器Filter来做登录验证、会话管理、日志记录以及一些通用的操作,过滤器需要实现Filter接口
  • Filter过滤器的执行顺序是在Servlet之前的,其生命周期和Servlet类似
    创建 => 初始化 => 过滤 => 销毁

(2)Filter 的作用

  • 在客户端的请求访问后端资源之前,拦截这些请求。
  • 在服务器的响应发送回客户端之前,处理这些响应。

2、常见过滤器

  • 身份验证过滤器(Authentication Filters)。
  • 数据压缩过滤器(Data compression Filters)。
  • 加密过滤器(Encryption Filters)。
  • 触发资源访问事件过滤器。
  • 图像转换过滤器(Image Conversion Filters)。
  • 日志记录和审核过滤器(Logging and Auditing Filters)。
  • MIME-TYPE 链过滤器(MIME-TYPE Chain Filters)。
  • 标记化过滤器(Tokenizing Filters)。
  • XSL/T 过滤器(XSL/T Filters),转换 XML 内容。

3、Filter 配置文件

<filter>
	<filter-name></filter-name>
	<filter-class></filter-class>
</filter->
<filter-mapping>
	<filter-name></filter-name>
	<url-pattern><filter-pattern>
</filter-mapping>
  • filter-name:过滤器类的名称,如MyFilter
  • filter-class:过滤器类的路径,如com.java.filter.MyFilter
  • url-pattern:拦截过滤规则,如
    • /*:表示拦截所有
    • *.do:表示以.do结尾的都会被拦截进入过滤器,一般用于某个模块的拦截处理
    • /test.do:表示拦截一个指定url的请求,一般用于比较重要的servlet针对性的拦截

4、Filter 执行过程

  • 客户端发起请求到服务器;
  • 服务器接收到请求后,根据URI信息在web.xml中找到对应的过滤器执行doFilter方法,该方法对此次请求进行处理后如果符合要求则放行;
  • 放行后如果还有符合要求的过滤则继续进行过滤,找到执行对应的servlet进行请求处理;
  • servlet对请求处理完毕后,也就service方法结束后,还需继续返回相应的doFilter方法继续执行。

二、Filter过滤器示例

1、过滤器完成多点登陆的问题
(1)使用session完成会话跟踪

  • 在用户没有登陆的情况下,不允许访问一些网页和资源
  • 用户登陆成功以后,将用户信息保存在session对象中
  • 通过登陆过滤器判断用户是否已经登陆,如果登陆放行,否则就冲定向到登陆网页
  • 不需要登陆也能访问的资源

(2)使用Cookie记录登陆用户状态实现自动登陆

  • 在用户登陆成功以后,生成一个不可重复的随机字符串作为用户的登陆令牌信息
  • 将该令牌信息保存到数据库该用户的数据中
  • 使用Cookie将该令牌信息记录到浏览器
  • 登陆过滤器中获取(token)Cookie,通过token从数据库中获取用户数据,再将用户数据保

(3)解决多点登陆的问题

2、代码
(1)model 层
① users 类的失血模型

public class Users {
	private int userId;
	private String userAccount;
	private String userPassWord;
	private String userTaken;
	
	//省略getter、setter
}

(2)util 层
① JDBCUtil 类:连接数据库

public class JDBCUtil {
	public static final ThreadLocal<Connection> threadLocal=new ThreadLocal<Connection>();
	private static String driver = null;
	private static String url = null;
	private static String user = null;
	private static String password = null;
	static Scanner sc = new Scanner(System.in);
	
	//静态代码块
	static{
		try {
			//获取文件流
			InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("user.properties");
			//创建properties对象
			Properties prop = new Properties();
			//使用prop对象的load()方法加载流
			prop.load(is);
			//获取其文件的数据
			driver = prop.getProperty("river");
			url = prop.getProperty("url");
			user = prop.getProperty("user");
			password = prop.getProperty("password");
			
			//注册驱动
			Class.forName(driver);
		} catch (Exception  e) {
			e.printStackTrace();
		}
	}
	
	//获取连接
	public  static Connection registJDBC() throws Exception {
		return DriverManager.getConnection(url, user, password);
	}
	
	//关流
	public static void close(ResultSet resultSet, PreparedStatement ps, Connection conn) {
		if(resultSet != null) {
			try {
				resultSet.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if(ps != null) {
			try {
				ps.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if(conn != null) {
			try {
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

② FinalDates 类:静态常量

public class FinalDates {
	public static final String USERS = "users";
	public static final String TAKEN="user_taken";
}

(3)service 层
① UserService 类:UserService 的接口类

public interface UserService {
	//定义Service层查询用户账户的方法
	public Users readAccount(String user_Account);
	//定义Service层修改用户登录令牌的方法
	public void updateTaken(int user_Id, String user_Taken);
	//定义Service层查询用户登录令牌的方法
	public Users readTaken(String user_Taken);
}

② Imp_UserService 类:实现 UserService 接口

public class Imp_UserService implements UserService{
	private UserDao userDao = new Imp_UserDao();
	@Override
	public Users readAccount(String user_Account) {
		Users users = null;
		try {
			users = userDao.readAccount(user_Account);
		}catch(Exception e) {
			e.printStackTrace();
		}
		return users;
	}

	@Override
	public void updateTaken(int user_Id, String user_Taken) {
		Connection conn=null;
		try {
			conn = JDBCUtil.registJDBC();
			
			//关闭自动提交
			conn.setAutoCommit(false);
			JDBCUtil.threadLocal.set(conn);
			
			userDao.updateTaken(user_Id, user_Taken);
			
			//提交事务
			conn.commit();
		} catch (Exception e) {
			try {
				//回滚事务
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}
	
	@Override
	public Users readTaken(String user_Taken) {
		Users users = null;
		try {
			users = userDao.readTaken(user_Taken);
		}catch(Exception e) {
			e.printStackTrace();
		}
		return users;
	}
}

(4)dao 层
① UserDao 类:UserDao 的接口类

public interface UserDao {
	//定义Dao层一个查询用户账户的方法
	public Users readAccount(String user_Account) throws Exception;
	//定义Dao层一个修改用户登录令牌的方法
	public void updateTaken(int user_Id,String user_Taken) throws Exception;
	//定义Dao层一个查询用户登录令牌的方法
	public Users readTaken(String user_Taken) throws Exception;
}

② Imp_UserDao 类:实现 UserDao 接口

public class Imp_UserDao implements UserDao{
	@Override
	public Users readAccount(String user_Account) throws Exception {
		Connection conn = JDBCUtil.registJDBC();
		String sql = "SELECT user_id,user_account,user_password,user_taken FROM users WHERE user_account = ?";
		PreparedStatement ps = conn.prepareStatement(sql);
		ps.setString(1, user_Account);
		ResultSet resultSet = ps.executeQuery();
		
		Users users = null;
		if(resultSet.next()) {
			users = new Users();
			users.setUserId(resultSet.getInt("user_id"));
			users.setUserAccount(resultSet.getString("user_account"));
			users.setUserPassWord(resultSet.getString("user_password"));
			users.setUserTaken(resultSet.getString("user_taken"));
		}
		JDBCUtil.close(resultSet, ps, conn);
		return users;
	}

	@Override
	public void updateTaken(int user_Id, String user_Taken) throws Exception {
		Connection conn = JDBCUtil.threadLocal.get();
		String sql = "UPDATE users SET user_taken = ? WHERE user_id = ?";
		PreparedStatement ps = conn.prepareStatement(sql);
		ps.setString(1, user_Taken);
		ps.setInt(2, user_Id);
		ps.executeUpdate();
		ps.close();
	}

	@Override
	public Users readTaken(String user_Taken) throws Exception {
		Connection conn = JDBCUtil.registJDBC();
		String sql = "SELECT user_id, user_account, user_password, user_taken FROM users WHERE user_taken = ?";
		PreparedStatement ps = conn.prepareStatement(sql);
		ps.setString(1, user_Taken);
		ResultSet resultSet = ps.executeQuery();
		Users users = null;
		if(resultSet.next()) {
			users=new Users();
			users.setUserId(resultSet.getInt("user_id"));
			users.setUserAccount(resultSet.getString("user_account"));
			users.setUserPassWord(resultSet.getString("user_password"));
			users.setUserTaken(resultSet.getString("user_taken"));
		}
		JDBCUtil.close(resultSet, ps, conn);
		return users;
	}
}

(5)filter 层
① LoginFilter 类:登录的过滤器操作
创建的过滤器类需要实现Filter接口,并且重写doFilter方法

@WebFilter("/*")
public class LoginFilter implements Filter{
	private List<String> starts = new ArrayList<String>();
	private List<String> ends = new ArrayList<String>();
	
	@Override
	public void init(FilterConfig config) throws ServletException {
		//存入后缀需要放行的地址
		ends.add("css");
		ends.add("js");
		ends.add("jpg");
		ends.add("png");
		ends.add("gif");
		
		//存入前缀需要放行的地址
		starts.add("login");
		starts.add("loginservlet");
		starts.add("LoginServlet");
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp,FilterChain chain) throws IOException, ServletException {
		//设置编码格式
		req.setCharacterEncoding("utf-8");
		resp.setContentType("text/html;charset=utf-8");
		//获取本次请求的请求路径
		HttpServletRequest request = (HttpServletRequest)req;
		String uri = request.getRequestURI().toLowerCase();
		//提取出前缀和后缀
		String resourceURI = uri.substring(uri.lastIndexOf("/") + 1 );
		String start = null;
		String end = null;
		int index = resourceURI.lastIndexOf(".");
		if(index != -1) {
			start = resourceURI.substring(0, index);
			end = resourceURI.substring(index + 1);
		}else {
			start = resourceURI;
		}
		
		//判断
		if(starts.contains(start) || ends.contains(end)) {
			System.out.println("进来0");
			//满足条件放行
			chain.doFilter(req, resp);
		}else {
			//判断浏览器发送的请求中的cookie信息是否包含taken
			Cookie[] cookies = request.getCookies();
			String taken = null;
			if(cookies != null) {
				for(Cookie cookie: cookies) {
					if(cookie.getName().equals(FinalDates.TAKEN)) {
						taken = cookie.getValue();
					}
				}
			}
			if(taken == null) {
				HttpServletResponse response=(HttpServletResponse)resp;
//				chain.doFilter(req, resp);
				response.sendRedirect("/GouYaoDemo/jsp/login.jsp");
			}else {
				UserService userService = new Imp_UserService();
				Users users = userService.readTaken(taken);
				if(users != null) {
					HttpSession session = request.getSession();
					session.setAttribute(FinalDates.USERS, users);
					chain.doFilter(req, resp);
				}else {
					String message = "您的账号在别处登录";
					request.setAttribute("message", message);
					request.getRequestDispatcher("/jsp/login.jsp").forward(request, resp);
				}
			}
		}
	}
	
	@Override
	public void destroy() {
	}
}
  • init():初始化,在这里可以配置允许直接放行的文件类型等,在服务器启动就会执行。
  • doFilter():这个时拦截请求的方法,在这个方法里,了可以对资源进行管理和分配, chain.doFilter()表示手动对满足条件的请求进行放行。
  • destroy():销毁,在服务器关闭的时候执行。

(6)servlet 层
① LoginServlet 类:用户登录操作类(包括用户登录信息判断等)

@WebServlet("/loginServlet")
public class LoginServlet extends  HttpServlet{
	@Override
	public void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//处理乱码
		request.setCharacterEncoding("utf-8");

		//获取输入框的数据
		String userName = request.getParameter("username");
		String passWord = request.getParameter("password");
		//定义一个字符串用于给用户反馈登录情况信息
		String message = null;
		//防止用户直接输入地址访问,在后端设定用户名和密码为空的判断
		//判断用户名是否为空
		if(userName == null || "".equals(userName)) {
			message = "用户名不能为空";
			request.setAttribute("message", message);
			//用户名为空的情况下再次请求转发到登录界面
			request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
			return;
		}
		//判断密码是否为空
		if(passWord == null || "".equals(passWord)) {
			message = "密码不能为空";
			request.setAttribute("message", message);
			//密码为空的情况下再次请求转发到登录界面
			request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
			return;
		}
		
		//判断用户账号是否存在以及账号密码是否正确
		UserService userService = new Imp_UserService();
		//通过service层调用dao层的查询用于账户的方法,并返回一个Users对象
		Users users = userService.readAccount(userName);
		if(users == null) {
			message = "用户名不存在";
			request.setAttribute("message", message);
			request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
		}else {
			if(!passWord.equals(users.getUserPassWord())) {
				message = "密码错误";
				request.setAttribute("message", message);
				request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
			}else {
				/*
				 * 用户账号密码都正确,将用户对象保存到Session中,
				 * 并随机生成一个用户登录令牌存入到数据库和浏览器中,
				 * 最后跳转到另一个页面,
				 */
				request.getSession().setAttribute(FinalDates.USERS, users);
				
				String userTaken = UUID.randomUUID().toString().replace("-", "");
				//将登录令牌更新到数据库中的用户数据
				userService.updateTaken(users.getUserId(), userTaken);
				//将登录令牌保存到浏览器中
				Cookie cookie = new Cookie(FinalDates.TAKEN, userTaken);
				cookie.setMaxAge(60*60*7);
				//将Cookie响应到浏览器
				response.addCookie(cookie);
				//请求转发到另一个网页
				request.getRequestDispatcher("/jsp/main.jsp").forward(request, response);
			}
		}
	}
}
发布了104 篇原创文章 · 获赞 58 · 访问量 7509

猜你喜欢

转载自blog.csdn.net/baidu_27414099/article/details/104440957
今日推荐