Для начала работы с фильтрами достаточно прочитать эту статью

Java Java EE

что такое фильтр

Фильтр — это одна из продвинутых функций сервлета, не думайте о ней слишком продвинуто, это просто класс Java, который реализует интерфейс фильтра!

Во-первых, давайте посмотрим, где находится фильтр в веб-контейнере:

Из приведенного выше рисунка мы можем найти, что,Когда браузер отправляет запрос на сервер, он сначала выполняет фильтр, а затем обращается к ресурсам Сети. Сервер отвечает Response, который также проходит через фильтр, прежде чем попасть в браузер с веб-ресурса. .

Мы можем легко найти этоФильтр можно сравнить с фильтром. Давайте подумаем, что может настоящий фильтр:При заваривании чая отфильтруйте чайные листья.. Как фильтр фильтрует чайные листья?Сетка заданного размера, пока сетка меньше, чем чайные листья, фильтрация может быть достигнута!

Кроме того, в веб-контейнере фильтры могут:Отфильтруйте некоторые конфиденциальные строки [при условии, что конфиденциальные строки не могут отображаться], избегайте искаженных китайских символов [при условии, что все веб-ресурсы используют кодировку UTF-8], проверка разрешений [при условии, что только браузеры с сеансами или файлами cookie могут получить доступ к веб-ресурсам] и т. д. Подождите, эффект фильтра очень велик, пока вы используете свое воображение, вы можете получить неожиданные эффекты.

То есть: когда необходимо ограничить доступ пользователя к определенным ресурсам, заранее обработать определенные ресурсы при обработке запросов, обработать содержимое ответа сервера и вернуть его, мы используем фильтры для его завершения!


Зачем нужен фильтр

Возьмем пример напрямую:

Нет фильтра для решения проблемы искажения китайских иероглифов.

  • Если я не использую фильтр:Браузер отправляет данные на сервлет через http запрос, если есть китайский, то надо указывать кодировку, иначе будет искажено!

  • Страница jsp отправляет китайские данные сервлету для обработки.


<form action="${pageContext.request.contextPath}/Demo1" method="post">

    <input type="text" name="username">

    <input type="submit" value="提交">

</form>

  • Когда сервлет не указывает кодировку, полученный код искажается

Как решить проблему искаженных китайских символов в Servlet, другие мои сообщения в блоге включают:blog.CSDN.net/hon_3 has/Aretti…

Это:Если я каждый раз принимаю китайские данные, приносимые клиентом, мне приходится устанавливать кодировку в Serlvet. Эта частота повторения кода слишком высока! ! ! !


Существуют фильтры для решения проблемы искажения китайских иероглифов.

С фильтрами все иначе:Пока я указываю кодировку в фильтре, я могу заставить все веб-ресурсы на сайте использовать эту кодировку, и возможность повторного использования идеальна!


API фильтра

Класс Java можно назвать фильтром, если он реализует интерфейс фильтра.! Методы интерфейса Filter также очень просты:

Излишне говорить, что методы init() и destroy() такие же, как и у сервлета.Выполняется только при загрузке и уничтожении веб-сервера, только один раз!

Стоит отметить, что метод doFilter(), ** имеет три параметра (ServletRequest, ServletResponse, FilterChain), ** из первых двух параметров мы можем найти:Фильтр может завершить операцию фильтрации любого протокола!

Что такое цепочка фильтров? Посмотрим:

FilterChain — это интерфейс, определяющий метод doFilter(). Что, черт возьми, здесь происходит? ? ? ? ? ?

Мы можем понять это так: существует не один фильтр, так как же нам управлять этими фильтрами?Цепная структура используется в Java.Поместите все фильтры в FilterChain, если условия выполнены, выполните следующий фильтр (если фильтра нет, выполните целевой ресурс).

Приведенные выше слова могут показаться немного насмешливыми, можно представить примеры из жизни:Теперь я хочу отфильтровать камни и чайные листья на чашке. Камень на первом этаже, а чай на первом этаже. Следовательно, фильтрующее устройство чайной чашки должно иметь два слоя фильтров. Это фильтрующее устройство — FilterChain, а фильтр, который фильтрует камни, и фильтр, который фильтрует чайные листья, — это Фильтр. В каменном фильтре чай принадлежит следующему слою, поэтому дайте чаю выйти, а чайный фильтр отфильтрует чай. После фильтрации чайных листьев остальное - чай ​​(чай можно сравнить с нашим целевым ресурсом)


