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();
}
}