tomcat正常请求过程
首先回顾下一个http请求在tomcat中的处理过程:
流程图
框架准备
为了分析异常处理的过程, 这里自定义一个filter,并主动抛出异常;
自定义Filter
public class HttpMethodAllowedFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if(1 < 2) {
throw new ServiceException("抛出异常");
}
filterChain.doFilter(request, response);
}
}
配置Filter
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//略....
httpSecurity.addFilterBefore(new HttpMethodAllowedFilter(), ChannelProcessingFilter.class);
}
tomcat异常处理
异常处理流程图
首先将异常处理的流程画出来,然后逐步分析代码的执行过程。
接下来分析下错误流程处理代码
:
StandardEngineValve
//org.apache.catalina.core.StandardEngineValve
public final void invoke(Request request, Response response)
throws IOException, ServletException {
Host host = request.getHost();
//check host,略......
// ErrorReportValve -> StandardHostValve
host.getPipeline().getFirst().invoke(request, response);
}
StandardHostValve
异常主要是在StandardHostValve
中处理的:
//org.apache.catalina.core.StandardHostValve
public final void invoke(Request request, Response response)
throws IOException, ServletException {
//使用此Context来处理请求
Context context = request.getContext();
// NonLoginAuthenticator -> StandardContextValve
context.getPipeline().getFirst().invoke(request, response);
//获取request中ERROR_EXCEPTION属性,来判断是否在请求过程中存在异常.
Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
//略....
//异常处理。
throwable(request, response, t);
}
protected void throwable(Request request, Response response,
Throwable throwable) {
Context context = request.getContext();
if (context == null) {
return;
}
Throwable realError = throwable;
//针对ServletException,ClientAbortException 的特殊处理....(略)
//寻找errorPage
ErrorPage errorPage = findErrorPage(context, throwable);
if (errorPage != null) {
//如果配置了errorPage,则做相应的处理(略...)
} else {
// 未找到请求处理期间抛出的异常对应的error-page
// 查看是否存在500错误页面,如果存在指定为响应返回;
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.setError();
status(request, response);
}
}
private void status(Request request, Response response) {
int statusCode = response.getStatus();
Context context = request.getContext();
if (!response.isError()) {
return;
}
//寻找有没有配置errorPage
ErrorPage errorPage = context.findErrorPage(statusCode);
//如果没有,则设置默认的page: "/error"
if (errorPage == null) {
// errorPage = "/error"
errorPage = context.findErrorPage(0);
}
if (errorPage != null && response.isErrorReportRequired()) {
response.setAppCommitted(false);
request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
Integer.valueOf(statusCode));
String message = response.getMessage();
if (message == null) {
message = "";
}
request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
errorPage.getLocation());
//设置dispatchType=="ERROR",匹配filter时只匹配“ERROR”类型的Filter
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,
DispatcherType.ERROR);
if (custom(request, response, errorPage)) {
response.setErrorReported();
response.finishResponse();
}
}
}
private boolean custom(Request request, Response response,
ErrorPage errorPage) {
ServletContext servletContext =
request.getContext().getServletContext();
RequestDispatcher rd =
servletContext.getRequestDispatcher(errorPage.getLocation());
if (response.isCommitted()) {
rd.include(request.getRequest(), response.getResponse());
} else {
response.resetBuffer(true);
response.setContentLength(-1);
//org.apache.catalina.core.ApplicationDispatcher,执行跳转命令
//执行跳转“/error”的请求;
rd.forward(request.getRequest(), response.getResponse());
response.setSuspended(false);
}
return true;
}
ApplicationDispatcher
到出现异常时,会调用ApplicationDispatcher.forward(“error”)
,方法执行调用过程为:forward -> doForward -> processRequest ->invoke
private void invoke(ServletRequest request, ServletResponse response,
State state) throws IOException, ServletException {
//略....
// 获取filterChian,当出现错误时即forword("/error")时,
// 获取的filter的dispatchType为“ERROR”
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
try {
filterChain.doFilter(request, response);
} //catch 其他异常(略.....)
catch (RuntimeException e) {
runtimeException = e;
}
// 抛出捕获的异常; 与后续的StandardWrapperValve(吃掉异常)不同
if (ioException != null)
throw ioException;
if (servletException != null)
throw servletException;
if (runtimeException != null)
throw runtimeException;
}
当抛出异常forward("/error")
时ApplicationFilterChain
内容如下:
StandardContextValve
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// 禁止任何请求直接访问: WEB-INF or META-INF 下的资源;
MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/META-INF"))
|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// Select the to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
//略.....
wrapper.getPipeline().getFirst().invoke(request, response);
}
StandardWrapperValve
//org.apache.catalina.core.StandardWrapperValve
public final void invoke(Request request, Response response)
throws IOException, ServletException {
//1.创建filterChain
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
//2. filterChain类型为: ApplicationFilterChain
try{
filterChain.doFilter(request.getRequest(), response.getResponse());
(request.getRequest(), response.getResponse());
// catch{} catch各种异常(略...)
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
//log异常(略....)
throwable = e;
// 注意!!! 这里不再向上抛出异常,
// 而是将异常信息,写到request的attribute中;
exception(request, response, e);
}
//释放filterchain
if (filterChain != null) {
filterChain.release();
}
//释放servlet
if (servlet != null) {
wrapper.deallocate(servlet);
}
}
ApplicationFilterFactory & ApplicationFilterChain
//org.apache.catalina.core.ApplicationFilterFactory
//1.
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
ApplicationFilterChain filterChain = new ApplicationFilterChain();
//设置servlet
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
//1.1 for-each filterMaps,匹配req.getRequestPath()的 filter
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
//1.2 for-each filterMaps,匹配 servletName
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
}
//org.apache.catalina.core.ApplicationFilterChain
//2.
public final class ApplicationFilterChain implements FilterChain {
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
//filterChain正在执行的filter的位置
private int pos = 0;
//filterchain中filter的个数
private int n = 0;
private Servlet servlet = null;
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
internalDoFilter(request,response);
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// 如果hasNext,则执行if{},调用filter.doFilter
// 执行到 n-1 ,当pos = n时,return; 继续往下执行
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// 当filterChain中 filter全部执行完毕后,执行servlet.service();
servlet.service(request, response);
}
}
此时正常请求时ApplicationFilterChain
内容如下:
为什么处理异常时前后创建的ApplicationFilterChain,内容不一致
DispatcherType
每一个request都有DispatcherType
类型,它是枚举类型
public enum DispatcherType {
FORWARD,
INCLUDE,
REQUEST,
ASYNC,
ERROR
}
而每个Filter也有与之对应的类型:
public class FilterMap extends XmlEncodingBase implements Serializable
public static final int ERROR = 1;
public static final int FORWARD = 2;
public static final int INCLUDE = 4;
public static final int REQUEST = 8;
public static final int ASYNC = 16;
}
在对servlet请求匹配关联Filter时,不仅需要匹配:URL,servletName
更需要匹配DispatcherType
.
FilterChainProxy
接上文中的FilterChainProxy
的组成如下图:
,我们都知道,SpringSecurity的异常都是交由ExceptionTranslationFilter
来处理的:
ExceptionTranslationFilter
public class ExceptionTranslationFilter extends GenericFilterBean {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
//nextFilter为 FilterSecurityInterceptor
//FilterSecurityInterceptor来判断是否通过认证和是否拥有权限;
//若未通过则抛出对应的AuthenticationException(401),AccessDeniedException(403)
chain.doFilter(request, response);
}catch (IOException ex) {
throw ex;
}
catch (Exception ex) {
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
RuntimeException ase = (AuthenticationException) throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class, causeChain);
//略 if()else{}判断....
handleSpringSecurityException(request, response, chain, ase);
}
}
private void handleSpringSecurityException(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, RuntimeException exception)
throws IOException, ServletException {
if (exception instanceof AuthenticationException) {
//调用authenticationEntryPoint.commence(request, response, reason);
sendStartAuthentication(request, response, chain,
(AuthenticationException) exception);
}
else if (exception instanceof AccessDeniedException) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) {
//调用authenticationEntryPoint.commence(request, response, reason);
sendStartAuthentication(
request,
response,
chain,
new InsufficientAuthenticationException(
messages.getMessage(
"ExceptionTranslationFilter.insufficientAuthentication",
"Full authentication is required to access this resource")));
}
else {
//调用accessDeniedHandler.handle()
accessDeniedHandler.handle(request, response,
(AccessDeniedException) exception);
}
}
}
}