Быстрый старт

написать простой фильтр

  • Класс Java, реализующий интерфейс Filter, называется фильтром.

	public class FilterDemo1 implements Filter {
	    public void destroy() {
	    }
	
	    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
	
	        //执行这一句,说明放行(让下一个过滤器执行,如果没有过滤器了,就执行执行目标资源)
	        chain.doFilter(req, resp);
	    }
	
	    public void init(FilterConfig config) throws ServletException {
	        
	    }
	}

развертывание фильтра

Фильтры аналогичны сервлетам и должны быть развернуты на веб-сервере.

Первый способ: настроить в файле web.xml

filter

<filter>для регистрации фильтров


	<filter>
	 	     <filter-name>FilterDemo1</filter-name>
		     <filter-class>FilterDemo1</filter-class>
		     <init-param>
			 <param-name>word_file</param-name>	
			 <param-value>/WEB-INF/word.txt</param-value>
		     </init-param>
	</filter>


  • <filter-name>используется дляУкажите имя для фильтра, содержимое элемента не может быть пустым.
  • <filter-class>элемент, используемый для указания фильтраполное квалифицированное имя класса.
  • <init-param>элемент используется для указания параметров инициализации фильтра, а его дочерние элементы задают имя параметра,<param-value>Задает значение параметра. В фильтре можноИспользуйте объект интерфейса FilterConfig для доступа к параметрам инициализации.

filter-mapping

<filter-mapping>элемент используется дляУстановите ресурс, за перехват которого отвечает Фильтр..

Перехваченный фильтром ресурс может бытьУкажите двумя способами: имя сервлета и путь запроса для доступа к ресурсу.



    <filter-mapping>
        <filter-name>FilterDemo1</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

  • <filter-name>Дочерний элемент используется для установки зарегистрированного имени фильтра.Значение должно быть именем фильтра, объявленного в элементе.
  • <url-pattern>Установите путь запроса, перехватываемый фильтром (стиль URL-адреса, связанный с фильтром)
  • <servlet-name>Указывает имя сервлета, перехваченного фильтром.
  • <dispatcher>Указывает, как ресурсы, перехваченные фильтром, вызываются контейнером сервлета, который может бытьОдин из REQUEST, INCLUDE, FORWARD и ERROR, по умолчанию REQUEST. Пользователь может установить несколько<dispatcher>дочерний элементИспользуется для указания того, что фильтр перехватывает несколько вызовов ресурсов.

dispatcher

Значения, которые могут устанавливать дочерние элементы и их значения:

  • ЗАПРОС:Когда пользователь посещает страницу напрямую, веб-контейнер вызывает фильтр.. Если к целевому ресурсу обращаются через методы include() или forward() диспетчера запросов, этот фильтр вызываться не будет.
  • ВКЛЮЧИТЬ: если целевой ресурсПри доступе через метод include() класса RequestDispatcher фильтр будет вызываться. В остальном фильтр вызываться не будет.
  • ВПЕРЕД: если целевой ресурс переданПри доступе к методу forward() объекта RequestDispatcher будет вызываться фильтр., иначе фильтр вызываться не будет.
  • ОШИБКА: если целевой ресурсПри вызове через механизм декларативной обработки исключений будет вызван фильтр.. В остальном фильтр вызываться не будет.

Второй способ: настроить через аннотации



	@WebFilter(filterName = "FilterDemo1",urlPatterns = "/*")


