springboot中使用filter

1.注册Bean:

@SpringBootApplication //等同于 @Configuration @EnableAutoConfiguration @ComponentScan
public class SpringbootFilterDemoApp {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootFilterDemoApp.class, args);
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new AuthorizationFilter()); // 自己的filter

        List<String> urlPatterns = new ArrayList<>();
        urlPatterns.add("/openapi/*");
        registrationBean.setUrlPatterns(urlPatterns);
        return registrationBean;
    }
}

2.自定义过滤器

继承Filter接口,对于满足条件的请求,使用filterChain.doFilter(request, response);将连接转发到目的地,不满足,则直接通过response写入错误信息

/**
 * 过滤器:校验接口访问权限
 */
public class AuthorizationFilter implements Filter{

    private static final Logger logger = LoggerFactory.getLogger(AuthorizationFilter.class);

    private CheckAuth checkAuth;

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

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 将请求转换成HttpServletRequest 请求
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse rsp = (HttpServletResponse) servletResponse;
        // 取得接口URI
        String currentURI = req.getRequestURI();
        // 注入checkAuth对象
        //详见WebApplicationContextUtils工具类
        。。。
        
        // 读取请求参数
        //get、post获取请求参数的方式是不一样的。
        if (req.getMethod().equals("POST")) {
		   requestWrapper = new HttpServletRequestWrapper(req);
		   String reqParamsStr = HttpHelper.getBodyString(requestWrapper);
	    if(reqParamsStr.length() >0) {
	        // 从json字符串获取参数
	        accessToken = "";
	    }
		} else if (req.getMethod().equals("GET")) {
		    requestWrapper = req;
		    String[] tokens = req.getParameterValues("accessToken");
		    if (tokens != null && tokens.length > 0) {
		        accessToken = tokens[0];
		    }
		}
        if (requestWrapper == null) {
            strError = "请求方式错误!";
        } else {
            if ("".equals(accessToken)) {
                strError = "缺少必要的参数";
            } else {
                // 校验接口权限
                if (checkAuth.checkAuth(accessToken, currentURI)) {
                    // Filter 只是链式处理,请求依然转发到目的地址。
                    filterChain.doFilter(requestWrapper, rsp);
                    return;
                } else {
                    strError = "权限不足";
                }
            }
        }

        rsp.setCharacterEncoding("UTF-8");
        rsp.setContentType("application/json; charset=utf-8");
        String rspValue = "";   // 返回错误json信息
        logger.info("返回:{}", rspValue);
        rsp.getWriter().write(rspValue);
    }

    @Override
    public void destroy() {

    }
}

3.注意事项

Filter中注入对象的时机问题

Filter的加载在servlet之前,所以不能在Filter中使用@Autowired注入对象CheckAuth肯定会失败。我们可以使用WebApplicationContextUtils来获取对象。(可参见web.xml配置文件中的listener、 filter、servlet 书写顺序)

工具类

WebApplicationContextUtils工具类

public class WebApplicationContextUtils{
	public static CheckAuth getBean(){
		// 注入checkAuth对象
		ServletContext sc = req.getSession().getServletContext();
		WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(sc);
		if (context != null && context.getBean("checkAuth") != null && checkAuth == null) {
		    checkAuth = context.getBean(CheckAuth.class);
		}
	}
}

流的问题

读取请求参数:

对于post请求,我们通过stream流进行读取,但是会发现直接读取response流中的数据后,使用filterChain.doFilter(requestWrapper, rsp);请求会报错:没有请求内容。因为:
  stream流的意思是当读取过之后就无法回到上一次读取的数据。流是单向的。当第一个filter中读取流之后流被读完,流已经发生变化,所以第二个filter中读不到数据。为了防止这种情况,需要将流再次写出去。下面是对Request的封装。

import java.io.BufferedReader; 
import java.io.ByteArrayInputStream;   
import java.io.IOException;   
import java.io.InputStreamReader;   
import java.nio.charset.Charset;   
import java.util.Enumeration;  

import javax.servlet.ReadListener; 
import javax.servlet.ServletInputStream; 
import javax.servlet.http.HttpServletRequest;

/**  * httpServletRequst封装类  */ 
public class HttpServletRequestWrapper extends
        javax.servlet.http.HttpServletRequestWrapper {

    private final byte[] body;  

    public HttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8")); 

    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

    @Override
    public String getHeader(String name) {
        return super.getHeader(name);
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        return super.getHeaderNames();
    }

    @Override
    public Enumeration<String> getHeaders(String name) {
        return super.getHeaders(name);
    } }

Request读取数据工具类

import java.io.BufferedReader;
import java.io.IOException;  
import java.io.InputStream;  
import java.io.InputStreamReader;  
import java.nio.charset.Charset;  

import javax.servlet.ServletRequest;  

public class HttpHelper {  
    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */  
    public static String getBodyString(ServletRequest request) {  
        StringBuilder sb = new StringBuilder();  
        InputStream inputStream = null;  
        BufferedReader reader = null;  
        try {  
            inputStream = request.getInputStream();  
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));  
            String line;
            while ((line = reader.readLine()) != null) {  
                sb.append(line);  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        } finally {  
            if (inputStream != null) {  
                try {  
                    inputStream.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            if (reader != null) {  
                try {  
                    reader.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
        return sb.toString();  
    }  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值