问题引入
在开发过程中需要对某些接口进行鉴权,一开始的想法是写个Filter,然后拦截指定的路径,通过获取header中的信息进行鉴权,没有权限则抛出自定义异常,然后自定义全局异常处理器再去拦截。
程序测试时发现,没权限时确实被拦截了,但是并没有进入到全局异常处理器,而是直接抛出500。通过查找发现,Filter的在进入Servlet之前进行拦截的,而全局异常是SpringBoot中的(本质是Servlet),所以全局异常处理并不能拦截到Filter抛出的异常。
基本概念
与这个相关的一些概念如下:
web.xml 的加载顺序是:context- param -> listener -> filter -> servlet
1、servlet:servlet是一种运行服务器端的java应用程序,具有独立于平台和协议的特性,并且可以动态的生成web页面,它工作在客户端请求与服务器响应的中间层。
2、filter:filter是一个可以复用的代码片段,可以用来转换HTTP请求、响应和头信息。Filter不像Servlet,它不能产生一个请求或者响应,它只是修改对某一资源的请求,或者修改从某一的响应。
3、listener:监听器,从字面上可以看出listener主要用来监听只用。通过listener可以监听web服务器中某一个执行动作,并根据其要求作出相应的响应。通俗的语言说就是在application,session,request三个对象创建消亡或者往其中添加修改删除属性时自动执行代码的功能组件。
4、interceptor:是在面向切面编程的,就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法,比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
Filter和Interceptor对比
过滤器Filter
是在请求进入容器后,但在进入servlet
之前进行预处理,请求结束是在servlet
处理完以后。
拦截器 Interceptor
是在请求进入servlet
后,在进入Controller
之前进行预处理的,Controller
中渲染了对应的视图之后请求结束。
解决Filter抛出异常无法全局捕获
由上面知道,在filter中抛出的自定义异常是无法在spring的全局异常处理器中捕获到的。那么介绍三种方案去解决这个问题。
1.自定义response返回
@Component
@Slf4j
public class authFilter implements Filter {
private String elasticJobCode;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
elasticJobCode = ConfigService.getAppConfig().getProperty("wormhole.code", "");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 认证相关
String signNew = "";
if(signNew.equals(sign)){
//认证成功
filterChain.doFilter(request, response);
} else{
//认证失败
updateResponse(response, ReturnCode.NO_PERMISSION);
}
}
@Override
public void destroy() {
}
private void updateResponse(HttpServletResponse response, ReturnCode errorCode) {
PrintWriter writer = null;
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
try {
writer = response.getWriter();
if (writer != null) {
writer.print(errorCode.toString());
}
} catch (IOException e) {
log.error("authenticate error : {}", errorCode, e);
} finally {
if (writer != null) {
writer.flush();
writer.close();
}
writer = null;
}
}
}
2.重写SpringBoot内置的对异常进行统一处理的Controller–BasicErrorController
(不建议)
- 自定义异常
public class MyException extends RuntimeException {
private static final long serialVersionUID = -1909845654770784460L;
private final int code;
public int getCode() {
return code