Приведенная выше конфигурация — «/*», для всех веб-ресурсов требуется фильтр маршрута.

Если вы хотите отфильтровать некоторые веб-ресурсы, вам необходимо указать имя веб-ресурса!


порядок выполнения фильтра

Как упоминалось выше, метод фильтра doFilter() чрезвычайно важен,Интерфейс FilterChain представляет все фильтры.Метод doFilter() в FilterChain определяет, следует ли освободить следующий фильтр для выполнения (если фильтра нет, выполнить целевой ресурс).

тест один

  • Сначала выведите предложение в doFilter() фильтра и вызовите метод doFilter() объекта цепочки

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        System.out.println("我是过滤器1");

        //执行这一句,说明放行(让下一个过滤器执行,或者执行目标资源)
        chain.doFilter(req, resp);
    }

  • Перейдем на страницу test.jsp:

Мы обнаружили, что доступ к test.jsp (нашему целевому ресурсу) был успешно выполнен, и строка была напечатана на сервере!


Тест два

давай попробуемchain.doFilter(req, resp);Закомментируйте этот код!

Страница test.jsp не имеет вывода (то есть к странице jsp нет доступа).


Тест третий

Просто посмотрите на код ниже. Мы уже знаем, что в консоли будет напечатано «готово к выпуску», а также доступна страница test.jsp,Но будет ли на консоли напечатано «выпуск завершен»?


    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        System.out.println("准备放行");

        //执行这一句,说明放行(让下一个过滤器执行,或者执行目标资源)
        chain.doFilter(req, resp);

        System.out.println("放行完成");
    }

Ответ тоже очень прост,обязательно напечатаю на консоли. Давайте взглянем:

Обратите внимание, что его полная последовательность процессов выглядит следующим образом:Клиент отправляет http-запрос на веб-сервер, веб-сервер выполняет фильтр, и когда выполнение достигает состояния «готов к выпуску», он выводит строку на консоль, а затем выполняет метод doFilter(). (например, test.jsp). После выполнения целевого ресурса вернитесь к фильтру, продолжите выполнение кода и затем выведите «выпуск завершен».

тест четыре

Добавим еще один фильтр, чтобы увидеть порядок выполнения.

  • фильтр 1

        System.out.println("过滤器1开始执行");

        //执行这一句,说明放行(让下一个过滤器执行,或者执行目标资源)
        chain.doFilter(req, resp);

        System.out.println("过滤器1开始完毕");

  • фильтр 2

        System.out.println("过滤器2开始执行");
        chain.doFilter(req, resp);
        System.out.println("过滤器2开始完毕");

  • Servlet


        System.out.println("我是Servlet1");

Когда мы зайдем на Servlet1, посмотрим, что появится в консоли:

Порядок выполнения следующий:Сначала выполните FilterDemo1, отпустите, выполните FilterDemo2, отпустите, выполните Servlet1, Servlet1 вернется к FilterDemo2 после выполнения, а после выполнения FilterDemo2 вернется к FilterDemo1


Примечание: Порядок выполнения между фильтрами зависит от порядка сопоставления в файле web.xml.Если он расположен впереди, он будет выполнен первым, а если он расположен сзади, он будет выполнен позже! Если он настроен по аннотации, сравните приоритет строки urlPatterns


Простое применение фильтра

  • Три типичных применения фильтра:
  • 1. Вы можете решить, вызывать ли метод chain.doFilter(request, response) в соответствии с условиями в фильтре, то есть разрешить ли выполнение целевого ресурса
  • 2. Прежде чем разрешить выполнение целевого ресурса, вы можете предварительно обработать запрос\ответ, а затем позволить целевому ресурсу выполниться.
  • 3. После выполнения целевого ресурса можно зафиксировать результат выполнения целевого ресурса для реализации некоторых специальных функций.

Отключить кеширование браузером всех динамических страниц


    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        //让Web资源不缓存,很简单,设置http中response的请求头即可了!

        //我们使用的是http协议,ServletResponse并没有能够设置请求头的方法,所以要强转成HttpServletRequest

        //一般我们写Filter都会把他俩强转成Http类型的
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        response.setDateHeader("Expires", -1);
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");

        //放行目标资源的response已经设置成不缓存的了
        chain.doFilter(request, response);
    }


  • До фильтрации заголовок ответа выглядит так:

  • После фильтрации заголовок ответа выглядит так:


Реализовать автоматический вход

Разработать сущность, собрать фиктивную базу данных, Дао

  • организация:


    private String username ;
    private String password;


    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

	//各种setter和getter

  • Коллекция фиктивной базы данных

	public class UserDB {
	
	    private static List<User> users = new ArrayList<>();
	
	
	
	    static {
	        users.add(new User("aaa", "123"));
	        users.add(new User("bbb", "123"));
	        users.add(new User("ccc", "123"));
	    }
	
	    public static List<User> getUsers() {
	        return users;
	    }
	
	    public static void setUsers(List<User> users) {
	        UserDB.users = users;
	    }
	}


  • развивать дао

    public User find(String username, String password) {

        List<User> userList = UserDB.getUsers();

        //遍历List集合,看看有没有对应的username和password
        for (User user : userList) {
            if (user.getUsername().equals(username) && user.getPassword().equals(password)) {
                return user;
            }
        }
        return null;
    }


интерфейс входа



<form action="${pageContext.request.contextPath}/LoginServlet">

    用户名<input type="text" name="username">
    <br>
    密码<input type="password" name="password">
    <br>

    <input type="radio" name="time" value="10">10分钟
    <input type="radio" name="time" value="30">30分钟
    <input type="radio" name="time" value="60">1小时
    <br>

    <input type="submit" value="登陆">

</form>


Сервлет, который обрабатывает вход в систему


        //得到客户端发送过来的数据
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        
        UserDao userDao = new UserDao();
        User user = userDao.find(username, password);

        if (user == null) {
            request.setAttribute("message", "用户名或密码是错的!");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
        }

        //如果不是为空,那么在session中保存一个属性
        request.getSession().setAttribute("user", user);
        request.setAttribute("message", "恭喜你,已经登陆了!");
        
        //如果想要用户关闭了浏览器,还能登陆,就必须要用到Cookie技术了
        Cookie cookie = new Cookie("autoLogin", user.getUsername() + "." + user.getPassword());

        //设置Cookie的最大声明周期为用户指定的
        cookie.setMaxAge(Integer.parseInt(request.getParameter("time")) * 60);
        
        //把Cookie返回给浏览器
        response.addCookie(cookie);
        
        //跳转到提示页面
        request.getRequestDispatcher("/message.jsp").forward(request, response);

фильтр


        HttpServletResponse response = (HttpServletResponse) resp;
        HttpServletRequest request = (HttpServletRequest) req;

        //如果用户没有关闭浏览器,就不需要Cookie做拼接登陆了
        if (request.getSession().getAttribute("user") != null) {
            chain.doFilter(request, response);
            return;
        }

        //用户关闭了浏览器,session的值就获取不到了。所以要通过Cookie来自动登陆
        Cookie[] cookies = request.getCookies();
        String value = null;
        for (int i = 0; cookies != null && i < cookies.length; i++) {
            if (cookies[i].getName().equals("autoLogin")) {
                value = cookies[i].getValue();
            }
        }

        //得到Cookie的用户名和密码
        if (value != null) {

            String username = value.split("\\.")[0];
            String password = value.split("\\.")[1];

            UserDao userDao = new UserDao();
            User user = userDao.find(username, password);

            if (user != null) {
                request.getSession().setAttribute("user", user);
            }
        }
        
        chain.doFilter(request, response);

  • Эффект:


улучшать

Мы напрямую помещаем имя пользователя и пароль в файл cookie, который находится в открытом виде. Кто-то, кто немного знаком с программированием, узнает номер вашего счета.

Итак, мы должны зашифровать пароль!


        Cookie cookie = new Cookie("autoLogin", user.getUsername() + "." + md5.md5(user.getPassword()));


  • В фильтре зашифрованный пароль не является паролем в базе. Итак, мы должныДобавить функцию в Dao [найти пользователя по имени пользователя]

    public User find(String username) {
        List<User> userList = UserDB.getUsers();

        //遍历List集合,看看有没有对应的username和password
        for (User user : userList) {
            if (user.getUsername().equals(username)) {
                return user;
            }
        }

        return null;
    }

  • В фильтре сравните, совпадают ли пароль md5, принесенный куки, и пароль, полученный в базе (тоже через md5)

        //得到Cookie的用户名和密码
        if (value != null) {

            String username = value.split("\\.")[0];
            String password = value.split("\\.")[1];

            //在Cookie拿到的密码是md5加密过的,不能直接与数据库中的密码比较
            UserDao userDao = new UserDao();
            User user = userDao.find(username);

            //通过用户名获得用户信息,得到用户的密码,用户的密码也md5一把

            String dbPassword = md5.md5(user.getPassword());
            //如果两个密码匹配了,就是正确的密码了
            if (password.equals(dbPassword)) {
                request.getSession().setAttribute("user", user);
            }

        }


Если в статье есть какие-либо ошибки, пожалуйста, поправьте меня, и мы сможем общаться друг с другом. Студенты, которые привыкли читать технические статьи о WeChat, могут обратить внимание на публичный аккаунт WeChat: Java3y.