SpringBoot 2.0 不得不说的过滤器 Filter

servlet最重要的特性之一:过滤器,对访问的路径、资源进行限定拦截,实现特有的功能。比如URL级别的权限认证,session验证,Referer 过滤等等操作

现在的情况是这样的:我有一个接口,返回的是JSON数据,但如何不做限定操作,浏览器直接访问该路径的话会直接得到数据,那么用户的信息就被泄露了。

其实就这个就是http Referer防止外链的操作,不懂的也没关系,只需要知道Referer直接浏览器访问是空,Submit表单提交就会有值

好了,现在我们就来实现这个过滤的操作


User 存储用户的姓名和年龄

package priv.augus.filter.controller;
/**
 * IntelliJ IDEA
 *
 * @author Augus
 * @date 2018/8/3
 */
public class User {
    private String name;

    private Integer age;

    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

通过实现接口Filter自定义我们RefererFilter的过滤器类,这里面的逻辑就是判断Referer的值

package priv.augus.filter.controller;


import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * IntelliJ IDEA
 *
 * @author Augus
 * @date 2018/8/3
 */
public class RefererFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse res = (HttpServletResponse) servletResponse;
        String referer = req.getHeader("Referer");
        if(referer==null){
            // 为空就滚去error
            res.sendRedirect("/error");
            return;
        }
        // 有值,就继续执行下一个过滤链
        filterChain.doFilter(req, res);
    }

    @Override
    public void destroy() {

    }
}

通过注解@Configuration和@Bean实现启动服务时候加载Bean

package priv.augus.filter.controller;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * IntelliJ IDEA
 *
 * @author Augus
 * @date 2018/8/3
 */
@Configuration
public class FilterConfiguration {

    @Bean
    public FilterRegistrationBean refererFilterRegistration() {
        FilterRegistrationBean<RefererFilter> registration = new FilterRegistrationBean<>();
        //注入过滤器
        registration.setFilter(new RefererFilter());
        //过滤规则
        registration.addUrlPatterns("/user/*");
        //过滤器名称
        registration.setName("ref");
        //过滤器顺序
        registration.setOrder(1);

        return registration;
    }
}

controller层就是一个简单的跳转

package priv.augus.filter.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * IntelliJ IDEA
 *
 * @author Augus
 * @date 2018/8/3
 */
@Controller
@RequestMapping("/")
public class IndexController {


    @RequestMapping("/index")
    public String index(){
        return "index";
    }

    @ResponseBody
    @RequestMapping("/user/all")
    public List<User> getAllUser(){
        // 模拟从数据库取数据返回
        User user = new User("小明",23);
        User user1 = new User("小大黄",12);
        List<User> userList = new ArrayList<>();
        userList.add(user);
        userList.add(user1);
        return userList;
    }
}

还有个Index页面,模拟的表单提交获取数据,我这里用的thymeleaf

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<form th:action="@{/user/all}" method="POST">
    <input type="submit" value="获取数据">
</form>
</body>
</html>

启动服务后

浏览器访问 http://localhost:8081/user/all

通过表单 submit获取的数据

Filter 不同的实现

@Component

刚才我的实现好了,这是一个简单的测试用例

现在我们就来看看到底Filter是怎么加载的,现在大家在看我的例子,我这里面过滤的路径是/user/,如果哪里不小心配置错了,过滤的路径是/,那么恭喜你,浏览器会死循环进入/erorr页面,因为过滤器把/erorr页面也拦截了。

有人就看到网上的案例自定义的RefererFilter上面要加上@Component注解,

@Component
public class RefererFilter implements Filter {}

现在通过日志查看有什么不同

没有@Component

有@Component

我们可以知道得知原来加了@Component会自动加载一个名字叫refererFilter的过滤器,并且默认过滤路径是/*

@WebFilter注解实现

网上太多博客是说实现是这样的,可是怕是都没有去正的实现,这样是错误的

@Component
@WebFilter(urlPatterns = "/user/*", filterName = "ref")
public class RefererFilter implements Filter {


看了日志后我们发现,@WebFilter定义名字叫ref的并没有实现,反倒是@Component和之前一样默认创建了一个名字叫refererFilter的/*的过滤器

其实在springboot的启动类上加上@ServletComponentScan就可以扫描到对应的@WebFilter 并且加载了

@SpringBootApplication
@ServletComponentScan
public class FilterApplication {