今天学习的filter是ExceptionTranslationFilter,看名字是异常翻译过滤器
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
try {
// 进入下一个filter
chain.doFilter(request, response);
this.logger.debug("Chain processed normally");
} catch (IOException var9) {
throw var9;
} catch (Exception var10) {
// 获取Thrrowable
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var10);
// 判断异常是否是AuthenticationException
RuntimeException ase = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (ase == null) {
// 判断异常是否是AccessDeniedException
ase = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}
if (ase == null) {
if (var10 instanceof ServletException) {
throw (ServletException)var10;
}
if (var10 instanceof RuntimeException) {
throw (RuntimeException)var10;
}
throw new RuntimeException(var10);
}
// 如果是AccessDeniedException 或者AuthenticationException则进入如下方法
this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
}
}
下面是handleSpringSecurityException方法的源码
private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException {
if (exception instanceof AuthenticationException) {
this.logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
this.sendStartAuthentication(request, response, chain, (AuthenticationException)exception);
} else if (exception instanceof AccessDeniedException) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (!this.authenticationTrustResolver.isAnonymous(authentication) && !this.authenticationTrustResolver.isRememberMe(authentication)) {
this.logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception);
this.accessDeniedHandler.handle(request, response, (AccessDeniedException)exception);
} else {
this.logger.debug("Access is denied (user is " + (this.authenticationTrustResolver.isAnonymous(authentication) ? "anonymous" : "not fully authenticated") + "); redirecting to authentication entry point", exception);
this.sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException("Full authentication is required to access this resource"));
}
}
}
1.handleSpringSecurityException方法首先判断是否是AuthenticationException异常,如果是先用RequestCache保存前一步链接(到时候登录成功之后可以直接跳到用户操作的url),然后跳转到登录页面。
跳转到登录界面前,HttpSessionRequestCache会保存当前请求的url,登录认证通过后会直接跳转到之前访问的页面。使用这个功能不能将<form-login>的always-use-default-target属性设置为TRUE,默认是FALSE的。这个逻辑在SavedRequestAwareAuthenticationSuccessHandler中处理。
protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException {
// SecurityContextHolderz中的Authentication设置为null
SecurityContextHolder.getContext().setAuthentication((Authentication)null);
// 缓存用户的前一步请求的url
this.requestCache.saveRequest(request, response);
this.logger.debug("Calling Authentication entry point.");
// 调用接口authenticationEntryPoint的commence方法处理异常的后续操作
this.authenticationEntryPoint.commence(request, response, reason);
}
2,如果是AccessDeniedException,先判断是否是匿名用户,如果是,也将跳转到登录页面,如果是已认证用户,则交给accessDeniedHandler处理。
if (!this.authenticationTrustResolver.isAnonymous(authentication) && !this.authenticationTrustResolver.isRememberMe(authentication)) {
this.logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception);
this.accessDeniedHandler.handle(request, response, (AccessDeniedException)exception);
} else {
// 如果是匿名用户,也将跳转到登录页面
this.logger.debug("Access is denied (user is " + (this.authenticationTrustResolver.isAnonymous(authentication) ? "anonymous" : "not fully authenticated") + "); redirecting to authentication entry point", exception);
this.sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException("Full authentication is required to access this resource"));
}
针对上诉总结:
ExceptionTranslationFilter 是Spring Security的核心filter之一,用来处理AuthenticationException和AccessDeniedException两种异常。
在我们的例子中,AuthenticationException指的是未登录状态下访问受保护资源,AccessDeniedException指的是登陆了但是由于权限不足(比如普通用户访问管理员界面)。
ExceptionTranslationFilter 持有两个处理类,分别是AuthenticationEntryPoint和AccessDeniedHandler。
ExceptionTranslationFilter 对异常的处理是通过这两个处理类实现的,处理规则很简单:
规则1. 如果异常是 AuthenticationException,使用 AuthenticationEntryPoint 处理
规则2. 如果异常是 AccessDeniedException 且用户是匿名用户,使用 AuthenticationEntryPoint 处理
规则3. 如果异常是 AccessDeniedException 且用户不是匿名用户,如果否则交给 AccessDeniedHandler 处理。
AccessDeniedHandler 默认实现是 AccessDeniedHandlerImpl。
AuthenticationEntryPoint 默认实现是 LoginUrlAuthenticationEntryPoint, 该类的处理是转发或重定向到登录页面
如果想替换默认的,直接在写这段话
http.exceptionHandling().authenticationEntryPoint(myAuthenticationEntryPoint).accessDeniedHandler(myAccessDeniedHandler);
因为exceptionHandling这个类提供了相应的set方法